| 1 | From 87eb40e63e1b149ef87fe05765b97328bfbd1c1f Mon Sep 17 00:00:00 2001 |
| 2 | From: Xiangfu Liu <xiangfu@macbook.openmobilefree.net> |
| 3 | Date: Wed, 14 Sep 2011 14:29:37 +0800 |
| 4 | Subject: [PATCH 24/32] forward code to linux 3.0 |
| 5 | |
| 6 | --- |
| 7 | arch/mips/Kconfig | 2 + |
| 8 | arch/mips/include/asm/cacheflush.h | 6 + |
| 9 | arch/mips/include/asm/mach-jz4740/gpio.h | 5 + |
| 10 | arch/mips/include/asm/mach-jz4740/jz4740_fb.h | 8 + |
| 11 | arch/mips/jz4740/Makefile | 1 + |
| 12 | arch/mips/jz4740/clock.c | 230 +++++++++++++++++++++++- |
| 13 | arch/mips/jz4740/clock.h | 4 + |
| 14 | arch/mips/jz4740/cpufreq.c | 226 ++++++++++++++++++++++++ |
| 15 | arch/mips/jz4740/gpio.c | 148 ++++------------ |
| 16 | arch/mips/jz4740/irq.c | 92 ++++------ |
| 17 | arch/mips/jz4740/irq.h | 6 +- |
| 18 | arch/mips/jz4740/platform.c | 20 ++- |
| 19 | arch/mips/jz4740/pm.c | 3 - |
| 20 | arch/mips/jz4740/reset.c | 46 +++++- |
| 21 | arch/mips/kernel/cpufreq/Kconfig | 13 ++- |
| 22 | 15 files changed, 623 insertions(+), 187 deletions(-) |
| 23 | create mode 100644 arch/mips/jz4740/cpufreq.c |
| 24 | |
| 25 | diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig |
| 26 | index 653da62..42c4e6a 100644 |
| 27 | --- a/arch/mips/Kconfig |
| 28 | +++ b/arch/mips/Kconfig |
| 29 | @@ -211,6 +211,8 @@ config MACH_JZ4740 |
| 30 | select SYS_HAS_EARLY_PRINTK |
| 31 | select HAVE_PWM |
| 32 | select HAVE_CLK |
| 33 | + select CPU_SUPPORTS_CPUFREQ |
| 34 | + select GENERIC_IRQ_CHIP |
| 35 | |
| 36 | config LANTIQ |
| 37 | bool "Lantiq based platforms" |
| 38 | diff --git a/arch/mips/include/asm/cacheflush.h b/arch/mips/include/asm/cacheflush.h |
| 39 | index 40bb9fd..2a26af0 100644 |
| 40 | --- a/arch/mips/include/asm/cacheflush.h |
| 41 | +++ b/arch/mips/include/asm/cacheflush.h |
| 42 | @@ -114,4 +114,10 @@ unsigned long run_uncached(void *func); |
| 43 | extern void *kmap_coherent(struct page *page, unsigned long addr); |
| 44 | extern void kunmap_coherent(void); |
| 45 | |
| 46 | +#define ARCH_HAS_FLUSH_KERNEL_DCACHE_PAGE |
| 47 | +static inline void flush_kernel_dcache_page(struct page *page) |
| 48 | +{ |
| 49 | + flush_dcache_page(page); |
| 50 | +} |
| 51 | + |
| 52 | #endif /* _ASM_CACHEFLUSH_H */ |
| 53 | diff --git a/arch/mips/include/asm/mach-jz4740/gpio.h b/arch/mips/include/asm/mach-jz4740/gpio.h |
| 54 | index 7b74703..5a3675e 100644 |
| 55 | --- a/arch/mips/include/asm/mach-jz4740/gpio.h |
| 56 | +++ b/arch/mips/include/asm/mach-jz4740/gpio.h |
| 57 | @@ -253,6 +253,9 @@ uint32_t jz_gpio_port_get_value(int port, uint32_t mask); |
| 58 | #define JZ_GPIO_MEM_WAIT JZ_GPIO_PORTC(27) |
| 59 | #define JZ_GPIO_MEM_FRE JZ_GPIO_PORTC(28) |
| 60 | #define JZ_GPIO_MEM_FWE JZ_GPIO_PORTC(29) |
| 61 | +/* Pins have different assignment in SLCD mode */ |
| 62 | +#define JZ_GPIO_SLCD_RS JZ_GPIO_PORTC(19) |
| 63 | +#define JZ_GPIO_SLCD_CS JZ_GPIO_PORTC(20) |
| 64 | |
| 65 | #define JZ_GPIO_FUNC_LCD_DATA0 JZ_GPIO_FUNC1 |
| 66 | #define JZ_GPIO_FUNC_LCD_DATA1 JZ_GPIO_FUNC1 |
| 67 | @@ -284,6 +287,8 @@ uint32_t jz_gpio_port_get_value(int port, uint32_t mask); |
| 68 | #define JZ_GPIO_FUNC_MEM_WAIT JZ_GPIO_FUNC1 |
| 69 | #define JZ_GPIO_FUNC_MEM_FRE JZ_GPIO_FUNC1 |
| 70 | #define JZ_GPIO_FUNC_MEM_FWE JZ_GPIO_FUNC1 |
| 71 | +#define JZ_GPIO_FUNC_SLCD_RS JZ_GPIO_FUNC1 |
| 72 | +#define JZ_GPIO_FUNC_SLCD_CS JZ_GPIO_FUNC1 |
| 73 | |
| 74 | |
| 75 | #define JZ_GPIO_MEM_ADDR19 JZ_GPIO_PORTB(22) |
| 76 | diff --git a/arch/mips/include/asm/mach-jz4740/jz4740_fb.h b/arch/mips/include/asm/mach-jz4740/jz4740_fb.h |
| 77 | index 6a50e6f..b2b6dba 100644 |
| 78 | --- a/arch/mips/include/asm/mach-jz4740/jz4740_fb.h |
| 79 | +++ b/arch/mips/include/asm/mach-jz4740/jz4740_fb.h |
| 80 | @@ -30,6 +30,12 @@ enum jz4740_fb_lcd_type { |
| 81 | JZ_LCD_TYPE_DUAL_COLOR_STN = 10, |
| 82 | JZ_LCD_TYPE_DUAL_MONOCHROME_STN = 11, |
| 83 | JZ_LCD_TYPE_8BIT_SERIAL = 12, |
| 84 | + JZ_LCD_TYPE_SMART_PARALLEL_8_BIT = 1 | (1 << 5), |
| 85 | + JZ_LCD_TYPE_SMART_PARALLEL_16_BIT = 0 | (1 << 5), |
| 86 | + JZ_LCD_TYPE_SMART_PARALLEL_18_BIT = 2 | (1 << 5), |
| 87 | + JZ_LCD_TYPE_SMART_SERIAL_8_BIT = 1 | (3 << 5), |
| 88 | + JZ_LCD_TYPE_SMART_SERIAL_16_BIT = 0 | (3 << 5), |
| 89 | + JZ_LCD_TYPE_SMART_SERIAL_18_BIT = 2 | (3 << 5), |
| 90 | }; |
| 91 | |
| 92 | #define JZ4740_FB_SPECIAL_TFT_CONFIG(start, stop) (((start) << 16) | (stop)) |
| 93 | @@ -62,6 +68,8 @@ struct jz4740_fb_platform_data { |
| 94 | |
| 95 | unsigned pixclk_falling_edge:1; |
| 96 | unsigned date_enable_active_low:1; |
| 97 | + unsigned chip_select_active_low:1; |
| 98 | + unsigned register_select_active_low:1; |
| 99 | }; |
| 100 | |
| 101 | #endif |
| 102 | diff --git a/arch/mips/jz4740/Makefile b/arch/mips/jz4740/Makefile |
| 103 | index 72eb2ad..e88ab59 100644 |
| 104 | --- a/arch/mips/jz4740/Makefile |
| 105 | +++ b/arch/mips/jz4740/Makefile |
| 106 | @@ -19,5 +19,6 @@ obj-$(CONFIG_JZ4740_ID800WT) += board-id800wt.o |
| 107 | # PM support |
| 108 | |
| 109 | obj-$(CONFIG_PM) += pm.o |
| 110 | +obj-$(CONFIG_CPU_FREQ_JZ) += cpufreq.o |
| 111 | |
| 112 | ccflags-y := -Werror -Wall |
| 113 | diff --git a/arch/mips/jz4740/clock.c b/arch/mips/jz4740/clock.c |
| 114 | index 118a8a5..da423d1 100644 |
| 115 | --- a/arch/mips/jz4740/clock.c |
| 116 | +++ b/arch/mips/jz4740/clock.c |
| 117 | @@ -1,5 +1,8 @@ |
| 118 | /* |
| 119 | + * Copyright (c) 2006-2007, Ingenic Semiconductor Inc. |
| 120 | * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> |
| 121 | + * Copyright (c) 2010, Ulrich Hecht <ulrich.hecht@gmail.com> |
| 122 | + * Copyright (c) 2010, Maarten ter Huurne <maarten@treewalker.org> |
| 123 | * JZ4740 SoC clock support |
| 124 | * |
| 125 | * This program is free software; you can redistribute it and/or modify it |
| 126 | @@ -41,16 +44,20 @@ |
| 127 | #define JZ_CLOCK_CTRL_I2S_SRC_PLL BIT(31) |
| 128 | #define JZ_CLOCK_CTRL_KO_ENABLE BIT(30) |
| 129 | #define JZ_CLOCK_CTRL_UDC_SRC_PLL BIT(29) |
| 130 | -#define JZ_CLOCK_CTRL_UDIV_MASK 0x1f800000 |
| 131 | #define JZ_CLOCK_CTRL_CHANGE_ENABLE BIT(22) |
| 132 | #define JZ_CLOCK_CTRL_PLL_HALF BIT(21) |
| 133 | -#define JZ_CLOCK_CTRL_LDIV_MASK 0x001f0000 |
| 134 | #define JZ_CLOCK_CTRL_UDIV_OFFSET 23 |
| 135 | #define JZ_CLOCK_CTRL_LDIV_OFFSET 16 |
| 136 | #define JZ_CLOCK_CTRL_MDIV_OFFSET 12 |
| 137 | #define JZ_CLOCK_CTRL_PDIV_OFFSET 8 |
| 138 | #define JZ_CLOCK_CTRL_HDIV_OFFSET 4 |
| 139 | #define JZ_CLOCK_CTRL_CDIV_OFFSET 0 |
| 140 | +#define JZ_CLOCK_CTRL_UDIV_MASK (0x3f << JZ_CLOCK_CTRL_UDIV_OFFSET) |
| 141 | +#define JZ_CLOCK_CTRL_LDIV_MASK (0x1f << JZ_CLOCK_CTRL_LDIV_OFFSET) |
| 142 | +#define JZ_CLOCK_CTRL_MDIV_MASK (0x0f << JZ_CLOCK_CTRL_MDIV_OFFSET) |
| 143 | +#define JZ_CLOCK_CTRL_PDIV_MASK (0x0f << JZ_CLOCK_CTRL_PDIV_OFFSET) |
| 144 | +#define JZ_CLOCK_CTRL_HDIV_MASK (0x0f << JZ_CLOCK_CTRL_HDIV_OFFSET) |
| 145 | +#define JZ_CLOCK_CTRL_CDIV_MASK (0x0f << JZ_CLOCK_CTRL_CDIV_OFFSET) |
| 146 | |
| 147 | #define JZ_CLOCK_GATE_UART0 BIT(0) |
| 148 | #define JZ_CLOCK_GATE_TCU BIT(1) |
| 149 | @@ -90,6 +97,7 @@ |
| 150 | #define JZ_CLOCK_PLL_M_OFFSET 23 |
| 151 | #define JZ_CLOCK_PLL_N_OFFSET 18 |
| 152 | #define JZ_CLOCK_PLL_OD_OFFSET 16 |
| 153 | +#define JZ_CLOCK_PLL_STABILIZE_OFFSET 0 |
| 154 | |
| 155 | #define JZ_CLOCK_LOW_POWER_MODE_DOZE BIT(2) |
| 156 | #define JZ_CLOCK_LOW_POWER_MODE_SLEEP BIT(0) |
| 157 | @@ -97,10 +105,15 @@ |
| 158 | #define JZ_CLOCK_SLEEP_CTRL_SUSPEND_UHC BIT(7) |
| 159 | #define JZ_CLOCK_SLEEP_CTRL_ENABLE_UDC BIT(6) |
| 160 | |
| 161 | +#define JZ_REG_EMC_RTCNT 0x88 |
| 162 | +#define JZ_REG_EMC_RTCOR 0x8C |
| 163 | + |
| 164 | static void __iomem *jz_clock_base; |
| 165 | static spinlock_t jz_clock_lock; |
| 166 | static LIST_HEAD(jz_clocks); |
| 167 | |
| 168 | +static void __iomem *jz_emc_base; |
| 169 | + |
| 170 | struct main_clk { |
| 171 | struct clk clk; |
| 172 | uint32_t div_offset; |
| 173 | @@ -204,25 +217,88 @@ static int jz_clk_ko_is_enabled(struct clk *clk) |
| 174 | return !!(jz_clk_reg_read(JZ_REG_CLOCK_CTRL) & JZ_CLOCK_CTRL_KO_ENABLE); |
| 175 | } |
| 176 | |
| 177 | +static struct static_clk jz_clk_ext; |
| 178 | + |
| 179 | +static unsigned long jz_clk_pll_calc_rate( |
| 180 | + unsigned int in_div, unsigned int feedback, unsigned int out_div) |
| 181 | +{ |
| 182 | + return ((jz_clk_ext.rate / in_div) * feedback) / out_div; |
| 183 | +} |
| 184 | + |
| 185 | +static void jz_clk_pll_calc_dividers(unsigned long rate, |
| 186 | + unsigned int *in_div, unsigned int *feedback, unsigned int *out_div) |
| 187 | +{ |
| 188 | + unsigned int target; |
| 189 | + |
| 190 | + /* The frequency after the input divider must be between 1 and 15 MHz. |
| 191 | + The highest divider yields the best resolution. */ |
| 192 | + *in_div = jz_clk_ext.rate / 1000000; |
| 193 | + if (*in_div >= 34) |
| 194 | + *in_div = 33; |
| 195 | + |
| 196 | + /* The frequency before the output divider must be between 100 and |
| 197 | + 500 MHz. The lowest target rate is more energy efficient. */ |
| 198 | + if (rate < 25000000) { |
| 199 | + *out_div = 4; |
| 200 | + target = 25000000 * 4; |
| 201 | + } else if (rate <= 50000000) { |
| 202 | + *out_div = 4; |
| 203 | + target = rate * 4; |
| 204 | + } else if (rate <= 100000000) { |
| 205 | + *out_div = 2; |
| 206 | + target = rate * 2; |
| 207 | + } else if (rate <= 500000000) { |
| 208 | + *out_div = 1; |
| 209 | + target = rate; |
| 210 | + } else { |
| 211 | + *out_div = 1; |
| 212 | + target = 500000000; |
| 213 | + } |
| 214 | + |
| 215 | + /* Compute the feedback divider. |
| 216 | + Since the divided input is at least 1 MHz and the target frequency |
| 217 | + at most 500 MHz, the feedback will be at most 500 and will therefore |
| 218 | + always fit in the 9-bit register. |
| 219 | + Similarly, the divided input is at most 15 MHz and the target |
| 220 | + frequency at least 100 MHz, so the feedback will be at least 6 |
| 221 | + where the minimum supported value is 2. */ |
| 222 | + *feedback = ((target / 1000) * *in_div) / (jz_clk_ext.rate / 1000); |
| 223 | +} |
| 224 | + |
| 225 | +static unsigned long jz_clk_pll_round_rate(struct clk *clk, unsigned long rate) |
| 226 | +{ |
| 227 | + unsigned int in_div, feedback, out_div; |
| 228 | + /* The PLL frequency must be a multiple of 24 MHz, since the LCD pixel |
| 229 | + * clock must be exactly 12 MHz for the TV-out to work. |
| 230 | + * TODO: A multiple of 12 MHz for the PLL would work if the PLL would |
| 231 | + * not be divided by 2 before being passed to the set of derived |
| 232 | + * clocks that includes the LCD pixel clock. |
| 233 | + * TODO: Systemwide decisions like this should be made by the board |
| 234 | + * support code, so add some kind of hook for that. |
| 235 | + */ |
| 236 | + unsigned long rate24 = (rate / 24000000) * 24000000; |
| 237 | + |
| 238 | + jz_clk_pll_calc_dividers(rate24, &in_div, &feedback, &out_div); |
| 239 | + return jz_clk_pll_calc_rate(in_div, feedback, out_div); |
| 240 | +} |
| 241 | + |
| 242 | static const int pllno[] = {1, 2, 2, 4}; |
| 243 | |
| 244 | static unsigned long jz_clk_pll_get_rate(struct clk *clk) |
| 245 | { |
| 246 | uint32_t val; |
| 247 | - int m; |
| 248 | - int n; |
| 249 | - int od; |
| 250 | + unsigned int in_div, feedback, out_div; |
| 251 | |
| 252 | val = jz_clk_reg_read(JZ_REG_CLOCK_PLL); |
| 253 | |
| 254 | if (val & JZ_CLOCK_PLL_BYPASS) |
| 255 | return clk_get_rate(clk->parent); |
| 256 | |
| 257 | - m = ((val >> 23) & 0x1ff) + 2; |
| 258 | - n = ((val >> 18) & 0x1f) + 2; |
| 259 | - od = (val >> 16) & 0x3; |
| 260 | + feedback = ((val >> 23) & 0x1ff) + 2; |
| 261 | + in_div = ((val >> 18) & 0x1f) + 2; |
| 262 | + out_div = pllno[(val >> 16) & 0x3]; |
| 263 | |
| 264 | - return ((clk_get_rate(clk->parent) / n) * m) / pllno[od]; |
| 265 | + return jz_clk_pll_calc_rate(in_div, feedback, out_div); |
| 266 | } |
| 267 | |
| 268 | static unsigned long jz_clk_pll_half_get_rate(struct clk *clk) |
| 269 | @@ -235,7 +311,77 @@ static unsigned long jz_clk_pll_half_get_rate(struct clk *clk) |
| 270 | return jz_clk_pll_get_rate(clk->parent) >> 1; |
| 271 | } |
| 272 | |
| 273 | -static const int jz_clk_main_divs[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; |
| 274 | +#define SDRAM_TREF 15625 /* Refresh period: 4096 refresh cycles/64ms */ |
| 275 | + |
| 276 | +static void sdram_set_pll(unsigned int pllin) |
| 277 | +{ |
| 278 | + unsigned int ns, sdramclock; |
| 279 | + |
| 280 | + ns = 1000000000 / pllin; |
| 281 | + sdramclock = (SDRAM_TREF / ns) / 64 + 1; |
| 282 | + if (sdramclock > 0xff) sdramclock = 0xff; |
| 283 | + /* Set refresh registers */ |
| 284 | + writew(sdramclock, jz_emc_base + JZ_REG_EMC_RTCOR); |
| 285 | + writew(sdramclock, jz_emc_base + JZ_REG_EMC_RTCNT); |
| 286 | +} |
| 287 | + |
| 288 | +static int jz_clk_pll_set_rate(struct clk *clk, unsigned long rate) |
| 289 | +{ |
| 290 | + unsigned int ctrl, plcr1; |
| 291 | + unsigned int feedback, in_div, out_div, pllout, pllout2; |
| 292 | + |
| 293 | + jz_clk_pll_calc_dividers(rate, &in_div, &feedback, &out_div); |
| 294 | + |
| 295 | + ctrl = jz_clk_reg_read(JZ_REG_CLOCK_CTRL); |
| 296 | + pllout = jz_clk_pll_calc_rate(in_div, feedback, out_div); |
| 297 | + pllout2 = (ctrl & JZ_CLOCK_CTRL_PLL_HALF) ? pllout : (pllout / 2); |
| 298 | + |
| 299 | + /* Init UHC clock */ |
| 300 | + writel(pllout2 / 48000000 - 1, jz_clock_base + JZ_REG_CLOCK_UHC); |
| 301 | + |
| 302 | + plcr1 = ((feedback - 2) << JZ_CLOCK_PLL_M_OFFSET) | |
| 303 | + ((in_div - 2) << JZ_CLOCK_PLL_N_OFFSET) | |
| 304 | + ((out_div - 1) << JZ_CLOCK_PLL_OD_OFFSET) | |
| 305 | + (0x20 << JZ_CLOCK_PLL_STABILIZE_OFFSET) | |
| 306 | + JZ_CLOCK_PLL_ENABLED; |
| 307 | + |
| 308 | + sdram_set_pll(pllout); |
| 309 | + |
| 310 | + /* LCD pixclock */ |
| 311 | + writel(pllout2 / 12000000 - 1, jz_clock_base + JZ_REG_CLOCK_LCD); |
| 312 | + |
| 313 | + /* configure PLL */ |
| 314 | + __asm__ __volatile__( |
| 315 | + ".set noreorder\n\t" |
| 316 | + ".align 5\n" |
| 317 | + "sw %1,0(%0)\n\t" |
| 318 | + "nop\n\t" |
| 319 | + "nop\n\t" |
| 320 | + "nop\n\t" |
| 321 | + "nop\n\t" |
| 322 | + "nop\n\t" |
| 323 | + "nop\n\t" |
| 324 | + "nop\n\t" |
| 325 | + ".set reorder\n\t" |
| 326 | + : |
| 327 | + : "r" (jz_clock_base + JZ_REG_CLOCK_PLL), "r" (plcr1)); |
| 328 | + |
| 329 | + /* MtH: For some reason the MSC will have problems if this flag is not |
| 330 | + restored, even though the MSC is supposedly the only divider |
| 331 | + that is not affected by this flag. */ |
| 332 | + jz_clk_reg_set_bits(JZ_REG_CLOCK_CTRL, JZ_CLOCK_CTRL_CHANGE_ENABLE); |
| 333 | + |
| 334 | + return 0; |
| 335 | +} |
| 336 | + |
| 337 | +static const unsigned int jz_clk_main_divs[] = { |
| 338 | + 1, 2, 3, 4, 6, 8, 12, 16, 24, 32 |
| 339 | +}; |
| 340 | +static const unsigned int jz_clk_main_divs_inv[] = { |
| 341 | + -1, 0, 1, 2, 3, -1, 4, -1, 5, -1, -1, -1, 6, -1, -1, -1, |
| 342 | + 7, -1, -1, -1, -1, -1, -1, -1, 8, -1, -1, -1, -1, -1, -1, -1, |
| 343 | + 9 |
| 344 | +}; |
| 345 | |
| 346 | static unsigned long jz_clk_main_round_rate(struct clk *clk, unsigned long rate) |
| 347 | { |
| 348 | @@ -290,6 +436,64 @@ static int jz_clk_main_set_rate(struct clk *clk, unsigned long rate) |
| 349 | return 0; |
| 350 | } |
| 351 | |
| 352 | +static struct main_clk jz_clk_cpu; |
| 353 | + |
| 354 | +int clk_main_set_dividers(bool immediate, unsigned int cdiv, unsigned int hdiv, |
| 355 | + unsigned int mdiv, unsigned int pdiv) |
| 356 | +{ |
| 357 | + unsigned int cdiv_enc, hdiv_enc, mdiv_enc, pdiv_enc; |
| 358 | + unsigned int ctrl; |
| 359 | + unsigned int tmp, wait; |
| 360 | + |
| 361 | + if (cdiv >= ARRAY_SIZE(jz_clk_main_divs_inv) || |
| 362 | + hdiv >= ARRAY_SIZE(jz_clk_main_divs_inv) || |
| 363 | + mdiv >= ARRAY_SIZE(jz_clk_main_divs_inv) || |
| 364 | + pdiv >= ARRAY_SIZE(jz_clk_main_divs_inv)) |
| 365 | + return -EINVAL; |
| 366 | + cdiv_enc = jz_clk_main_divs_inv[cdiv]; |
| 367 | + hdiv_enc = jz_clk_main_divs_inv[hdiv]; |
| 368 | + mdiv_enc = jz_clk_main_divs_inv[mdiv]; |
| 369 | + pdiv_enc = jz_clk_main_divs_inv[pdiv]; |
| 370 | + if (cdiv_enc == (unsigned int)-1 || |
| 371 | + hdiv_enc == (unsigned int)-1 || |
| 372 | + mdiv_enc == (unsigned int)-1 || |
| 373 | + pdiv_enc == (unsigned int)-1) |
| 374 | + return -EINVAL; |
| 375 | + |
| 376 | + ctrl = jz_clk_reg_read(JZ_REG_CLOCK_CTRL); |
| 377 | + ctrl &= ~(JZ_CLOCK_CTRL_CHANGE_ENABLE | |
| 378 | + JZ_CLOCK_CTRL_CDIV_MASK | JZ_CLOCK_CTRL_HDIV_MASK | |
| 379 | + JZ_CLOCK_CTRL_MDIV_MASK | JZ_CLOCK_CTRL_PDIV_MASK); |
| 380 | + if (immediate) ctrl |= JZ_CLOCK_CTRL_CHANGE_ENABLE; |
| 381 | + ctrl |= (cdiv_enc << JZ_CLOCK_CTRL_CDIV_OFFSET) | |
| 382 | + (hdiv_enc << JZ_CLOCK_CTRL_HDIV_OFFSET) | |
| 383 | + (mdiv_enc << JZ_CLOCK_CTRL_MDIV_OFFSET) | |
| 384 | + (pdiv_enc << JZ_CLOCK_CTRL_PDIV_OFFSET); |
| 385 | + |
| 386 | + /* set dividers */ |
| 387 | + /* delay loops lifted from the old Ingenic cpufreq driver */ |
| 388 | + wait = ((clk_get_rate(&jz_clk_cpu.clk) / 1000000) * 500) / 1000; |
| 389 | + __asm__ __volatile__( |
| 390 | + ".set noreorder\n\t" |
| 391 | + ".align 5\n" |
| 392 | + "sw %2,0(%1)\n\t" |
| 393 | + "li %0,0\n\t" |
| 394 | + "1:\n\t" |
| 395 | + "bne %0,%3,1b\n\t" |
| 396 | + "addi %0, 1\n\t" |
| 397 | + "nop\n\t" |
| 398 | + "nop\n\t" |
| 399 | + "nop\n\t" |
| 400 | + "nop\n\t" |
| 401 | + ".set reorder\n\t" |
| 402 | + : "=r" (tmp) |
| 403 | + : "r" (jz_clock_base + JZ_REG_CLOCK_CTRL), "r" (ctrl), |
| 404 | + "r" (wait)); |
| 405 | + |
| 406 | + return 0; |
| 407 | +} |
| 408 | +EXPORT_SYMBOL_GPL(clk_main_set_dividers); |
| 409 | + |
| 410 | static struct clk_ops jz_clk_static_ops = { |
| 411 | .get_rate = jz_clk_static_get_rate, |
| 412 | .enable = jz_clk_enable_gating, |
| 413 | @@ -307,6 +511,8 @@ static struct static_clk jz_clk_ext = { |
| 414 | |
| 415 | static struct clk_ops jz_clk_pll_ops = { |
| 416 | .get_rate = jz_clk_pll_get_rate, |
| 417 | + .set_rate = jz_clk_pll_set_rate, |
| 418 | + .round_rate = jz_clk_pll_round_rate, |
| 419 | }; |
| 420 | |
| 421 | static struct clk jz_clk_pll = { |
| 422 | @@ -897,6 +1103,10 @@ static int jz4740_clock_init(void) |
| 423 | if (!jz_clock_base) |
| 424 | return -EBUSY; |
| 425 | |
| 426 | + jz_emc_base = ioremap(JZ4740_EMC_BASE_ADDR, 0x100); |
| 427 | + if (!jz_emc_base) |
| 428 | + return -EBUSY; |
| 429 | + |
| 430 | spin_lock_init(&jz_clock_lock); |
| 431 | |
| 432 | jz_clk_ext.rate = jz4740_clock_bdata.ext_rate; |
| 433 | diff --git a/arch/mips/jz4740/clock.h b/arch/mips/jz4740/clock.h |
| 434 | index 5d07499..cc8d1db 100644 |
| 435 | --- a/arch/mips/jz4740/clock.h |
| 436 | +++ b/arch/mips/jz4740/clock.h |
| 437 | @@ -17,6 +17,7 @@ |
| 438 | #define __MIPS_JZ4740_CLOCK_H__ |
| 439 | |
| 440 | #include <linux/list.h> |
| 441 | +#include <linux/types.h> |
| 442 | |
| 443 | struct jz4740_clock_board_data { |
| 444 | unsigned long ext_rate; |
| 445 | @@ -63,6 +64,9 @@ struct clk { |
| 446 | |
| 447 | int clk_is_enabled(struct clk *clk); |
| 448 | |
| 449 | +int clk_main_set_dividers(bool immediate, unsigned int cdiv, unsigned int hdiv, |
| 450 | + unsigned int mdiv, unsigned int pdiv); |
| 451 | + |
| 452 | #ifdef CONFIG_DEBUG_FS |
| 453 | void jz4740_clock_debugfs_init(void); |
| 454 | void jz4740_clock_debugfs_add_clk(struct clk *clk); |
| 455 | diff --git a/arch/mips/jz4740/cpufreq.c b/arch/mips/jz4740/cpufreq.c |
| 456 | new file mode 100644 |
| 457 | index 0000000..aa41e9f |
| 458 | --- /dev/null |
| 459 | +++ b/arch/mips/jz4740/cpufreq.c |
| 460 | @@ -0,0 +1,226 @@ |
| 461 | +/* |
| 462 | + * linux/arch/mips/jz4740/cpufreq.c |
| 463 | + * |
| 464 | + * cpufreq driver for JZ4740 |
| 465 | + * |
| 466 | + * Copyright (c) 2010 Ulrich Hecht <ulrich.hecht@gmail.com> |
| 467 | + * Copyright (c) 2010 Maarten ter Huurne <maarten@treewalker.org> |
| 468 | + * |
| 469 | + * This program is free software; you can redistribute it and/or modify |
| 470 | + * it under the terms of the GNU General Public License version 2 as |
| 471 | + * published by the Free Software Foundation. |
| 472 | + */ |
| 473 | + |
| 474 | +#include <linux/kernel.h> |
| 475 | +#include <linux/module.h> |
| 476 | +#include <linux/init.h> |
| 477 | +#include <linux/err.h> |
| 478 | + |
| 479 | +#include <linux/cpufreq.h> |
| 480 | + |
| 481 | +#include <linux/clk.h> |
| 482 | +#include <asm/mach-jz4740/base.h> |
| 483 | + |
| 484 | +#include "clock.h" |
| 485 | + |
| 486 | +#define DEBUG_CPUFREQ |
| 487 | + |
| 488 | +#ifdef DEBUG_CPUFREQ |
| 489 | +#define dprintk(X...) printk(KERN_INFO X) |
| 490 | +#else |
| 491 | +#define dprintk(X...) do { } while(0) |
| 492 | +#endif |
| 493 | + |
| 494 | +#define HCLK_MIN 30000 |
| 495 | +/* TODO: The maximum MCLK most likely depends on the SDRAM chips used, |
| 496 | + so it is board-specific. */ |
| 497 | +#define MCLK_MAX 140000 |
| 498 | + |
| 499 | +/* Same as jz_clk_main_divs, but with 24 and 32 removed because the hardware |
| 500 | + spec states those dividers must not be used for CCLK or HCLK. */ |
| 501 | +static const unsigned int jz4740_freq_cpu_divs[] = {1, 2, 3, 4, 6, 8, 12, 16}; |
| 502 | + |
| 503 | +struct jz4740_freq_percpu_info { |
| 504 | + unsigned int pll_rate; |
| 505 | + struct cpufreq_frequency_table table[ |
| 506 | + ARRAY_SIZE(jz4740_freq_cpu_divs) + 1]; |
| 507 | +}; |
| 508 | + |
| 509 | +static struct clk *pll; |
| 510 | +static struct clk *cclk; |
| 511 | + |
| 512 | +static struct jz4740_freq_percpu_info jz4740_freq_info; |
| 513 | + |
| 514 | +static struct cpufreq_driver cpufreq_jz4740_driver; |
| 515 | + |
| 516 | +static void jz4740_freq_fill_table(struct cpufreq_policy *policy, |
| 517 | + unsigned int pll_rate) |
| 518 | +{ |
| 519 | + struct cpufreq_frequency_table *table = &jz4740_freq_info.table[0]; |
| 520 | + int i; |
| 521 | + |
| 522 | +#ifdef CONFIG_CPU_FREQ_STAT_DETAILS |
| 523 | + /* for showing /sys/devices/system/cpu/cpuX/cpufreq/stats/ */ |
| 524 | + static bool init = false; |
| 525 | + if (init) |
| 526 | + cpufreq_frequency_table_put_attr(policy->cpu); |
| 527 | + else |
| 528 | + init = true; |
| 529 | +#endif |
| 530 | + |
| 531 | + jz4740_freq_info.pll_rate = pll_rate; |
| 532 | + |
| 533 | + for (i = 0; i < ARRAY_SIZE(jz4740_freq_cpu_divs); i++) { |
| 534 | + unsigned int freq = pll_rate / jz4740_freq_cpu_divs[i]; |
| 535 | + if (freq < HCLK_MIN) break; |
| 536 | + table[i].index = i; |
| 537 | + table[i].frequency = freq; |
| 538 | + } |
| 539 | + table[i].index = i; |
| 540 | + table[i].frequency = CPUFREQ_TABLE_END; |
| 541 | + |
| 542 | + policy->min = table[i - 1].frequency; |
| 543 | + policy->max = table[0].frequency; |
| 544 | + |
| 545 | +#ifdef CONFIG_CPU_FREQ_STAT_DETAILS |
| 546 | + cpufreq_frequency_table_get_attr(table, policy->cpu); |
| 547 | +#endif |
| 548 | +} |
| 549 | + |
| 550 | +static unsigned int jz4740_freq_get(unsigned int cpu) |
| 551 | +{ |
| 552 | + return clk_get_rate(cclk) / 1000; |
| 553 | +} |
| 554 | + |
| 555 | +static int jz4740_freq_verify(struct cpufreq_policy *policy) |
| 556 | +{ |
| 557 | + unsigned int new_pll; |
| 558 | + |
| 559 | + cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, |
| 560 | + policy->cpuinfo.max_freq); |
| 561 | + |
| 562 | + new_pll = clk_round_rate(pll, policy->max * 1000) / 1000; |
| 563 | + if (jz4740_freq_info.pll_rate != new_pll) |
| 564 | + jz4740_freq_fill_table(policy, new_pll); |
| 565 | + |
| 566 | + return 0; |
| 567 | +} |
| 568 | + |
| 569 | +static int jz4740_freq_target(struct cpufreq_policy *policy, |
| 570 | + unsigned int target_freq, |
| 571 | + unsigned int relation) |
| 572 | +{ |
| 573 | + struct cpufreq_frequency_table *table = &jz4740_freq_info.table[0]; |
| 574 | + struct cpufreq_freqs freqs; |
| 575 | + unsigned int new_index = 0; |
| 576 | + unsigned int old_pll = clk_get_rate(pll) / 1000; |
| 577 | + unsigned int new_pll = jz4740_freq_info.pll_rate; |
| 578 | + int ret = 0; |
| 579 | + |
| 580 | + if (cpufreq_frequency_table_target(policy, table, |
| 581 | + target_freq, relation, &new_index)) |
| 582 | + return -EINVAL; |
| 583 | + freqs = (struct cpufreq_freqs) { |
| 584 | + .old = jz4740_freq_get(policy->cpu), |
| 585 | + .new = table[new_index].frequency, |
| 586 | + .cpu = policy->cpu, |
| 587 | + .flags = cpufreq_jz4740_driver.flags, |
| 588 | + }; |
| 589 | + if (freqs.new != freqs.old || new_pll != old_pll) { |
| 590 | + unsigned int cdiv, hdiv, mdiv, pdiv; |
| 591 | + cdiv = jz4740_freq_cpu_divs[new_index]; |
| 592 | + hdiv = (cdiv == 3 || cdiv == 6) ? cdiv * 2 : cdiv * 3; |
| 593 | + while (new_pll < HCLK_MIN * hdiv) |
| 594 | + hdiv -= cdiv; |
| 595 | + mdiv = hdiv; |
| 596 | + if (new_pll > MCLK_MAX * mdiv) { |
| 597 | + /* 4,4 performs better than 3,6 */ |
| 598 | + if (new_pll > MCLK_MAX * 4) |
| 599 | + mdiv *= 2; |
| 600 | + else |
| 601 | + hdiv = mdiv = cdiv * 4; |
| 602 | + } |
| 603 | + pdiv = mdiv; |
| 604 | + dprintk(KERN_INFO "%s: cclk %p, setting from %d to %d, " |
| 605 | + "dividers %d, %d, %d, %d\n", |
| 606 | + __FUNCTION__, cclk, freqs.old, freqs.new, |
| 607 | + cdiv, hdiv, mdiv, pdiv); |
| 608 | + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); |
| 609 | + ret = clk_main_set_dividers(new_pll == old_pll, |
| 610 | + cdiv, hdiv, mdiv, pdiv); |
| 611 | + if (ret) { |
| 612 | + dprintk(KERN_INFO "failed to set dividers\n"); |
| 613 | + } else if (new_pll != old_pll) { |
| 614 | + dprintk(KERN_INFO "%s: pll %p, setting from %d to %d\n", |
| 615 | + __FUNCTION__, pll, old_pll, new_pll); |
| 616 | + ret = clk_set_rate(pll, new_pll * 1000); |
| 617 | + } |
| 618 | + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); |
| 619 | + } |
| 620 | + |
| 621 | + return ret; |
| 622 | +} |
| 623 | + |
| 624 | +static int jz4740_cpufreq_driver_init(struct cpufreq_policy *policy) |
| 625 | +{ |
| 626 | + int ret; |
| 627 | + |
| 628 | + dprintk(KERN_INFO "Jz4740 cpufreq driver\n"); |
| 629 | + |
| 630 | + if (policy->cpu != 0) |
| 631 | + return -EINVAL; |
| 632 | + |
| 633 | + pll = clk_get(NULL, "pll"); |
| 634 | + if (IS_ERR(pll)) { |
| 635 | + ret = PTR_ERR(pll); |
| 636 | + goto err_exit; |
| 637 | + } |
| 638 | + |
| 639 | + cclk = clk_get(NULL, "cclk"); |
| 640 | + if (IS_ERR(cclk)) { |
| 641 | + ret = PTR_ERR(cclk); |
| 642 | + goto err_clk_put_pll; |
| 643 | + } |
| 644 | + |
| 645 | + policy->cpuinfo.min_freq = HCLK_MIN; |
| 646 | + policy->cpuinfo.max_freq = 500000; |
| 647 | + policy->cpuinfo.transition_latency = 100000; /* in nanoseconds */ |
| 648 | + policy->cur = jz4740_freq_get(policy->cpu); |
| 649 | + policy->governor = CPUFREQ_DEFAULT_GOVERNOR; |
| 650 | + /* min and max are set by jz4740_freq_fill_table() */ |
| 651 | + |
| 652 | + jz4740_freq_fill_table(policy, clk_get_rate(pll) / 1000 /* in kHz */); |
| 653 | + |
| 654 | + return 0; |
| 655 | + |
| 656 | +err_clk_put_pll: |
| 657 | + clk_put(pll); |
| 658 | +err_exit: |
| 659 | + return ret; |
| 660 | +} |
| 661 | + |
| 662 | +static struct cpufreq_driver cpufreq_jz4740_driver = { |
| 663 | + .init = jz4740_cpufreq_driver_init, |
| 664 | + .verify = jz4740_freq_verify, |
| 665 | + .target = jz4740_freq_target, |
| 666 | + .get = jz4740_freq_get, |
| 667 | + .name = "jz4740", |
| 668 | +}; |
| 669 | + |
| 670 | +static int __init jz4740_cpufreq_init(void) |
| 671 | +{ |
| 672 | + return cpufreq_register_driver(&cpufreq_jz4740_driver); |
| 673 | +} |
| 674 | + |
| 675 | +static void __exit jz4740_cpufreq_exit(void) |
| 676 | +{ |
| 677 | + cpufreq_unregister_driver(&cpufreq_jz4740_driver); |
| 678 | +} |
| 679 | + |
| 680 | +module_init(jz4740_cpufreq_init); |
| 681 | +module_exit(jz4740_cpufreq_exit); |
| 682 | + |
| 683 | +MODULE_AUTHOR("Ulrich Hecht <ulrich.hecht@gmail.com>, " |
| 684 | + "Maarten ter Huurne <maarten@treewalker.org>"); |
| 685 | +MODULE_DESCRIPTION("cpufreq driver for Jz4740"); |
| 686 | +MODULE_LICENSE("GPL"); |
| 687 | diff --git a/arch/mips/jz4740/gpio.c b/arch/mips/jz4740/gpio.c |
| 688 | index 73031f7..e1ddb95 100644 |
| 689 | --- a/arch/mips/jz4740/gpio.c |
| 690 | +++ b/arch/mips/jz4740/gpio.c |
| 691 | @@ -17,8 +17,6 @@ |
| 692 | #include <linux/module.h> |
| 693 | #include <linux/init.h> |
| 694 | |
| 695 | -#include <linux/spinlock.h> |
| 696 | -#include <linux/sysdev.h> |
| 697 | #include <linux/io.h> |
| 698 | #include <linux/gpio.h> |
| 699 | #include <linux/delay.h> |
| 700 | @@ -30,6 +28,8 @@ |
| 701 | |
| 702 | #include <asm/mach-jz4740/base.h> |
| 703 | |
| 704 | +#include "irq.h" |
| 705 | + |
| 706 | #define JZ4740_GPIO_BASE_A (32*0) |
| 707 | #define JZ4740_GPIO_BASE_B (32*1) |
| 708 | #define JZ4740_GPIO_BASE_C (32*2) |
| 709 | @@ -77,16 +77,11 @@ |
| 710 | struct jz_gpio_chip { |
| 711 | unsigned int irq; |
| 712 | unsigned int irq_base; |
| 713 | - uint32_t wakeup; |
| 714 | - uint32_t suspend_mask; |
| 715 | uint32_t edge_trigger_both; |
| 716 | |
| 717 | void __iomem *base; |
| 718 | |
| 719 | - spinlock_t lock; |
| 720 | - |
| 721 | struct gpio_chip gpio_chip; |
| 722 | - struct sys_device sysdev; |
| 723 | }; |
| 724 | |
| 725 | static struct jz_gpio_chip jz4740_gpio_chips[]; |
| 726 | @@ -103,7 +98,8 @@ static inline struct jz_gpio_chip *gpio_chip_to_jz_gpio_chip(struct gpio_chip *g |
| 727 | |
| 728 | static inline struct jz_gpio_chip *irq_to_jz_gpio_chip(struct irq_data *data) |
| 729 | { |
| 730 | - return irq_data_get_irq_chip_data(data); |
| 731 | + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); |
| 732 | + return gc->private; |
| 733 | } |
| 734 | |
| 735 | static inline void jz_gpio_write_bit(unsigned int gpio, unsigned int reg) |
| 736 | @@ -305,21 +301,15 @@ static void jz_gpio_irq_demux_handler(unsigned int irq, struct irq_desc *desc) |
| 737 | { |
| 738 | uint32_t flag; |
| 739 | unsigned int gpio_irq; |
| 740 | - unsigned int gpio_bank; |
| 741 | struct jz_gpio_chip *chip = irq_desc_get_handler_data(desc); |
| 742 | |
| 743 | - gpio_bank = JZ4740_IRQ_GPIO0 - irq; |
| 744 | - |
| 745 | flag = readl(chip->base + JZ_REG_GPIO_FLAG); |
| 746 | - |
| 747 | if (!flag) |
| 748 | return; |
| 749 | |
| 750 | - gpio_irq = __fls(flag); |
| 751 | - |
| 752 | - jz_gpio_check_trigger_both(chip, irq); |
| 753 | + gpio_irq = chip->irq_base + __fls(flag); |
| 754 | |
| 755 | - gpio_irq += (gpio_bank << 5) + JZ4740_IRQ_GPIO(0); |
| 756 | + jz_gpio_check_trigger_both(chip, gpio_irq); |
| 757 | |
| 758 | generic_handle_irq(gpio_irq); |
| 759 | }; |
| 760 | @@ -330,18 +320,12 @@ static inline void jz_gpio_set_irq_bit(struct irq_data *data, unsigned int reg) |
| 761 | writel(IRQ_TO_BIT(data->irq), chip->base + reg); |
| 762 | } |
| 763 | |
| 764 | -static void jz_gpio_irq_mask(struct irq_data *data) |
| 765 | -{ |
| 766 | - jz_gpio_set_irq_bit(data, JZ_REG_GPIO_MASK_SET); |
| 767 | -}; |
| 768 | - |
| 769 | static void jz_gpio_irq_unmask(struct irq_data *data) |
| 770 | { |
| 771 | struct jz_gpio_chip *chip = irq_to_jz_gpio_chip(data); |
| 772 | |
| 773 | jz_gpio_check_trigger_both(chip, data->irq); |
| 774 | - |
| 775 | - jz_gpio_set_irq_bit(data, JZ_REG_GPIO_MASK_CLEAR); |
| 776 | + irq_gc_unmask_enable_reg(data); |
| 777 | }; |
| 778 | |
| 779 | /* TODO: Check if function is gpio */ |
| 780 | @@ -354,18 +338,13 @@ static unsigned int jz_gpio_irq_startup(struct irq_data *data) |
| 781 | |
| 782 | static void jz_gpio_irq_shutdown(struct irq_data *data) |
| 783 | { |
| 784 | - jz_gpio_irq_mask(data); |
| 785 | + irq_gc_mask_disable_reg(data); |
| 786 | |
| 787 | /* Set direction to input */ |
| 788 | jz_gpio_set_irq_bit(data, JZ_REG_GPIO_DIRECTION_CLEAR); |
| 789 | jz_gpio_set_irq_bit(data, JZ_REG_GPIO_SELECT_CLEAR); |
| 790 | } |
| 791 | |
| 792 | -static void jz_gpio_irq_ack(struct irq_data *data) |
| 793 | -{ |
| 794 | - jz_gpio_set_irq_bit(data, JZ_REG_GPIO_FLAG_CLEAR); |
| 795 | -}; |
| 796 | - |
| 797 | static int jz_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type) |
| 798 | { |
| 799 | struct jz_gpio_chip *chip = irq_to_jz_gpio_chip(data); |
| 800 | @@ -409,35 +388,13 @@ static int jz_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type) |
| 801 | static int jz_gpio_irq_set_wake(struct irq_data *data, unsigned int on) |
| 802 | { |
| 803 | struct jz_gpio_chip *chip = irq_to_jz_gpio_chip(data); |
| 804 | - spin_lock(&chip->lock); |
| 805 | - if (on) |
| 806 | - chip->wakeup |= IRQ_TO_BIT(data->irq); |
| 807 | - else |
| 808 | - chip->wakeup &= ~IRQ_TO_BIT(data->irq); |
| 809 | - spin_unlock(&chip->lock); |
| 810 | |
| 811 | + irq_gc_set_wake(data, on); |
| 812 | irq_set_irq_wake(chip->irq, on); |
| 813 | + |
| 814 | return 0; |
| 815 | } |
| 816 | |
| 817 | -static struct irq_chip jz_gpio_irq_chip = { |
| 818 | - .name = "GPIO", |
| 819 | - .irq_mask = jz_gpio_irq_mask, |
| 820 | - .irq_unmask = jz_gpio_irq_unmask, |
| 821 | - .irq_ack = jz_gpio_irq_ack, |
| 822 | - .irq_startup = jz_gpio_irq_startup, |
| 823 | - .irq_shutdown = jz_gpio_irq_shutdown, |
| 824 | - .irq_set_type = jz_gpio_irq_set_type, |
| 825 | - .irq_set_wake = jz_gpio_irq_set_wake, |
| 826 | - .flags = IRQCHIP_SET_TYPE_MASKED, |
| 827 | -}; |
| 828 | - |
| 829 | -/* |
| 830 | - * This lock class tells lockdep that GPIO irqs are in a different |
| 831 | - * category than their parents, so it won't report false recursion. |
| 832 | - */ |
| 833 | -static struct lock_class_key gpio_lock_class; |
| 834 | - |
| 835 | #define JZ4740_GPIO_CHIP(_bank) { \ |
| 836 | .irq_base = JZ4740_IRQ_GPIO_BASE_ ## _bank, \ |
| 837 | .gpio_chip = { \ |
| 838 | @@ -459,78 +416,49 @@ static struct jz_gpio_chip jz4740_gpio_chips[] = { |
| 839 | JZ4740_GPIO_CHIP(D), |
| 840 | }; |
| 841 | |
| 842 | -static inline struct jz_gpio_chip *sysdev_to_chip(struct sys_device *dev) |
| 843 | -{ |
| 844 | - return container_of(dev, struct jz_gpio_chip, sysdev); |
| 845 | -} |
| 846 | - |
| 847 | -static int jz4740_gpio_suspend(struct sys_device *dev, pm_message_t state) |
| 848 | -{ |
| 849 | - struct jz_gpio_chip *chip = sysdev_to_chip(dev); |
| 850 | - |
| 851 | - chip->suspend_mask = readl(chip->base + JZ_REG_GPIO_MASK); |
| 852 | - writel(~(chip->wakeup), chip->base + JZ_REG_GPIO_MASK_SET); |
| 853 | - writel(chip->wakeup, chip->base + JZ_REG_GPIO_MASK_CLEAR); |
| 854 | - |
| 855 | - return 0; |
| 856 | -} |
| 857 | - |
| 858 | -static int jz4740_gpio_resume(struct sys_device *dev) |
| 859 | +static void jz4740_gpio_chip_init(struct jz_gpio_chip *chip, unsigned int id) |
| 860 | { |
| 861 | - struct jz_gpio_chip *chip = sysdev_to_chip(dev); |
| 862 | - uint32_t mask = chip->suspend_mask; |
| 863 | - |
| 864 | - writel(~mask, chip->base + JZ_REG_GPIO_MASK_CLEAR); |
| 865 | - writel(mask, chip->base + JZ_REG_GPIO_MASK_SET); |
| 866 | + struct irq_chip_generic *gc; |
| 867 | + struct irq_chip_type *ct; |
| 868 | |
| 869 | - return 0; |
| 870 | -} |
| 871 | + chip->base = ioremap(JZ4740_GPIO_BASE_ADDR + (id * 0x100), 0x100); |
| 872 | |
| 873 | -static struct sysdev_class jz4740_gpio_sysdev_class = { |
| 874 | - .name = "gpio", |
| 875 | - .suspend = jz4740_gpio_suspend, |
| 876 | - .resume = jz4740_gpio_resume, |
| 877 | -}; |
| 878 | + chip->irq = JZ4740_IRQ_INTC_GPIO(id); |
| 879 | + irq_set_handler_data(chip->irq, chip); |
| 880 | + irq_set_chained_handler(chip->irq, jz_gpio_irq_demux_handler); |
| 881 | |
| 882 | -static int jz4740_gpio_chip_init(struct jz_gpio_chip *chip, unsigned int id) |
| 883 | -{ |
| 884 | - int ret, irq; |
| 885 | + gc = irq_alloc_generic_chip(chip->gpio_chip.label, 1, chip->irq_base, |
| 886 | + chip->base, handle_level_irq); |
| 887 | |
| 888 | - chip->sysdev.id = id; |
| 889 | - chip->sysdev.cls = &jz4740_gpio_sysdev_class; |
| 890 | - ret = sysdev_register(&chip->sysdev); |
| 891 | + gc->wake_enabled = IRQ_MSK(chip->gpio_chip.ngpio); |
| 892 | + gc->private = chip; |
| 893 | |
| 894 | - if (ret) |
| 895 | - return ret; |
| 896 | + ct = gc->chip_types; |
| 897 | + ct->regs.enable = JZ_REG_GPIO_MASK_CLEAR; |
| 898 | + ct->regs.disable = JZ_REG_GPIO_MASK_SET; |
| 899 | + ct->regs.ack = JZ_REG_GPIO_FLAG_CLEAR; |
| 900 | |
| 901 | - spin_lock_init(&chip->lock); |
| 902 | + ct->chip.name = "GPIO"; |
| 903 | + ct->chip.irq_mask = irq_gc_mask_disable_reg; |
| 904 | + ct->chip.irq_unmask = jz_gpio_irq_unmask; |
| 905 | + ct->chip.irq_ack = irq_gc_ack_set_bit; |
| 906 | + ct->chip.irq_suspend = jz4740_irq_suspend; |
| 907 | + ct->chip.irq_resume = jz4740_irq_resume; |
| 908 | + ct->chip.irq_startup = jz_gpio_irq_startup; |
| 909 | + ct->chip.irq_shutdown = jz_gpio_irq_shutdown; |
| 910 | + ct->chip.irq_set_type = jz_gpio_irq_set_type; |
| 911 | + ct->chip.irq_set_wake = jz_gpio_irq_set_wake; |
| 912 | + ct->chip.flags = IRQCHIP_SET_TYPE_MASKED; |
| 913 | |
| 914 | - chip->base = ioremap(JZ4740_GPIO_BASE_ADDR + (id * 0x100), 0x100); |
| 915 | + irq_setup_generic_chip(gc, IRQ_MSK(chip->gpio_chip.ngpio), |
| 916 | + IRQ_GC_INIT_NESTED_LOCK, 0, IRQ_NOPROBE | IRQ_LEVEL); |
| 917 | |
| 918 | gpiochip_add(&chip->gpio_chip); |
| 919 | - |
| 920 | - chip->irq = JZ4740_IRQ_INTC_GPIO(id); |
| 921 | - irq_set_handler_data(chip->irq, chip); |
| 922 | - irq_set_chained_handler(chip->irq, jz_gpio_irq_demux_handler); |
| 923 | - |
| 924 | - for (irq = chip->irq_base; irq < chip->irq_base + chip->gpio_chip.ngpio; ++irq) { |
| 925 | - irq_set_lockdep_class(irq, &gpio_lock_class); |
| 926 | - irq_set_chip_data(irq, chip); |
| 927 | - irq_set_chip_and_handler(irq, &jz_gpio_irq_chip, |
| 928 | - handle_level_irq); |
| 929 | - } |
| 930 | - |
| 931 | - return 0; |
| 932 | } |
| 933 | |
| 934 | static int __init jz4740_gpio_init(void) |
| 935 | { |
| 936 | unsigned int i; |
| 937 | - int ret; |
| 938 | - |
| 939 | - ret = sysdev_class_register(&jz4740_gpio_sysdev_class); |
| 940 | - if (ret) |
| 941 | - return ret; |
| 942 | |
| 943 | for (i = 0; i < ARRAY_SIZE(jz4740_gpio_chips); ++i) |
| 944 | jz4740_gpio_chip_init(&jz4740_gpio_chips[i], i); |
| 945 | diff --git a/arch/mips/jz4740/irq.c b/arch/mips/jz4740/irq.c |
| 946 | index d82c0c4..fc57ded 100644 |
| 947 | --- a/arch/mips/jz4740/irq.c |
| 948 | +++ b/arch/mips/jz4740/irq.c |
| 949 | @@ -32,8 +32,6 @@ |
| 950 | #include <asm/mach-jz4740/base.h> |
| 951 | |
| 952 | static void __iomem *jz_intc_base; |
| 953 | -static uint32_t jz_intc_wakeup; |
| 954 | -static uint32_t jz_intc_saved; |
| 955 | |
| 956 | #define JZ_REG_INTC_STATUS 0x00 |
| 957 | #define JZ_REG_INTC_MASK 0x04 |
| 958 | @@ -41,51 +39,36 @@ static uint32_t jz_intc_saved; |
| 959 | #define JZ_REG_INTC_CLEAR_MASK 0x0c |
| 960 | #define JZ_REG_INTC_PENDING 0x10 |
| 961 | |
| 962 | -#define IRQ_BIT(x) BIT((x) - JZ4740_IRQ_BASE) |
| 963 | - |
| 964 | -static inline unsigned long intc_irq_bit(struct irq_data *data) |
| 965 | +static irqreturn_t jz4740_cascade(int irq, void *data) |
| 966 | { |
| 967 | - return (unsigned long)irq_data_get_irq_chip_data(data); |
| 968 | -} |
| 969 | + uint32_t irq_reg; |
| 970 | |
| 971 | -static void intc_irq_unmask(struct irq_data *data) |
| 972 | -{ |
| 973 | - writel(intc_irq_bit(data), jz_intc_base + JZ_REG_INTC_CLEAR_MASK); |
| 974 | -} |
| 975 | + irq_reg = readl(jz_intc_base + JZ_REG_INTC_PENDING); |
| 976 | |
| 977 | -static void intc_irq_mask(struct irq_data *data) |
| 978 | -{ |
| 979 | - writel(intc_irq_bit(data), jz_intc_base + JZ_REG_INTC_SET_MASK); |
| 980 | + if (irq_reg) |
| 981 | + generic_handle_irq(__fls(irq_reg) + JZ4740_IRQ_BASE); |
| 982 | + |
| 983 | + return IRQ_HANDLED; |
| 984 | } |
| 985 | |
| 986 | -static int intc_irq_set_wake(struct irq_data *data, unsigned int on) |
| 987 | +static void jz4740_irq_set_mask(struct irq_chip_generic *gc, uint32_t mask) |
| 988 | { |
| 989 | - if (on) |
| 990 | - jz_intc_wakeup |= intc_irq_bit(data); |
| 991 | - else |
| 992 | - jz_intc_wakeup &= ~intc_irq_bit(data); |
| 993 | + struct irq_chip_regs *regs = &gc->chip_types->regs; |
| 994 | |
| 995 | - return 0; |
| 996 | + writel(mask, gc->reg_base + regs->enable); |
| 997 | + writel(~mask, gc->reg_base + regs->disable); |
| 998 | } |
| 999 | |
| 1000 | -static struct irq_chip intc_irq_type = { |
| 1001 | - .name = "INTC", |
| 1002 | - .irq_mask = intc_irq_mask, |
| 1003 | - .irq_mask_ack = intc_irq_mask, |
| 1004 | - .irq_unmask = intc_irq_unmask, |
| 1005 | - .irq_set_wake = intc_irq_set_wake, |
| 1006 | -}; |
| 1007 | - |
| 1008 | -static irqreturn_t jz4740_cascade(int irq, void *data) |
| 1009 | +void jz4740_irq_suspend(struct irq_data *data) |
| 1010 | { |
| 1011 | - uint32_t irq_reg; |
| 1012 | - |
| 1013 | - irq_reg = readl(jz_intc_base + JZ_REG_INTC_PENDING); |
| 1014 | - |
| 1015 | - if (irq_reg) |
| 1016 | - generic_handle_irq(__fls(irq_reg) + JZ4740_IRQ_BASE); |
| 1017 | + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); |
| 1018 | + jz4740_irq_set_mask(gc, gc->wake_active); |
| 1019 | +} |
| 1020 | |
| 1021 | - return IRQ_HANDLED; |
| 1022 | +void jz4740_irq_resume(struct irq_data *data) |
| 1023 | +{ |
| 1024 | + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); |
| 1025 | + jz4740_irq_set_mask(gc, gc->mask_cache); |
| 1026 | } |
| 1027 | |
| 1028 | static struct irqaction jz4740_cascade_action = { |
| 1029 | @@ -95,7 +78,9 @@ static struct irqaction jz4740_cascade_action = { |
| 1030 | |
| 1031 | void __init arch_init_irq(void) |
| 1032 | { |
| 1033 | - int i; |
| 1034 | + struct irq_chip_generic *gc; |
| 1035 | + struct irq_chip_type *ct; |
| 1036 | + |
| 1037 | mips_cpu_irq_init(); |
| 1038 | |
| 1039 | jz_intc_base = ioremap(JZ4740_INTC_BASE_ADDR, 0x14); |
| 1040 | @@ -103,10 +88,22 @@ void __init arch_init_irq(void) |
| 1041 | /* Mask all irqs */ |
| 1042 | writel(0xffffffff, jz_intc_base + JZ_REG_INTC_SET_MASK); |
| 1043 | |
| 1044 | - for (i = JZ4740_IRQ_BASE; i < JZ4740_IRQ_BASE + 32; i++) { |
| 1045 | - irq_set_chip_data(i, (void *)IRQ_BIT(i)); |
| 1046 | - irq_set_chip_and_handler(i, &intc_irq_type, handle_level_irq); |
| 1047 | - } |
| 1048 | + gc = irq_alloc_generic_chip("INTC", 1, JZ4740_IRQ_BASE, jz_intc_base, |
| 1049 | + handle_level_irq); |
| 1050 | + |
| 1051 | + gc->wake_enabled = IRQ_MSK(32); |
| 1052 | + |
| 1053 | + ct = gc->chip_types; |
| 1054 | + ct->regs.enable = JZ_REG_INTC_CLEAR_MASK; |
| 1055 | + ct->regs.disable = JZ_REG_INTC_SET_MASK; |
| 1056 | + ct->chip.irq_unmask = irq_gc_unmask_enable_reg; |
| 1057 | + ct->chip.irq_mask = irq_gc_mask_disable_reg; |
| 1058 | + ct->chip.irq_mask_ack = irq_gc_mask_disable_reg; |
| 1059 | + ct->chip.irq_set_wake = irq_gc_set_wake; |
| 1060 | + ct->chip.irq_suspend = jz4740_irq_suspend; |
| 1061 | + ct->chip.irq_resume = jz4740_irq_resume; |
| 1062 | + |
| 1063 | + irq_setup_generic_chip(gc, IRQ_MSK(32), 0, 0, IRQ_NOPROBE | IRQ_LEVEL); |
| 1064 | |
| 1065 | setup_irq(2, &jz4740_cascade_action); |
| 1066 | } |
| 1067 | @@ -122,19 +119,6 @@ asmlinkage void plat_irq_dispatch(void) |
| 1068 | spurious_interrupt(); |
| 1069 | } |
| 1070 | |
| 1071 | -void jz4740_intc_suspend(void) |
| 1072 | -{ |
| 1073 | - jz_intc_saved = readl(jz_intc_base + JZ_REG_INTC_MASK); |
| 1074 | - writel(~jz_intc_wakeup, jz_intc_base + JZ_REG_INTC_SET_MASK); |
| 1075 | - writel(jz_intc_wakeup, jz_intc_base + JZ_REG_INTC_CLEAR_MASK); |
| 1076 | -} |
| 1077 | - |
| 1078 | -void jz4740_intc_resume(void) |
| 1079 | -{ |
| 1080 | - writel(~jz_intc_saved, jz_intc_base + JZ_REG_INTC_CLEAR_MASK); |
| 1081 | - writel(jz_intc_saved, jz_intc_base + JZ_REG_INTC_SET_MASK); |
| 1082 | -} |
| 1083 | - |
| 1084 | #ifdef CONFIG_DEBUG_FS |
| 1085 | |
| 1086 | static inline void intc_seq_reg(struct seq_file *s, const char *name, |
| 1087 | diff --git a/arch/mips/jz4740/irq.h b/arch/mips/jz4740/irq.h |
| 1088 | index 56b5ead..f75e39d 100644 |
| 1089 | --- a/arch/mips/jz4740/irq.h |
| 1090 | +++ b/arch/mips/jz4740/irq.h |
| 1091 | @@ -15,7 +15,9 @@ |
| 1092 | #ifndef __MIPS_JZ4740_IRQ_H__ |
| 1093 | #define __MIPS_JZ4740_IRQ_H__ |
| 1094 | |
| 1095 | -extern void jz4740_intc_suspend(void); |
| 1096 | -extern void jz4740_intc_resume(void); |
| 1097 | +#include <linux/irq.h> |
| 1098 | + |
| 1099 | +extern void jz4740_irq_suspend(struct irq_data *data); |
| 1100 | +extern void jz4740_irq_resume(struct irq_data *data); |
| 1101 | |
| 1102 | #endif |
| 1103 | diff --git a/arch/mips/jz4740/platform.c b/arch/mips/jz4740/platform.c |
| 1104 | index cc6de5b..a5647d9 100644 |
| 1105 | --- a/arch/mips/jz4740/platform.c |
| 1106 | +++ b/arch/mips/jz4740/platform.c |
| 1107 | @@ -157,11 +157,29 @@ static struct resource jz4740_nand_resources[] = { |
| 1108 | .flags = IORESOURCE_MEM, |
| 1109 | }, |
| 1110 | { |
| 1111 | - .name = "bank", |
| 1112 | + .name = "bank1", |
| 1113 | .start = 0x18000000, |
| 1114 | .end = 0x180C0000 - 1, |
| 1115 | .flags = IORESOURCE_MEM, |
| 1116 | }, |
| 1117 | + { |
| 1118 | + .name = "bank2", |
| 1119 | + .start = 0x14000000, |
| 1120 | + .end = 0x140C0000 - 1, |
| 1121 | + .flags = IORESOURCE_MEM, |
| 1122 | + }, |
| 1123 | + { |
| 1124 | + .name = "bank3", |
| 1125 | + .start = 0x0C000000, |
| 1126 | + .end = 0x0C0C0000 - 1, |
| 1127 | + .flags = IORESOURCE_MEM, |
| 1128 | + }, |
| 1129 | + { |
| 1130 | + .name = "bank4", |
| 1131 | + .start = 0x08000000, |
| 1132 | + .end = 0x080C0000 - 1, |
| 1133 | + .flags = IORESOURCE_MEM, |
| 1134 | + }, |
| 1135 | }; |
| 1136 | |
| 1137 | struct platform_device jz4740_nand_device = { |
| 1138 | diff --git a/arch/mips/jz4740/pm.c b/arch/mips/jz4740/pm.c |
| 1139 | index 902d5b5..6744fa7 100644 |
| 1140 | --- a/arch/mips/jz4740/pm.c |
| 1141 | +++ b/arch/mips/jz4740/pm.c |
| 1142 | @@ -21,11 +21,9 @@ |
| 1143 | #include <asm/mach-jz4740/clock.h> |
| 1144 | |
| 1145 | #include "clock.h" |
| 1146 | -#include "irq.h" |
| 1147 | |
| 1148 | static int jz4740_pm_enter(suspend_state_t state) |
| 1149 | { |
| 1150 | - jz4740_intc_suspend(); |
| 1151 | jz4740_clock_suspend(); |
| 1152 | |
| 1153 | jz4740_clock_set_wait_mode(JZ4740_WAIT_MODE_SLEEP); |
| 1154 | @@ -37,7 +35,6 @@ static int jz4740_pm_enter(suspend_state_t state) |
| 1155 | jz4740_clock_set_wait_mode(JZ4740_WAIT_MODE_IDLE); |
| 1156 | |
| 1157 | jz4740_clock_resume(); |
| 1158 | - jz4740_intc_resume(); |
| 1159 | |
| 1160 | return 0; |
| 1161 | } |
| 1162 | diff --git a/arch/mips/jz4740/reset.c b/arch/mips/jz4740/reset.c |
| 1163 | index 5f1fb95..e6d1d7b 100644 |
| 1164 | --- a/arch/mips/jz4740/reset.c |
| 1165 | +++ b/arch/mips/jz4740/reset.c |
| 1166 | @@ -21,6 +21,9 @@ |
| 1167 | #include <asm/mach-jz4740/base.h> |
| 1168 | #include <asm/mach-jz4740/timer.h> |
| 1169 | |
| 1170 | +#include "reset.h" |
| 1171 | +#include "clock.h" |
| 1172 | + |
| 1173 | static void jz4740_halt(void) |
| 1174 | { |
| 1175 | while (1) { |
| 1176 | @@ -53,21 +56,52 @@ static void jz4740_restart(char *command) |
| 1177 | jz4740_halt(); |
| 1178 | } |
| 1179 | |
| 1180 | -#define JZ_REG_RTC_CTRL 0x00 |
| 1181 | -#define JZ_REG_RTC_HIBERNATE 0x20 |
| 1182 | +#define JZ_REG_RTC_CTRL 0x00 |
| 1183 | +#define JZ_REG_RTC_HIBERNATE 0x20 |
| 1184 | +#define JZ_REG_RTC_WAKEUP_FILTER 0x24 |
| 1185 | +#define JZ_REG_RTC_RESET_COUNTER 0x28 |
| 1186 | |
| 1187 | -#define JZ_RTC_CTRL_WRDY BIT(7) |
| 1188 | +#define JZ_RTC_CTRL_WRDY BIT(7) |
| 1189 | +#define JZ_RTC_WAKEUP_FILTER_MASK 0x0000FFE0 |
| 1190 | +#define JZ_RTC_RESET_COUNTER_MASK 0x00000FE0 |
| 1191 | |
| 1192 | -static void jz4740_power_off(void) |
| 1193 | +static inline void jz4740_rtc_wait_ready(void __iomem *rtc_base) |
| 1194 | { |
| 1195 | - void __iomem *rtc_base = ioremap(JZ4740_RTC_BASE_ADDR, 0x24); |
| 1196 | uint32_t ctrl; |
| 1197 | - |
| 1198 | do { |
| 1199 | ctrl = readl(rtc_base + JZ_REG_RTC_CTRL); |
| 1200 | } while (!(ctrl & JZ_RTC_CTRL_WRDY)); |
| 1201 | +} |
| 1202 | |
| 1203 | +static void jz4740_power_off(void) |
| 1204 | +{ |
| 1205 | + void __iomem *rtc_base = ioremap(JZ4740_RTC_BASE_ADDR, 0x38); |
| 1206 | + unsigned long long wakeup_filter_ticks; |
| 1207 | + unsigned long long reset_counter_ticks; |
| 1208 | + |
| 1209 | + /* Set minimum wakeup pin assertion time: 100 ms. |
| 1210 | + Range is 0 to 2 sec if RTC is clocked at 32 kHz. */ |
| 1211 | + wakeup_filter_ticks = (100 * jz4740_clock_bdata.rtc_rate) / 1000; |
| 1212 | + if (wakeup_filter_ticks < JZ_RTC_WAKEUP_FILTER_MASK) |
| 1213 | + wakeup_filter_ticks &= JZ_RTC_WAKEUP_FILTER_MASK; |
| 1214 | + else |
| 1215 | + wakeup_filter_ticks = JZ_RTC_WAKEUP_FILTER_MASK; |
| 1216 | + jz4740_rtc_wait_ready(rtc_base); |
| 1217 | + writel(wakeup_filter_ticks, rtc_base + JZ_REG_RTC_WAKEUP_FILTER); |
| 1218 | + |
| 1219 | + /* Set reset pin low-level assertion time after wakeup: 60 ms. |
| 1220 | + Range is 0 to 125 ms if RTC is clocked at 32 kHz. */ |
| 1221 | + reset_counter_ticks = (60 * jz4740_clock_bdata.rtc_rate) / 1000; |
| 1222 | + if (reset_counter_ticks < JZ_RTC_RESET_COUNTER_MASK) |
| 1223 | + reset_counter_ticks &= JZ_RTC_RESET_COUNTER_MASK; |
| 1224 | + else |
| 1225 | + reset_counter_ticks = JZ_RTC_RESET_COUNTER_MASK; |
| 1226 | + jz4740_rtc_wait_ready(rtc_base); |
| 1227 | + writel(reset_counter_ticks, rtc_base + JZ_REG_RTC_RESET_COUNTER); |
| 1228 | + |
| 1229 | + jz4740_rtc_wait_ready(rtc_base); |
| 1230 | writel(1, rtc_base + JZ_REG_RTC_HIBERNATE); |
| 1231 | + |
| 1232 | jz4740_halt(); |
| 1233 | } |
| 1234 | |
| 1235 | diff --git a/arch/mips/kernel/cpufreq/Kconfig b/arch/mips/kernel/cpufreq/Kconfig |
| 1236 | index 58c601e..11af8e8 100644 |
| 1237 | --- a/arch/mips/kernel/cpufreq/Kconfig |
| 1238 | +++ b/arch/mips/kernel/cpufreq/Kconfig |
| 1239 | @@ -8,7 +8,7 @@ config MIPS_EXTERNAL_TIMER |
| 1240 | config MIPS_CPUFREQ |
| 1241 | bool |
| 1242 | default y |
| 1243 | - depends on CPU_SUPPORTS_CPUFREQ && MIPS_EXTERNAL_TIMER |
| 1244 | + depends on CPU_SUPPORTS_CPUFREQ |
| 1245 | |
| 1246 | if MIPS_CPUFREQ |
| 1247 | |
| 1248 | @@ -24,6 +24,7 @@ config LOONGSON2_CPUFREQ |
| 1249 | tristate "Loongson2 CPUFreq Driver" |
| 1250 | select CPU_FREQ_TABLE |
| 1251 | depends on MIPS_CPUFREQ |
| 1252 | + depends on MIPS_EXTERNAL_TIMER |
| 1253 | help |
| 1254 | This option adds a CPUFreq driver for loongson processors which |
| 1255 | support software configurable cpu frequency. |
| 1256 | @@ -34,6 +35,16 @@ config LOONGSON2_CPUFREQ |
| 1257 | |
| 1258 | If in doubt, say N. |
| 1259 | |
| 1260 | +config CPU_FREQ_JZ |
| 1261 | + tristate "CPUfreq driver for JZ CPUs" |
| 1262 | + select CPU_FREQ_TABLE |
| 1263 | + depends on MACH_JZ4740 |
| 1264 | + default n |
| 1265 | + help |
| 1266 | + This enables the CPUfreq driver for JZ CPUs. |
| 1267 | + |
| 1268 | + If in doubt, say N. |
| 1269 | + |
| 1270 | endif # CPU_FREQ |
| 1271 | |
| 1272 | endmenu |
| 1273 | -- |
| 1274 | 1.7.4.1 |
| 1275 | |
| 1276 | |