mirror of
git://projects.qi-hardware.com/openwrt-xburst.git
synced 2025-01-01 04:09:55 +02:00
9a1322f91d
take from qi-kernel.git thanks to Werner
1276 lines
38 KiB
Diff
1276 lines
38 KiB
Diff
From 87eb40e63e1b149ef87fe05765b97328bfbd1c1f Mon Sep 17 00:00:00 2001
|
|
From: Xiangfu Liu <xiangfu@macbook.openmobilefree.net>
|
|
Date: Wed, 14 Sep 2011 14:29:37 +0800
|
|
Subject: [PATCH 24/32] forward code to linux 3.0
|
|
|
|
---
|
|
arch/mips/Kconfig | 2 +
|
|
arch/mips/include/asm/cacheflush.h | 6 +
|
|
arch/mips/include/asm/mach-jz4740/gpio.h | 5 +
|
|
arch/mips/include/asm/mach-jz4740/jz4740_fb.h | 8 +
|
|
arch/mips/jz4740/Makefile | 1 +
|
|
arch/mips/jz4740/clock.c | 230 +++++++++++++++++++++++-
|
|
arch/mips/jz4740/clock.h | 4 +
|
|
arch/mips/jz4740/cpufreq.c | 226 ++++++++++++++++++++++++
|
|
arch/mips/jz4740/gpio.c | 148 ++++------------
|
|
arch/mips/jz4740/irq.c | 92 ++++------
|
|
arch/mips/jz4740/irq.h | 6 +-
|
|
arch/mips/jz4740/platform.c | 20 ++-
|
|
arch/mips/jz4740/pm.c | 3 -
|
|
arch/mips/jz4740/reset.c | 46 +++++-
|
|
arch/mips/kernel/cpufreq/Kconfig | 13 ++-
|
|
15 files changed, 623 insertions(+), 187 deletions(-)
|
|
create mode 100644 arch/mips/jz4740/cpufreq.c
|
|
|
|
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
|
|
index 653da62..42c4e6a 100644
|
|
--- a/arch/mips/Kconfig
|
|
+++ b/arch/mips/Kconfig
|
|
@@ -211,6 +211,8 @@ config MACH_JZ4740
|
|
select SYS_HAS_EARLY_PRINTK
|
|
select HAVE_PWM
|
|
select HAVE_CLK
|
|
+ select CPU_SUPPORTS_CPUFREQ
|
|
+ select GENERIC_IRQ_CHIP
|
|
|
|
config LANTIQ
|
|
bool "Lantiq based platforms"
|
|
diff --git a/arch/mips/include/asm/cacheflush.h b/arch/mips/include/asm/cacheflush.h
|
|
index 40bb9fd..2a26af0 100644
|
|
--- a/arch/mips/include/asm/cacheflush.h
|
|
+++ b/arch/mips/include/asm/cacheflush.h
|
|
@@ -114,4 +114,10 @@ unsigned long run_uncached(void *func);
|
|
extern void *kmap_coherent(struct page *page, unsigned long addr);
|
|
extern void kunmap_coherent(void);
|
|
|
|
+#define ARCH_HAS_FLUSH_KERNEL_DCACHE_PAGE
|
|
+static inline void flush_kernel_dcache_page(struct page *page)
|
|
+{
|
|
+ flush_dcache_page(page);
|
|
+}
|
|
+
|
|
#endif /* _ASM_CACHEFLUSH_H */
|
|
diff --git a/arch/mips/include/asm/mach-jz4740/gpio.h b/arch/mips/include/asm/mach-jz4740/gpio.h
|
|
index 7b74703..5a3675e 100644
|
|
--- a/arch/mips/include/asm/mach-jz4740/gpio.h
|
|
+++ b/arch/mips/include/asm/mach-jz4740/gpio.h
|
|
@@ -253,6 +253,9 @@ uint32_t jz_gpio_port_get_value(int port, uint32_t mask);
|
|
#define JZ_GPIO_MEM_WAIT JZ_GPIO_PORTC(27)
|
|
#define JZ_GPIO_MEM_FRE JZ_GPIO_PORTC(28)
|
|
#define JZ_GPIO_MEM_FWE JZ_GPIO_PORTC(29)
|
|
+/* Pins have different assignment in SLCD mode */
|
|
+#define JZ_GPIO_SLCD_RS JZ_GPIO_PORTC(19)
|
|
+#define JZ_GPIO_SLCD_CS JZ_GPIO_PORTC(20)
|
|
|
|
#define JZ_GPIO_FUNC_LCD_DATA0 JZ_GPIO_FUNC1
|
|
#define JZ_GPIO_FUNC_LCD_DATA1 JZ_GPIO_FUNC1
|
|
@@ -284,6 +287,8 @@ uint32_t jz_gpio_port_get_value(int port, uint32_t mask);
|
|
#define JZ_GPIO_FUNC_MEM_WAIT JZ_GPIO_FUNC1
|
|
#define JZ_GPIO_FUNC_MEM_FRE JZ_GPIO_FUNC1
|
|
#define JZ_GPIO_FUNC_MEM_FWE JZ_GPIO_FUNC1
|
|
+#define JZ_GPIO_FUNC_SLCD_RS JZ_GPIO_FUNC1
|
|
+#define JZ_GPIO_FUNC_SLCD_CS JZ_GPIO_FUNC1
|
|
|
|
|
|
#define JZ_GPIO_MEM_ADDR19 JZ_GPIO_PORTB(22)
|
|
diff --git a/arch/mips/include/asm/mach-jz4740/jz4740_fb.h b/arch/mips/include/asm/mach-jz4740/jz4740_fb.h
|
|
index 6a50e6f..b2b6dba 100644
|
|
--- a/arch/mips/include/asm/mach-jz4740/jz4740_fb.h
|
|
+++ b/arch/mips/include/asm/mach-jz4740/jz4740_fb.h
|
|
@@ -30,6 +30,12 @@ enum jz4740_fb_lcd_type {
|
|
JZ_LCD_TYPE_DUAL_COLOR_STN = 10,
|
|
JZ_LCD_TYPE_DUAL_MONOCHROME_STN = 11,
|
|
JZ_LCD_TYPE_8BIT_SERIAL = 12,
|
|
+ JZ_LCD_TYPE_SMART_PARALLEL_8_BIT = 1 | (1 << 5),
|
|
+ JZ_LCD_TYPE_SMART_PARALLEL_16_BIT = 0 | (1 << 5),
|
|
+ JZ_LCD_TYPE_SMART_PARALLEL_18_BIT = 2 | (1 << 5),
|
|
+ JZ_LCD_TYPE_SMART_SERIAL_8_BIT = 1 | (3 << 5),
|
|
+ JZ_LCD_TYPE_SMART_SERIAL_16_BIT = 0 | (3 << 5),
|
|
+ JZ_LCD_TYPE_SMART_SERIAL_18_BIT = 2 | (3 << 5),
|
|
};
|
|
|
|
#define JZ4740_FB_SPECIAL_TFT_CONFIG(start, stop) (((start) << 16) | (stop))
|
|
@@ -62,6 +68,8 @@ struct jz4740_fb_platform_data {
|
|
|
|
unsigned pixclk_falling_edge:1;
|
|
unsigned date_enable_active_low:1;
|
|
+ unsigned chip_select_active_low:1;
|
|
+ unsigned register_select_active_low:1;
|
|
};
|
|
|
|
#endif
|
|
diff --git a/arch/mips/jz4740/Makefile b/arch/mips/jz4740/Makefile
|
|
index 72eb2ad..e88ab59 100644
|
|
--- a/arch/mips/jz4740/Makefile
|
|
+++ b/arch/mips/jz4740/Makefile
|
|
@@ -19,5 +19,6 @@ obj-$(CONFIG_JZ4740_ID800WT) += board-id800wt.o
|
|
# PM support
|
|
|
|
obj-$(CONFIG_PM) += pm.o
|
|
+obj-$(CONFIG_CPU_FREQ_JZ) += cpufreq.o
|
|
|
|
ccflags-y := -Werror -Wall
|
|
diff --git a/arch/mips/jz4740/clock.c b/arch/mips/jz4740/clock.c
|
|
index 118a8a5..da423d1 100644
|
|
--- a/arch/mips/jz4740/clock.c
|
|
+++ b/arch/mips/jz4740/clock.c
|
|
@@ -1,5 +1,8 @@
|
|
/*
|
|
+ * Copyright (c) 2006-2007, Ingenic Semiconductor Inc.
|
|
* Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
|
|
+ * Copyright (c) 2010, Ulrich Hecht <ulrich.hecht@gmail.com>
|
|
+ * Copyright (c) 2010, Maarten ter Huurne <maarten@treewalker.org>
|
|
* JZ4740 SoC clock support
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
@@ -41,16 +44,20 @@
|
|
#define JZ_CLOCK_CTRL_I2S_SRC_PLL BIT(31)
|
|
#define JZ_CLOCK_CTRL_KO_ENABLE BIT(30)
|
|
#define JZ_CLOCK_CTRL_UDC_SRC_PLL BIT(29)
|
|
-#define JZ_CLOCK_CTRL_UDIV_MASK 0x1f800000
|
|
#define JZ_CLOCK_CTRL_CHANGE_ENABLE BIT(22)
|
|
#define JZ_CLOCK_CTRL_PLL_HALF BIT(21)
|
|
-#define JZ_CLOCK_CTRL_LDIV_MASK 0x001f0000
|
|
#define JZ_CLOCK_CTRL_UDIV_OFFSET 23
|
|
#define JZ_CLOCK_CTRL_LDIV_OFFSET 16
|
|
#define JZ_CLOCK_CTRL_MDIV_OFFSET 12
|
|
#define JZ_CLOCK_CTRL_PDIV_OFFSET 8
|
|
#define JZ_CLOCK_CTRL_HDIV_OFFSET 4
|
|
#define JZ_CLOCK_CTRL_CDIV_OFFSET 0
|
|
+#define JZ_CLOCK_CTRL_UDIV_MASK (0x3f << JZ_CLOCK_CTRL_UDIV_OFFSET)
|
|
+#define JZ_CLOCK_CTRL_LDIV_MASK (0x1f << JZ_CLOCK_CTRL_LDIV_OFFSET)
|
|
+#define JZ_CLOCK_CTRL_MDIV_MASK (0x0f << JZ_CLOCK_CTRL_MDIV_OFFSET)
|
|
+#define JZ_CLOCK_CTRL_PDIV_MASK (0x0f << JZ_CLOCK_CTRL_PDIV_OFFSET)
|
|
+#define JZ_CLOCK_CTRL_HDIV_MASK (0x0f << JZ_CLOCK_CTRL_HDIV_OFFSET)
|
|
+#define JZ_CLOCK_CTRL_CDIV_MASK (0x0f << JZ_CLOCK_CTRL_CDIV_OFFSET)
|
|
|
|
#define JZ_CLOCK_GATE_UART0 BIT(0)
|
|
#define JZ_CLOCK_GATE_TCU BIT(1)
|
|
@@ -90,6 +97,7 @@
|
|
#define JZ_CLOCK_PLL_M_OFFSET 23
|
|
#define JZ_CLOCK_PLL_N_OFFSET 18
|
|
#define JZ_CLOCK_PLL_OD_OFFSET 16
|
|
+#define JZ_CLOCK_PLL_STABILIZE_OFFSET 0
|
|
|
|
#define JZ_CLOCK_LOW_POWER_MODE_DOZE BIT(2)
|
|
#define JZ_CLOCK_LOW_POWER_MODE_SLEEP BIT(0)
|
|
@@ -97,10 +105,15 @@
|
|
#define JZ_CLOCK_SLEEP_CTRL_SUSPEND_UHC BIT(7)
|
|
#define JZ_CLOCK_SLEEP_CTRL_ENABLE_UDC BIT(6)
|
|
|
|
+#define JZ_REG_EMC_RTCNT 0x88
|
|
+#define JZ_REG_EMC_RTCOR 0x8C
|
|
+
|
|
static void __iomem *jz_clock_base;
|
|
static spinlock_t jz_clock_lock;
|
|
static LIST_HEAD(jz_clocks);
|
|
|
|
+static void __iomem *jz_emc_base;
|
|
+
|
|
struct main_clk {
|
|
struct clk clk;
|
|
uint32_t div_offset;
|
|
@@ -204,25 +217,88 @@ static int jz_clk_ko_is_enabled(struct clk *clk)
|
|
return !!(jz_clk_reg_read(JZ_REG_CLOCK_CTRL) & JZ_CLOCK_CTRL_KO_ENABLE);
|
|
}
|
|
|
|
+static struct static_clk jz_clk_ext;
|
|
+
|
|
+static unsigned long jz_clk_pll_calc_rate(
|
|
+ unsigned int in_div, unsigned int feedback, unsigned int out_div)
|
|
+{
|
|
+ return ((jz_clk_ext.rate / in_div) * feedback) / out_div;
|
|
+}
|
|
+
|
|
+static void jz_clk_pll_calc_dividers(unsigned long rate,
|
|
+ unsigned int *in_div, unsigned int *feedback, unsigned int *out_div)
|
|
+{
|
|
+ unsigned int target;
|
|
+
|
|
+ /* The frequency after the input divider must be between 1 and 15 MHz.
|
|
+ The highest divider yields the best resolution. */
|
|
+ *in_div = jz_clk_ext.rate / 1000000;
|
|
+ if (*in_div >= 34)
|
|
+ *in_div = 33;
|
|
+
|
|
+ /* The frequency before the output divider must be between 100 and
|
|
+ 500 MHz. The lowest target rate is more energy efficient. */
|
|
+ if (rate < 25000000) {
|
|
+ *out_div = 4;
|
|
+ target = 25000000 * 4;
|
|
+ } else if (rate <= 50000000) {
|
|
+ *out_div = 4;
|
|
+ target = rate * 4;
|
|
+ } else if (rate <= 100000000) {
|
|
+ *out_div = 2;
|
|
+ target = rate * 2;
|
|
+ } else if (rate <= 500000000) {
|
|
+ *out_div = 1;
|
|
+ target = rate;
|
|
+ } else {
|
|
+ *out_div = 1;
|
|
+ target = 500000000;
|
|
+ }
|
|
+
|
|
+ /* Compute the feedback divider.
|
|
+ Since the divided input is at least 1 MHz and the target frequency
|
|
+ at most 500 MHz, the feedback will be at most 500 and will therefore
|
|
+ always fit in the 9-bit register.
|
|
+ Similarly, the divided input is at most 15 MHz and the target
|
|
+ frequency at least 100 MHz, so the feedback will be at least 6
|
|
+ where the minimum supported value is 2. */
|
|
+ *feedback = ((target / 1000) * *in_div) / (jz_clk_ext.rate / 1000);
|
|
+}
|
|
+
|
|
+static unsigned long jz_clk_pll_round_rate(struct clk *clk, unsigned long rate)
|
|
+{
|
|
+ unsigned int in_div, feedback, out_div;
|
|
+ /* The PLL frequency must be a multiple of 24 MHz, since the LCD pixel
|
|
+ * clock must be exactly 12 MHz for the TV-out to work.
|
|
+ * TODO: A multiple of 12 MHz for the PLL would work if the PLL would
|
|
+ * not be divided by 2 before being passed to the set of derived
|
|
+ * clocks that includes the LCD pixel clock.
|
|
+ * TODO: Systemwide decisions like this should be made by the board
|
|
+ * support code, so add some kind of hook for that.
|
|
+ */
|
|
+ unsigned long rate24 = (rate / 24000000) * 24000000;
|
|
+
|
|
+ jz_clk_pll_calc_dividers(rate24, &in_div, &feedback, &out_div);
|
|
+ return jz_clk_pll_calc_rate(in_div, feedback, out_div);
|
|
+}
|
|
+
|
|
static const int pllno[] = {1, 2, 2, 4};
|
|
|
|
static unsigned long jz_clk_pll_get_rate(struct clk *clk)
|
|
{
|
|
uint32_t val;
|
|
- int m;
|
|
- int n;
|
|
- int od;
|
|
+ unsigned int in_div, feedback, out_div;
|
|
|
|
val = jz_clk_reg_read(JZ_REG_CLOCK_PLL);
|
|
|
|
if (val & JZ_CLOCK_PLL_BYPASS)
|
|
return clk_get_rate(clk->parent);
|
|
|
|
- m = ((val >> 23) & 0x1ff) + 2;
|
|
- n = ((val >> 18) & 0x1f) + 2;
|
|
- od = (val >> 16) & 0x3;
|
|
+ feedback = ((val >> 23) & 0x1ff) + 2;
|
|
+ in_div = ((val >> 18) & 0x1f) + 2;
|
|
+ out_div = pllno[(val >> 16) & 0x3];
|
|
|
|
- return ((clk_get_rate(clk->parent) / n) * m) / pllno[od];
|
|
+ return jz_clk_pll_calc_rate(in_div, feedback, out_div);
|
|
}
|
|
|
|
static unsigned long jz_clk_pll_half_get_rate(struct clk *clk)
|
|
@@ -235,7 +311,77 @@ static unsigned long jz_clk_pll_half_get_rate(struct clk *clk)
|
|
return jz_clk_pll_get_rate(clk->parent) >> 1;
|
|
}
|
|
|
|
-static const int jz_clk_main_divs[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32};
|
|
+#define SDRAM_TREF 15625 /* Refresh period: 4096 refresh cycles/64ms */
|
|
+
|
|
+static void sdram_set_pll(unsigned int pllin)
|
|
+{
|
|
+ unsigned int ns, sdramclock;
|
|
+
|
|
+ ns = 1000000000 / pllin;
|
|
+ sdramclock = (SDRAM_TREF / ns) / 64 + 1;
|
|
+ if (sdramclock > 0xff) sdramclock = 0xff;
|
|
+ /* Set refresh registers */
|
|
+ writew(sdramclock, jz_emc_base + JZ_REG_EMC_RTCOR);
|
|
+ writew(sdramclock, jz_emc_base + JZ_REG_EMC_RTCNT);
|
|
+}
|
|
+
|
|
+static int jz_clk_pll_set_rate(struct clk *clk, unsigned long rate)
|
|
+{
|
|
+ unsigned int ctrl, plcr1;
|
|
+ unsigned int feedback, in_div, out_div, pllout, pllout2;
|
|
+
|
|
+ jz_clk_pll_calc_dividers(rate, &in_div, &feedback, &out_div);
|
|
+
|
|
+ ctrl = jz_clk_reg_read(JZ_REG_CLOCK_CTRL);
|
|
+ pllout = jz_clk_pll_calc_rate(in_div, feedback, out_div);
|
|
+ pllout2 = (ctrl & JZ_CLOCK_CTRL_PLL_HALF) ? pllout : (pllout / 2);
|
|
+
|
|
+ /* Init UHC clock */
|
|
+ writel(pllout2 / 48000000 - 1, jz_clock_base + JZ_REG_CLOCK_UHC);
|
|
+
|
|
+ plcr1 = ((feedback - 2) << JZ_CLOCK_PLL_M_OFFSET) |
|
|
+ ((in_div - 2) << JZ_CLOCK_PLL_N_OFFSET) |
|
|
+ ((out_div - 1) << JZ_CLOCK_PLL_OD_OFFSET) |
|
|
+ (0x20 << JZ_CLOCK_PLL_STABILIZE_OFFSET) |
|
|
+ JZ_CLOCK_PLL_ENABLED;
|
|
+
|
|
+ sdram_set_pll(pllout);
|
|
+
|
|
+ /* LCD pixclock */
|
|
+ writel(pllout2 / 12000000 - 1, jz_clock_base + JZ_REG_CLOCK_LCD);
|
|
+
|
|
+ /* configure PLL */
|
|
+ __asm__ __volatile__(
|
|
+ ".set noreorder\n\t"
|
|
+ ".align 5\n"
|
|
+ "sw %1,0(%0)\n\t"
|
|
+ "nop\n\t"
|
|
+ "nop\n\t"
|
|
+ "nop\n\t"
|
|
+ "nop\n\t"
|
|
+ "nop\n\t"
|
|
+ "nop\n\t"
|
|
+ "nop\n\t"
|
|
+ ".set reorder\n\t"
|
|
+ :
|
|
+ : "r" (jz_clock_base + JZ_REG_CLOCK_PLL), "r" (plcr1));
|
|
+
|
|
+ /* MtH: For some reason the MSC will have problems if this flag is not
|
|
+ restored, even though the MSC is supposedly the only divider
|
|
+ that is not affected by this flag. */
|
|
+ jz_clk_reg_set_bits(JZ_REG_CLOCK_CTRL, JZ_CLOCK_CTRL_CHANGE_ENABLE);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const unsigned int jz_clk_main_divs[] = {
|
|
+ 1, 2, 3, 4, 6, 8, 12, 16, 24, 32
|
|
+};
|
|
+static const unsigned int jz_clk_main_divs_inv[] = {
|
|
+ -1, 0, 1, 2, 3, -1, 4, -1, 5, -1, -1, -1, 6, -1, -1, -1,
|
|
+ 7, -1, -1, -1, -1, -1, -1, -1, 8, -1, -1, -1, -1, -1, -1, -1,
|
|
+ 9
|
|
+};
|
|
|
|
static unsigned long jz_clk_main_round_rate(struct clk *clk, unsigned long rate)
|
|
{
|
|
@@ -290,6 +436,64 @@ static int jz_clk_main_set_rate(struct clk *clk, unsigned long rate)
|
|
return 0;
|
|
}
|
|
|
|
+static struct main_clk jz_clk_cpu;
|
|
+
|
|
+int clk_main_set_dividers(bool immediate, unsigned int cdiv, unsigned int hdiv,
|
|
+ unsigned int mdiv, unsigned int pdiv)
|
|
+{
|
|
+ unsigned int cdiv_enc, hdiv_enc, mdiv_enc, pdiv_enc;
|
|
+ unsigned int ctrl;
|
|
+ unsigned int tmp, wait;
|
|
+
|
|
+ if (cdiv >= ARRAY_SIZE(jz_clk_main_divs_inv) ||
|
|
+ hdiv >= ARRAY_SIZE(jz_clk_main_divs_inv) ||
|
|
+ mdiv >= ARRAY_SIZE(jz_clk_main_divs_inv) ||
|
|
+ pdiv >= ARRAY_SIZE(jz_clk_main_divs_inv))
|
|
+ return -EINVAL;
|
|
+ cdiv_enc = jz_clk_main_divs_inv[cdiv];
|
|
+ hdiv_enc = jz_clk_main_divs_inv[hdiv];
|
|
+ mdiv_enc = jz_clk_main_divs_inv[mdiv];
|
|
+ pdiv_enc = jz_clk_main_divs_inv[pdiv];
|
|
+ if (cdiv_enc == (unsigned int)-1 ||
|
|
+ hdiv_enc == (unsigned int)-1 ||
|
|
+ mdiv_enc == (unsigned int)-1 ||
|
|
+ pdiv_enc == (unsigned int)-1)
|
|
+ return -EINVAL;
|
|
+
|
|
+ ctrl = jz_clk_reg_read(JZ_REG_CLOCK_CTRL);
|
|
+ ctrl &= ~(JZ_CLOCK_CTRL_CHANGE_ENABLE |
|
|
+ JZ_CLOCK_CTRL_CDIV_MASK | JZ_CLOCK_CTRL_HDIV_MASK |
|
|
+ JZ_CLOCK_CTRL_MDIV_MASK | JZ_CLOCK_CTRL_PDIV_MASK);
|
|
+ if (immediate) ctrl |= JZ_CLOCK_CTRL_CHANGE_ENABLE;
|
|
+ ctrl |= (cdiv_enc << JZ_CLOCK_CTRL_CDIV_OFFSET) |
|
|
+ (hdiv_enc << JZ_CLOCK_CTRL_HDIV_OFFSET) |
|
|
+ (mdiv_enc << JZ_CLOCK_CTRL_MDIV_OFFSET) |
|
|
+ (pdiv_enc << JZ_CLOCK_CTRL_PDIV_OFFSET);
|
|
+
|
|
+ /* set dividers */
|
|
+ /* delay loops lifted from the old Ingenic cpufreq driver */
|
|
+ wait = ((clk_get_rate(&jz_clk_cpu.clk) / 1000000) * 500) / 1000;
|
|
+ __asm__ __volatile__(
|
|
+ ".set noreorder\n\t"
|
|
+ ".align 5\n"
|
|
+ "sw %2,0(%1)\n\t"
|
|
+ "li %0,0\n\t"
|
|
+ "1:\n\t"
|
|
+ "bne %0,%3,1b\n\t"
|
|
+ "addi %0, 1\n\t"
|
|
+ "nop\n\t"
|
|
+ "nop\n\t"
|
|
+ "nop\n\t"
|
|
+ "nop\n\t"
|
|
+ ".set reorder\n\t"
|
|
+ : "=r" (tmp)
|
|
+ : "r" (jz_clock_base + JZ_REG_CLOCK_CTRL), "r" (ctrl),
|
|
+ "r" (wait));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(clk_main_set_dividers);
|
|
+
|
|
static struct clk_ops jz_clk_static_ops = {
|
|
.get_rate = jz_clk_static_get_rate,
|
|
.enable = jz_clk_enable_gating,
|
|
@@ -307,6 +511,8 @@ static struct static_clk jz_clk_ext = {
|
|
|
|
static struct clk_ops jz_clk_pll_ops = {
|
|
.get_rate = jz_clk_pll_get_rate,
|
|
+ .set_rate = jz_clk_pll_set_rate,
|
|
+ .round_rate = jz_clk_pll_round_rate,
|
|
};
|
|
|
|
static struct clk jz_clk_pll = {
|
|
@@ -897,6 +1103,10 @@ static int jz4740_clock_init(void)
|
|
if (!jz_clock_base)
|
|
return -EBUSY;
|
|
|
|
+ jz_emc_base = ioremap(JZ4740_EMC_BASE_ADDR, 0x100);
|
|
+ if (!jz_emc_base)
|
|
+ return -EBUSY;
|
|
+
|
|
spin_lock_init(&jz_clock_lock);
|
|
|
|
jz_clk_ext.rate = jz4740_clock_bdata.ext_rate;
|
|
diff --git a/arch/mips/jz4740/clock.h b/arch/mips/jz4740/clock.h
|
|
index 5d07499..cc8d1db 100644
|
|
--- a/arch/mips/jz4740/clock.h
|
|
+++ b/arch/mips/jz4740/clock.h
|
|
@@ -17,6 +17,7 @@
|
|
#define __MIPS_JZ4740_CLOCK_H__
|
|
|
|
#include <linux/list.h>
|
|
+#include <linux/types.h>
|
|
|
|
struct jz4740_clock_board_data {
|
|
unsigned long ext_rate;
|
|
@@ -63,6 +64,9 @@ struct clk {
|
|
|
|
int clk_is_enabled(struct clk *clk);
|
|
|
|
+int clk_main_set_dividers(bool immediate, unsigned int cdiv, unsigned int hdiv,
|
|
+ unsigned int mdiv, unsigned int pdiv);
|
|
+
|
|
#ifdef CONFIG_DEBUG_FS
|
|
void jz4740_clock_debugfs_init(void);
|
|
void jz4740_clock_debugfs_add_clk(struct clk *clk);
|
|
diff --git a/arch/mips/jz4740/cpufreq.c b/arch/mips/jz4740/cpufreq.c
|
|
new file mode 100644
|
|
index 0000000..aa41e9f
|
|
--- /dev/null
|
|
+++ b/arch/mips/jz4740/cpufreq.c
|
|
@@ -0,0 +1,226 @@
|
|
+/*
|
|
+ * linux/arch/mips/jz4740/cpufreq.c
|
|
+ *
|
|
+ * cpufreq driver for JZ4740
|
|
+ *
|
|
+ * Copyright (c) 2010 Ulrich Hecht <ulrich.hecht@gmail.com>
|
|
+ * Copyright (c) 2010 Maarten ter Huurne <maarten@treewalker.org>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License version 2 as
|
|
+ * published by the Free Software Foundation.
|
|
+ */
|
|
+
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/err.h>
|
|
+
|
|
+#include <linux/cpufreq.h>
|
|
+
|
|
+#include <linux/clk.h>
|
|
+#include <asm/mach-jz4740/base.h>
|
|
+
|
|
+#include "clock.h"
|
|
+
|
|
+#define DEBUG_CPUFREQ
|
|
+
|
|
+#ifdef DEBUG_CPUFREQ
|
|
+#define dprintk(X...) printk(KERN_INFO X)
|
|
+#else
|
|
+#define dprintk(X...) do { } while(0)
|
|
+#endif
|
|
+
|
|
+#define HCLK_MIN 30000
|
|
+/* TODO: The maximum MCLK most likely depends on the SDRAM chips used,
|
|
+ so it is board-specific. */
|
|
+#define MCLK_MAX 140000
|
|
+
|
|
+/* Same as jz_clk_main_divs, but with 24 and 32 removed because the hardware
|
|
+ spec states those dividers must not be used for CCLK or HCLK. */
|
|
+static const unsigned int jz4740_freq_cpu_divs[] = {1, 2, 3, 4, 6, 8, 12, 16};
|
|
+
|
|
+struct jz4740_freq_percpu_info {
|
|
+ unsigned int pll_rate;
|
|
+ struct cpufreq_frequency_table table[
|
|
+ ARRAY_SIZE(jz4740_freq_cpu_divs) + 1];
|
|
+};
|
|
+
|
|
+static struct clk *pll;
|
|
+static struct clk *cclk;
|
|
+
|
|
+static struct jz4740_freq_percpu_info jz4740_freq_info;
|
|
+
|
|
+static struct cpufreq_driver cpufreq_jz4740_driver;
|
|
+
|
|
+static void jz4740_freq_fill_table(struct cpufreq_policy *policy,
|
|
+ unsigned int pll_rate)
|
|
+{
|
|
+ struct cpufreq_frequency_table *table = &jz4740_freq_info.table[0];
|
|
+ int i;
|
|
+
|
|
+#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
|
|
+ /* for showing /sys/devices/system/cpu/cpuX/cpufreq/stats/ */
|
|
+ static bool init = false;
|
|
+ if (init)
|
|
+ cpufreq_frequency_table_put_attr(policy->cpu);
|
|
+ else
|
|
+ init = true;
|
|
+#endif
|
|
+
|
|
+ jz4740_freq_info.pll_rate = pll_rate;
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(jz4740_freq_cpu_divs); i++) {
|
|
+ unsigned int freq = pll_rate / jz4740_freq_cpu_divs[i];
|
|
+ if (freq < HCLK_MIN) break;
|
|
+ table[i].index = i;
|
|
+ table[i].frequency = freq;
|
|
+ }
|
|
+ table[i].index = i;
|
|
+ table[i].frequency = CPUFREQ_TABLE_END;
|
|
+
|
|
+ policy->min = table[i - 1].frequency;
|
|
+ policy->max = table[0].frequency;
|
|
+
|
|
+#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
|
|
+ cpufreq_frequency_table_get_attr(table, policy->cpu);
|
|
+#endif
|
|
+}
|
|
+
|
|
+static unsigned int jz4740_freq_get(unsigned int cpu)
|
|
+{
|
|
+ return clk_get_rate(cclk) / 1000;
|
|
+}
|
|
+
|
|
+static int jz4740_freq_verify(struct cpufreq_policy *policy)
|
|
+{
|
|
+ unsigned int new_pll;
|
|
+
|
|
+ cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
|
|
+ policy->cpuinfo.max_freq);
|
|
+
|
|
+ new_pll = clk_round_rate(pll, policy->max * 1000) / 1000;
|
|
+ if (jz4740_freq_info.pll_rate != new_pll)
|
|
+ jz4740_freq_fill_table(policy, new_pll);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int jz4740_freq_target(struct cpufreq_policy *policy,
|
|
+ unsigned int target_freq,
|
|
+ unsigned int relation)
|
|
+{
|
|
+ struct cpufreq_frequency_table *table = &jz4740_freq_info.table[0];
|
|
+ struct cpufreq_freqs freqs;
|
|
+ unsigned int new_index = 0;
|
|
+ unsigned int old_pll = clk_get_rate(pll) / 1000;
|
|
+ unsigned int new_pll = jz4740_freq_info.pll_rate;
|
|
+ int ret = 0;
|
|
+
|
|
+ if (cpufreq_frequency_table_target(policy, table,
|
|
+ target_freq, relation, &new_index))
|
|
+ return -EINVAL;
|
|
+ freqs = (struct cpufreq_freqs) {
|
|
+ .old = jz4740_freq_get(policy->cpu),
|
|
+ .new = table[new_index].frequency,
|
|
+ .cpu = policy->cpu,
|
|
+ .flags = cpufreq_jz4740_driver.flags,
|
|
+ };
|
|
+ if (freqs.new != freqs.old || new_pll != old_pll) {
|
|
+ unsigned int cdiv, hdiv, mdiv, pdiv;
|
|
+ cdiv = jz4740_freq_cpu_divs[new_index];
|
|
+ hdiv = (cdiv == 3 || cdiv == 6) ? cdiv * 2 : cdiv * 3;
|
|
+ while (new_pll < HCLK_MIN * hdiv)
|
|
+ hdiv -= cdiv;
|
|
+ mdiv = hdiv;
|
|
+ if (new_pll > MCLK_MAX * mdiv) {
|
|
+ /* 4,4 performs better than 3,6 */
|
|
+ if (new_pll > MCLK_MAX * 4)
|
|
+ mdiv *= 2;
|
|
+ else
|
|
+ hdiv = mdiv = cdiv * 4;
|
|
+ }
|
|
+ pdiv = mdiv;
|
|
+ dprintk(KERN_INFO "%s: cclk %p, setting from %d to %d, "
|
|
+ "dividers %d, %d, %d, %d\n",
|
|
+ __FUNCTION__, cclk, freqs.old, freqs.new,
|
|
+ cdiv, hdiv, mdiv, pdiv);
|
|
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
|
|
+ ret = clk_main_set_dividers(new_pll == old_pll,
|
|
+ cdiv, hdiv, mdiv, pdiv);
|
|
+ if (ret) {
|
|
+ dprintk(KERN_INFO "failed to set dividers\n");
|
|
+ } else if (new_pll != old_pll) {
|
|
+ dprintk(KERN_INFO "%s: pll %p, setting from %d to %d\n",
|
|
+ __FUNCTION__, pll, old_pll, new_pll);
|
|
+ ret = clk_set_rate(pll, new_pll * 1000);
|
|
+ }
|
|
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int jz4740_cpufreq_driver_init(struct cpufreq_policy *policy)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ dprintk(KERN_INFO "Jz4740 cpufreq driver\n");
|
|
+
|
|
+ if (policy->cpu != 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ pll = clk_get(NULL, "pll");
|
|
+ if (IS_ERR(pll)) {
|
|
+ ret = PTR_ERR(pll);
|
|
+ goto err_exit;
|
|
+ }
|
|
+
|
|
+ cclk = clk_get(NULL, "cclk");
|
|
+ if (IS_ERR(cclk)) {
|
|
+ ret = PTR_ERR(cclk);
|
|
+ goto err_clk_put_pll;
|
|
+ }
|
|
+
|
|
+ policy->cpuinfo.min_freq = HCLK_MIN;
|
|
+ policy->cpuinfo.max_freq = 500000;
|
|
+ policy->cpuinfo.transition_latency = 100000; /* in nanoseconds */
|
|
+ policy->cur = jz4740_freq_get(policy->cpu);
|
|
+ policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
|
|
+ /* min and max are set by jz4740_freq_fill_table() */
|
|
+
|
|
+ jz4740_freq_fill_table(policy, clk_get_rate(pll) / 1000 /* in kHz */);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err_clk_put_pll:
|
|
+ clk_put(pll);
|
|
+err_exit:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static struct cpufreq_driver cpufreq_jz4740_driver = {
|
|
+ .init = jz4740_cpufreq_driver_init,
|
|
+ .verify = jz4740_freq_verify,
|
|
+ .target = jz4740_freq_target,
|
|
+ .get = jz4740_freq_get,
|
|
+ .name = "jz4740",
|
|
+};
|
|
+
|
|
+static int __init jz4740_cpufreq_init(void)
|
|
+{
|
|
+ return cpufreq_register_driver(&cpufreq_jz4740_driver);
|
|
+}
|
|
+
|
|
+static void __exit jz4740_cpufreq_exit(void)
|
|
+{
|
|
+ cpufreq_unregister_driver(&cpufreq_jz4740_driver);
|
|
+}
|
|
+
|
|
+module_init(jz4740_cpufreq_init);
|
|
+module_exit(jz4740_cpufreq_exit);
|
|
+
|
|
+MODULE_AUTHOR("Ulrich Hecht <ulrich.hecht@gmail.com>, "
|
|
+ "Maarten ter Huurne <maarten@treewalker.org>");
|
|
+MODULE_DESCRIPTION("cpufreq driver for Jz4740");
|
|
+MODULE_LICENSE("GPL");
|
|
diff --git a/arch/mips/jz4740/gpio.c b/arch/mips/jz4740/gpio.c
|
|
index 73031f7..e1ddb95 100644
|
|
--- a/arch/mips/jz4740/gpio.c
|
|
+++ b/arch/mips/jz4740/gpio.c
|
|
@@ -17,8 +17,6 @@
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
|
|
-#include <linux/spinlock.h>
|
|
-#include <linux/sysdev.h>
|
|
#include <linux/io.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/delay.h>
|
|
@@ -30,6 +28,8 @@
|
|
|
|
#include <asm/mach-jz4740/base.h>
|
|
|
|
+#include "irq.h"
|
|
+
|
|
#define JZ4740_GPIO_BASE_A (32*0)
|
|
#define JZ4740_GPIO_BASE_B (32*1)
|
|
#define JZ4740_GPIO_BASE_C (32*2)
|
|
@@ -77,16 +77,11 @@
|
|
struct jz_gpio_chip {
|
|
unsigned int irq;
|
|
unsigned int irq_base;
|
|
- uint32_t wakeup;
|
|
- uint32_t suspend_mask;
|
|
uint32_t edge_trigger_both;
|
|
|
|
void __iomem *base;
|
|
|
|
- spinlock_t lock;
|
|
-
|
|
struct gpio_chip gpio_chip;
|
|
- struct sys_device sysdev;
|
|
};
|
|
|
|
static struct jz_gpio_chip jz4740_gpio_chips[];
|
|
@@ -103,7 +98,8 @@ static inline struct jz_gpio_chip *gpio_chip_to_jz_gpio_chip(struct gpio_chip *g
|
|
|
|
static inline struct jz_gpio_chip *irq_to_jz_gpio_chip(struct irq_data *data)
|
|
{
|
|
- return irq_data_get_irq_chip_data(data);
|
|
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
|
|
+ return gc->private;
|
|
}
|
|
|
|
static inline void jz_gpio_write_bit(unsigned int gpio, unsigned int reg)
|
|
@@ -305,21 +301,15 @@ static void jz_gpio_irq_demux_handler(unsigned int irq, struct irq_desc *desc)
|
|
{
|
|
uint32_t flag;
|
|
unsigned int gpio_irq;
|
|
- unsigned int gpio_bank;
|
|
struct jz_gpio_chip *chip = irq_desc_get_handler_data(desc);
|
|
|
|
- gpio_bank = JZ4740_IRQ_GPIO0 - irq;
|
|
-
|
|
flag = readl(chip->base + JZ_REG_GPIO_FLAG);
|
|
-
|
|
if (!flag)
|
|
return;
|
|
|
|
- gpio_irq = __fls(flag);
|
|
-
|
|
- jz_gpio_check_trigger_both(chip, irq);
|
|
+ gpio_irq = chip->irq_base + __fls(flag);
|
|
|
|
- gpio_irq += (gpio_bank << 5) + JZ4740_IRQ_GPIO(0);
|
|
+ jz_gpio_check_trigger_both(chip, gpio_irq);
|
|
|
|
generic_handle_irq(gpio_irq);
|
|
};
|
|
@@ -330,18 +320,12 @@ static inline void jz_gpio_set_irq_bit(struct irq_data *data, unsigned int reg)
|
|
writel(IRQ_TO_BIT(data->irq), chip->base + reg);
|
|
}
|
|
|
|
-static void jz_gpio_irq_mask(struct irq_data *data)
|
|
-{
|
|
- jz_gpio_set_irq_bit(data, JZ_REG_GPIO_MASK_SET);
|
|
-};
|
|
-
|
|
static void jz_gpio_irq_unmask(struct irq_data *data)
|
|
{
|
|
struct jz_gpio_chip *chip = irq_to_jz_gpio_chip(data);
|
|
|
|
jz_gpio_check_trigger_both(chip, data->irq);
|
|
-
|
|
- jz_gpio_set_irq_bit(data, JZ_REG_GPIO_MASK_CLEAR);
|
|
+ irq_gc_unmask_enable_reg(data);
|
|
};
|
|
|
|
/* TODO: Check if function is gpio */
|
|
@@ -354,18 +338,13 @@ static unsigned int jz_gpio_irq_startup(struct irq_data *data)
|
|
|
|
static void jz_gpio_irq_shutdown(struct irq_data *data)
|
|
{
|
|
- jz_gpio_irq_mask(data);
|
|
+ irq_gc_mask_disable_reg(data);
|
|
|
|
/* Set direction to input */
|
|
jz_gpio_set_irq_bit(data, JZ_REG_GPIO_DIRECTION_CLEAR);
|
|
jz_gpio_set_irq_bit(data, JZ_REG_GPIO_SELECT_CLEAR);
|
|
}
|
|
|
|
-static void jz_gpio_irq_ack(struct irq_data *data)
|
|
-{
|
|
- jz_gpio_set_irq_bit(data, JZ_REG_GPIO_FLAG_CLEAR);
|
|
-};
|
|
-
|
|
static int jz_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type)
|
|
{
|
|
struct jz_gpio_chip *chip = irq_to_jz_gpio_chip(data);
|
|
@@ -409,35 +388,13 @@ static int jz_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type)
|
|
static int jz_gpio_irq_set_wake(struct irq_data *data, unsigned int on)
|
|
{
|
|
struct jz_gpio_chip *chip = irq_to_jz_gpio_chip(data);
|
|
- spin_lock(&chip->lock);
|
|
- if (on)
|
|
- chip->wakeup |= IRQ_TO_BIT(data->irq);
|
|
- else
|
|
- chip->wakeup &= ~IRQ_TO_BIT(data->irq);
|
|
- spin_unlock(&chip->lock);
|
|
|
|
+ irq_gc_set_wake(data, on);
|
|
irq_set_irq_wake(chip->irq, on);
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
-static struct irq_chip jz_gpio_irq_chip = {
|
|
- .name = "GPIO",
|
|
- .irq_mask = jz_gpio_irq_mask,
|
|
- .irq_unmask = jz_gpio_irq_unmask,
|
|
- .irq_ack = jz_gpio_irq_ack,
|
|
- .irq_startup = jz_gpio_irq_startup,
|
|
- .irq_shutdown = jz_gpio_irq_shutdown,
|
|
- .irq_set_type = jz_gpio_irq_set_type,
|
|
- .irq_set_wake = jz_gpio_irq_set_wake,
|
|
- .flags = IRQCHIP_SET_TYPE_MASKED,
|
|
-};
|
|
-
|
|
-/*
|
|
- * This lock class tells lockdep that GPIO irqs are in a different
|
|
- * category than their parents, so it won't report false recursion.
|
|
- */
|
|
-static struct lock_class_key gpio_lock_class;
|
|
-
|
|
#define JZ4740_GPIO_CHIP(_bank) { \
|
|
.irq_base = JZ4740_IRQ_GPIO_BASE_ ## _bank, \
|
|
.gpio_chip = { \
|
|
@@ -459,78 +416,49 @@ static struct jz_gpio_chip jz4740_gpio_chips[] = {
|
|
JZ4740_GPIO_CHIP(D),
|
|
};
|
|
|
|
-static inline struct jz_gpio_chip *sysdev_to_chip(struct sys_device *dev)
|
|
-{
|
|
- return container_of(dev, struct jz_gpio_chip, sysdev);
|
|
-}
|
|
-
|
|
-static int jz4740_gpio_suspend(struct sys_device *dev, pm_message_t state)
|
|
-{
|
|
- struct jz_gpio_chip *chip = sysdev_to_chip(dev);
|
|
-
|
|
- chip->suspend_mask = readl(chip->base + JZ_REG_GPIO_MASK);
|
|
- writel(~(chip->wakeup), chip->base + JZ_REG_GPIO_MASK_SET);
|
|
- writel(chip->wakeup, chip->base + JZ_REG_GPIO_MASK_CLEAR);
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int jz4740_gpio_resume(struct sys_device *dev)
|
|
+static void jz4740_gpio_chip_init(struct jz_gpio_chip *chip, unsigned int id)
|
|
{
|
|
- struct jz_gpio_chip *chip = sysdev_to_chip(dev);
|
|
- uint32_t mask = chip->suspend_mask;
|
|
-
|
|
- writel(~mask, chip->base + JZ_REG_GPIO_MASK_CLEAR);
|
|
- writel(mask, chip->base + JZ_REG_GPIO_MASK_SET);
|
|
+ struct irq_chip_generic *gc;
|
|
+ struct irq_chip_type *ct;
|
|
|
|
- return 0;
|
|
-}
|
|
+ chip->base = ioremap(JZ4740_GPIO_BASE_ADDR + (id * 0x100), 0x100);
|
|
|
|
-static struct sysdev_class jz4740_gpio_sysdev_class = {
|
|
- .name = "gpio",
|
|
- .suspend = jz4740_gpio_suspend,
|
|
- .resume = jz4740_gpio_resume,
|
|
-};
|
|
+ chip->irq = JZ4740_IRQ_INTC_GPIO(id);
|
|
+ irq_set_handler_data(chip->irq, chip);
|
|
+ irq_set_chained_handler(chip->irq, jz_gpio_irq_demux_handler);
|
|
|
|
-static int jz4740_gpio_chip_init(struct jz_gpio_chip *chip, unsigned int id)
|
|
-{
|
|
- int ret, irq;
|
|
+ gc = irq_alloc_generic_chip(chip->gpio_chip.label, 1, chip->irq_base,
|
|
+ chip->base, handle_level_irq);
|
|
|
|
- chip->sysdev.id = id;
|
|
- chip->sysdev.cls = &jz4740_gpio_sysdev_class;
|
|
- ret = sysdev_register(&chip->sysdev);
|
|
+ gc->wake_enabled = IRQ_MSK(chip->gpio_chip.ngpio);
|
|
+ gc->private = chip;
|
|
|
|
- if (ret)
|
|
- return ret;
|
|
+ ct = gc->chip_types;
|
|
+ ct->regs.enable = JZ_REG_GPIO_MASK_CLEAR;
|
|
+ ct->regs.disable = JZ_REG_GPIO_MASK_SET;
|
|
+ ct->regs.ack = JZ_REG_GPIO_FLAG_CLEAR;
|
|
|
|
- spin_lock_init(&chip->lock);
|
|
+ ct->chip.name = "GPIO";
|
|
+ ct->chip.irq_mask = irq_gc_mask_disable_reg;
|
|
+ ct->chip.irq_unmask = jz_gpio_irq_unmask;
|
|
+ ct->chip.irq_ack = irq_gc_ack_set_bit;
|
|
+ ct->chip.irq_suspend = jz4740_irq_suspend;
|
|
+ ct->chip.irq_resume = jz4740_irq_resume;
|
|
+ ct->chip.irq_startup = jz_gpio_irq_startup;
|
|
+ ct->chip.irq_shutdown = jz_gpio_irq_shutdown;
|
|
+ ct->chip.irq_set_type = jz_gpio_irq_set_type;
|
|
+ ct->chip.irq_set_wake = jz_gpio_irq_set_wake;
|
|
+ ct->chip.flags = IRQCHIP_SET_TYPE_MASKED;
|
|
|
|
- chip->base = ioremap(JZ4740_GPIO_BASE_ADDR + (id * 0x100), 0x100);
|
|
+ irq_setup_generic_chip(gc, IRQ_MSK(chip->gpio_chip.ngpio),
|
|
+ IRQ_GC_INIT_NESTED_LOCK, 0, IRQ_NOPROBE | IRQ_LEVEL);
|
|
|
|
gpiochip_add(&chip->gpio_chip);
|
|
-
|
|
- chip->irq = JZ4740_IRQ_INTC_GPIO(id);
|
|
- irq_set_handler_data(chip->irq, chip);
|
|
- irq_set_chained_handler(chip->irq, jz_gpio_irq_demux_handler);
|
|
-
|
|
- for (irq = chip->irq_base; irq < chip->irq_base + chip->gpio_chip.ngpio; ++irq) {
|
|
- irq_set_lockdep_class(irq, &gpio_lock_class);
|
|
- irq_set_chip_data(irq, chip);
|
|
- irq_set_chip_and_handler(irq, &jz_gpio_irq_chip,
|
|
- handle_level_irq);
|
|
- }
|
|
-
|
|
- return 0;
|
|
}
|
|
|
|
static int __init jz4740_gpio_init(void)
|
|
{
|
|
unsigned int i;
|
|
- int ret;
|
|
-
|
|
- ret = sysdev_class_register(&jz4740_gpio_sysdev_class);
|
|
- if (ret)
|
|
- return ret;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(jz4740_gpio_chips); ++i)
|
|
jz4740_gpio_chip_init(&jz4740_gpio_chips[i], i);
|
|
diff --git a/arch/mips/jz4740/irq.c b/arch/mips/jz4740/irq.c
|
|
index d82c0c4..fc57ded 100644
|
|
--- a/arch/mips/jz4740/irq.c
|
|
+++ b/arch/mips/jz4740/irq.c
|
|
@@ -32,8 +32,6 @@
|
|
#include <asm/mach-jz4740/base.h>
|
|
|
|
static void __iomem *jz_intc_base;
|
|
-static uint32_t jz_intc_wakeup;
|
|
-static uint32_t jz_intc_saved;
|
|
|
|
#define JZ_REG_INTC_STATUS 0x00
|
|
#define JZ_REG_INTC_MASK 0x04
|
|
@@ -41,51 +39,36 @@ static uint32_t jz_intc_saved;
|
|
#define JZ_REG_INTC_CLEAR_MASK 0x0c
|
|
#define JZ_REG_INTC_PENDING 0x10
|
|
|
|
-#define IRQ_BIT(x) BIT((x) - JZ4740_IRQ_BASE)
|
|
-
|
|
-static inline unsigned long intc_irq_bit(struct irq_data *data)
|
|
+static irqreturn_t jz4740_cascade(int irq, void *data)
|
|
{
|
|
- return (unsigned long)irq_data_get_irq_chip_data(data);
|
|
-}
|
|
+ uint32_t irq_reg;
|
|
|
|
-static void intc_irq_unmask(struct irq_data *data)
|
|
-{
|
|
- writel(intc_irq_bit(data), jz_intc_base + JZ_REG_INTC_CLEAR_MASK);
|
|
-}
|
|
+ irq_reg = readl(jz_intc_base + JZ_REG_INTC_PENDING);
|
|
|
|
-static void intc_irq_mask(struct irq_data *data)
|
|
-{
|
|
- writel(intc_irq_bit(data), jz_intc_base + JZ_REG_INTC_SET_MASK);
|
|
+ if (irq_reg)
|
|
+ generic_handle_irq(__fls(irq_reg) + JZ4740_IRQ_BASE);
|
|
+
|
|
+ return IRQ_HANDLED;
|
|
}
|
|
|
|
-static int intc_irq_set_wake(struct irq_data *data, unsigned int on)
|
|
+static void jz4740_irq_set_mask(struct irq_chip_generic *gc, uint32_t mask)
|
|
{
|
|
- if (on)
|
|
- jz_intc_wakeup |= intc_irq_bit(data);
|
|
- else
|
|
- jz_intc_wakeup &= ~intc_irq_bit(data);
|
|
+ struct irq_chip_regs *regs = &gc->chip_types->regs;
|
|
|
|
- return 0;
|
|
+ writel(mask, gc->reg_base + regs->enable);
|
|
+ writel(~mask, gc->reg_base + regs->disable);
|
|
}
|
|
|
|
-static struct irq_chip intc_irq_type = {
|
|
- .name = "INTC",
|
|
- .irq_mask = intc_irq_mask,
|
|
- .irq_mask_ack = intc_irq_mask,
|
|
- .irq_unmask = intc_irq_unmask,
|
|
- .irq_set_wake = intc_irq_set_wake,
|
|
-};
|
|
-
|
|
-static irqreturn_t jz4740_cascade(int irq, void *data)
|
|
+void jz4740_irq_suspend(struct irq_data *data)
|
|
{
|
|
- uint32_t irq_reg;
|
|
-
|
|
- irq_reg = readl(jz_intc_base + JZ_REG_INTC_PENDING);
|
|
-
|
|
- if (irq_reg)
|
|
- generic_handle_irq(__fls(irq_reg) + JZ4740_IRQ_BASE);
|
|
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
|
|
+ jz4740_irq_set_mask(gc, gc->wake_active);
|
|
+}
|
|
|
|
- return IRQ_HANDLED;
|
|
+void jz4740_irq_resume(struct irq_data *data)
|
|
+{
|
|
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
|
|
+ jz4740_irq_set_mask(gc, gc->mask_cache);
|
|
}
|
|
|
|
static struct irqaction jz4740_cascade_action = {
|
|
@@ -95,7 +78,9 @@ static struct irqaction jz4740_cascade_action = {
|
|
|
|
void __init arch_init_irq(void)
|
|
{
|
|
- int i;
|
|
+ struct irq_chip_generic *gc;
|
|
+ struct irq_chip_type *ct;
|
|
+
|
|
mips_cpu_irq_init();
|
|
|
|
jz_intc_base = ioremap(JZ4740_INTC_BASE_ADDR, 0x14);
|
|
@@ -103,10 +88,22 @@ void __init arch_init_irq(void)
|
|
/* Mask all irqs */
|
|
writel(0xffffffff, jz_intc_base + JZ_REG_INTC_SET_MASK);
|
|
|
|
- for (i = JZ4740_IRQ_BASE; i < JZ4740_IRQ_BASE + 32; i++) {
|
|
- irq_set_chip_data(i, (void *)IRQ_BIT(i));
|
|
- irq_set_chip_and_handler(i, &intc_irq_type, handle_level_irq);
|
|
- }
|
|
+ gc = irq_alloc_generic_chip("INTC", 1, JZ4740_IRQ_BASE, jz_intc_base,
|
|
+ handle_level_irq);
|
|
+
|
|
+ gc->wake_enabled = IRQ_MSK(32);
|
|
+
|
|
+ ct = gc->chip_types;
|
|
+ ct->regs.enable = JZ_REG_INTC_CLEAR_MASK;
|
|
+ ct->regs.disable = JZ_REG_INTC_SET_MASK;
|
|
+ ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
|
|
+ ct->chip.irq_mask = irq_gc_mask_disable_reg;
|
|
+ ct->chip.irq_mask_ack = irq_gc_mask_disable_reg;
|
|
+ ct->chip.irq_set_wake = irq_gc_set_wake;
|
|
+ ct->chip.irq_suspend = jz4740_irq_suspend;
|
|
+ ct->chip.irq_resume = jz4740_irq_resume;
|
|
+
|
|
+ irq_setup_generic_chip(gc, IRQ_MSK(32), 0, 0, IRQ_NOPROBE | IRQ_LEVEL);
|
|
|
|
setup_irq(2, &jz4740_cascade_action);
|
|
}
|
|
@@ -122,19 +119,6 @@ asmlinkage void plat_irq_dispatch(void)
|
|
spurious_interrupt();
|
|
}
|
|
|
|
-void jz4740_intc_suspend(void)
|
|
-{
|
|
- jz_intc_saved = readl(jz_intc_base + JZ_REG_INTC_MASK);
|
|
- writel(~jz_intc_wakeup, jz_intc_base + JZ_REG_INTC_SET_MASK);
|
|
- writel(jz_intc_wakeup, jz_intc_base + JZ_REG_INTC_CLEAR_MASK);
|
|
-}
|
|
-
|
|
-void jz4740_intc_resume(void)
|
|
-{
|
|
- writel(~jz_intc_saved, jz_intc_base + JZ_REG_INTC_CLEAR_MASK);
|
|
- writel(jz_intc_saved, jz_intc_base + JZ_REG_INTC_SET_MASK);
|
|
-}
|
|
-
|
|
#ifdef CONFIG_DEBUG_FS
|
|
|
|
static inline void intc_seq_reg(struct seq_file *s, const char *name,
|
|
diff --git a/arch/mips/jz4740/irq.h b/arch/mips/jz4740/irq.h
|
|
index 56b5ead..f75e39d 100644
|
|
--- a/arch/mips/jz4740/irq.h
|
|
+++ b/arch/mips/jz4740/irq.h
|
|
@@ -15,7 +15,9 @@
|
|
#ifndef __MIPS_JZ4740_IRQ_H__
|
|
#define __MIPS_JZ4740_IRQ_H__
|
|
|
|
-extern void jz4740_intc_suspend(void);
|
|
-extern void jz4740_intc_resume(void);
|
|
+#include <linux/irq.h>
|
|
+
|
|
+extern void jz4740_irq_suspend(struct irq_data *data);
|
|
+extern void jz4740_irq_resume(struct irq_data *data);
|
|
|
|
#endif
|
|
diff --git a/arch/mips/jz4740/platform.c b/arch/mips/jz4740/platform.c
|
|
index cc6de5b..a5647d9 100644
|
|
--- a/arch/mips/jz4740/platform.c
|
|
+++ b/arch/mips/jz4740/platform.c
|
|
@@ -157,11 +157,29 @@ static struct resource jz4740_nand_resources[] = {
|
|
.flags = IORESOURCE_MEM,
|
|
},
|
|
{
|
|
- .name = "bank",
|
|
+ .name = "bank1",
|
|
.start = 0x18000000,
|
|
.end = 0x180C0000 - 1,
|
|
.flags = IORESOURCE_MEM,
|
|
},
|
|
+ {
|
|
+ .name = "bank2",
|
|
+ .start = 0x14000000,
|
|
+ .end = 0x140C0000 - 1,
|
|
+ .flags = IORESOURCE_MEM,
|
|
+ },
|
|
+ {
|
|
+ .name = "bank3",
|
|
+ .start = 0x0C000000,
|
|
+ .end = 0x0C0C0000 - 1,
|
|
+ .flags = IORESOURCE_MEM,
|
|
+ },
|
|
+ {
|
|
+ .name = "bank4",
|
|
+ .start = 0x08000000,
|
|
+ .end = 0x080C0000 - 1,
|
|
+ .flags = IORESOURCE_MEM,
|
|
+ },
|
|
};
|
|
|
|
struct platform_device jz4740_nand_device = {
|
|
diff --git a/arch/mips/jz4740/pm.c b/arch/mips/jz4740/pm.c
|
|
index 902d5b5..6744fa7 100644
|
|
--- a/arch/mips/jz4740/pm.c
|
|
+++ b/arch/mips/jz4740/pm.c
|
|
@@ -21,11 +21,9 @@
|
|
#include <asm/mach-jz4740/clock.h>
|
|
|
|
#include "clock.h"
|
|
-#include "irq.h"
|
|
|
|
static int jz4740_pm_enter(suspend_state_t state)
|
|
{
|
|
- jz4740_intc_suspend();
|
|
jz4740_clock_suspend();
|
|
|
|
jz4740_clock_set_wait_mode(JZ4740_WAIT_MODE_SLEEP);
|
|
@@ -37,7 +35,6 @@ static int jz4740_pm_enter(suspend_state_t state)
|
|
jz4740_clock_set_wait_mode(JZ4740_WAIT_MODE_IDLE);
|
|
|
|
jz4740_clock_resume();
|
|
- jz4740_intc_resume();
|
|
|
|
return 0;
|
|
}
|
|
diff --git a/arch/mips/jz4740/reset.c b/arch/mips/jz4740/reset.c
|
|
index 5f1fb95..e6d1d7b 100644
|
|
--- a/arch/mips/jz4740/reset.c
|
|
+++ b/arch/mips/jz4740/reset.c
|
|
@@ -21,6 +21,9 @@
|
|
#include <asm/mach-jz4740/base.h>
|
|
#include <asm/mach-jz4740/timer.h>
|
|
|
|
+#include "reset.h"
|
|
+#include "clock.h"
|
|
+
|
|
static void jz4740_halt(void)
|
|
{
|
|
while (1) {
|
|
@@ -53,21 +56,52 @@ static void jz4740_restart(char *command)
|
|
jz4740_halt();
|
|
}
|
|
|
|
-#define JZ_REG_RTC_CTRL 0x00
|
|
-#define JZ_REG_RTC_HIBERNATE 0x20
|
|
+#define JZ_REG_RTC_CTRL 0x00
|
|
+#define JZ_REG_RTC_HIBERNATE 0x20
|
|
+#define JZ_REG_RTC_WAKEUP_FILTER 0x24
|
|
+#define JZ_REG_RTC_RESET_COUNTER 0x28
|
|
|
|
-#define JZ_RTC_CTRL_WRDY BIT(7)
|
|
+#define JZ_RTC_CTRL_WRDY BIT(7)
|
|
+#define JZ_RTC_WAKEUP_FILTER_MASK 0x0000FFE0
|
|
+#define JZ_RTC_RESET_COUNTER_MASK 0x00000FE0
|
|
|
|
-static void jz4740_power_off(void)
|
|
+static inline void jz4740_rtc_wait_ready(void __iomem *rtc_base)
|
|
{
|
|
- void __iomem *rtc_base = ioremap(JZ4740_RTC_BASE_ADDR, 0x24);
|
|
uint32_t ctrl;
|
|
-
|
|
do {
|
|
ctrl = readl(rtc_base + JZ_REG_RTC_CTRL);
|
|
} while (!(ctrl & JZ_RTC_CTRL_WRDY));
|
|
+}
|
|
|
|
+static void jz4740_power_off(void)
|
|
+{
|
|
+ void __iomem *rtc_base = ioremap(JZ4740_RTC_BASE_ADDR, 0x38);
|
|
+ unsigned long long wakeup_filter_ticks;
|
|
+ unsigned long long reset_counter_ticks;
|
|
+
|
|
+ /* Set minimum wakeup pin assertion time: 100 ms.
|
|
+ Range is 0 to 2 sec if RTC is clocked at 32 kHz. */
|
|
+ wakeup_filter_ticks = (100 * jz4740_clock_bdata.rtc_rate) / 1000;
|
|
+ if (wakeup_filter_ticks < JZ_RTC_WAKEUP_FILTER_MASK)
|
|
+ wakeup_filter_ticks &= JZ_RTC_WAKEUP_FILTER_MASK;
|
|
+ else
|
|
+ wakeup_filter_ticks = JZ_RTC_WAKEUP_FILTER_MASK;
|
|
+ jz4740_rtc_wait_ready(rtc_base);
|
|
+ writel(wakeup_filter_ticks, rtc_base + JZ_REG_RTC_WAKEUP_FILTER);
|
|
+
|
|
+ /* Set reset pin low-level assertion time after wakeup: 60 ms.
|
|
+ Range is 0 to 125 ms if RTC is clocked at 32 kHz. */
|
|
+ reset_counter_ticks = (60 * jz4740_clock_bdata.rtc_rate) / 1000;
|
|
+ if (reset_counter_ticks < JZ_RTC_RESET_COUNTER_MASK)
|
|
+ reset_counter_ticks &= JZ_RTC_RESET_COUNTER_MASK;
|
|
+ else
|
|
+ reset_counter_ticks = JZ_RTC_RESET_COUNTER_MASK;
|
|
+ jz4740_rtc_wait_ready(rtc_base);
|
|
+ writel(reset_counter_ticks, rtc_base + JZ_REG_RTC_RESET_COUNTER);
|
|
+
|
|
+ jz4740_rtc_wait_ready(rtc_base);
|
|
writel(1, rtc_base + JZ_REG_RTC_HIBERNATE);
|
|
+
|
|
jz4740_halt();
|
|
}
|
|
|
|
diff --git a/arch/mips/kernel/cpufreq/Kconfig b/arch/mips/kernel/cpufreq/Kconfig
|
|
index 58c601e..11af8e8 100644
|
|
--- a/arch/mips/kernel/cpufreq/Kconfig
|
|
+++ b/arch/mips/kernel/cpufreq/Kconfig
|
|
@@ -8,7 +8,7 @@ config MIPS_EXTERNAL_TIMER
|
|
config MIPS_CPUFREQ
|
|
bool
|
|
default y
|
|
- depends on CPU_SUPPORTS_CPUFREQ && MIPS_EXTERNAL_TIMER
|
|
+ depends on CPU_SUPPORTS_CPUFREQ
|
|
|
|
if MIPS_CPUFREQ
|
|
|
|
@@ -24,6 +24,7 @@ config LOONGSON2_CPUFREQ
|
|
tristate "Loongson2 CPUFreq Driver"
|
|
select CPU_FREQ_TABLE
|
|
depends on MIPS_CPUFREQ
|
|
+ depends on MIPS_EXTERNAL_TIMER
|
|
help
|
|
This option adds a CPUFreq driver for loongson processors which
|
|
support software configurable cpu frequency.
|
|
@@ -34,6 +35,16 @@ config LOONGSON2_CPUFREQ
|
|
|
|
If in doubt, say N.
|
|
|
|
+config CPU_FREQ_JZ
|
|
+ tristate "CPUfreq driver for JZ CPUs"
|
|
+ select CPU_FREQ_TABLE
|
|
+ depends on MACH_JZ4740
|
|
+ default n
|
|
+ help
|
|
+ This enables the CPUfreq driver for JZ CPUs.
|
|
+
|
|
+ If in doubt, say N.
|
|
+
|
|
endif # CPU_FREQ
|
|
|
|
endmenu
|
|
--
|
|
1.7.4.1
|
|
|