diff options
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/boot/compressed/head.S | 2 | ||||
-rw-r--r-- | arch/arm/include/asm/fiq.h | 5 | ||||
-rw-r--r-- | arch/arm/kernel/fiq.c | 79 | ||||
-rw-r--r-- | arch/arm/mach-s3c2410/include/mach/gpio.h | 5 | ||||
-rw-r--r-- | arch/arm/mach-s3c2410/include/mach/irqs.h | 22 | ||||
-rw-r--r-- | arch/arm/plat-s3c/include/plat/nand.h | 1 | ||||
-rw-r--r-- | arch/arm/plat-s3c/include/plat/uncompress.h | 19 | ||||
-rw-r--r-- | arch/arm/plat-s3c/pwm.c | 72 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/gpiolib.c | 13 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/include/plat/irq.h | 21 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/irq.c | 48 |
11 files changed, 265 insertions, 22 deletions
diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S index fa6fbf45cf3..4f0708b83ed 100644 --- a/arch/arm/boot/compressed/head.S +++ b/arch/arm/boot/compressed/head.S @@ -58,7 +58,7 @@ add \rb, \rb, #0x00010000 @ Ser1 #endif .endm -#elif defined(CONFIG_ARCH_S3C2410) +#elif defined(CONFIG_ARCH_S3C2410) && CONFIG_S3C_LOWLEVEL_UART_PORT >= 0 .macro loadsp, rb mov \rb, #0x50000000 add \rb, \rb, #0x4000 * CONFIG_S3C_LOWLEVEL_UART_PORT diff --git a/arch/arm/include/asm/fiq.h b/arch/arm/include/asm/fiq.h index 2242ce22ec6..7ade2b8445d 100644 --- a/arch/arm/include/asm/fiq.h +++ b/arch/arm/include/asm/fiq.h @@ -29,8 +29,9 @@ struct fiq_handler { extern int claim_fiq(struct fiq_handler *f); extern void release_fiq(struct fiq_handler *f); extern void set_fiq_handler(void *start, unsigned int length); -extern void set_fiq_regs(struct pt_regs *regs); -extern void get_fiq_regs(struct pt_regs *regs); +extern void set_fiq_c_handler(void (*handler)(void)); +extern void __attribute__((naked)) set_fiq_regs(struct pt_regs *regs); +extern void __attribute__((naked)) get_fiq_regs(struct pt_regs *regs); extern void enable_fiq(int fiq); extern void disable_fiq(int fiq); diff --git a/arch/arm/kernel/fiq.c b/arch/arm/kernel/fiq.c index 6ff7919613d..c07691ef4c0 100644 --- a/arch/arm/kernel/fiq.c +++ b/arch/arm/kernel/fiq.c @@ -8,6 +8,8 @@ * * FIQ support re-written by Russell King to be more generic * + * FIQ handler in C supoprt written by Andy Green <andy@openmoko.com> + * * We now properly support a method by which the FIQ handlers can * be stacked onto the vector. We still do not support sharing * the FIQ vector itself. @@ -124,6 +126,83 @@ void __naked get_fiq_regs(struct pt_regs *regs) : "r" (®s->ARM_r8), "I" (PSR_I_BIT | PSR_F_BIT | FIQ_MODE)); } +/* -------- FIQ handler in C --------- + * + * Major Caveats for using this + * --------------------------- + * * + * * 1) it CANNOT touch any vmalloc()'d memory, only memory + * that was kmalloc()'d. Static allocations in the monolithic kernel + * are kmalloc()'d so they are okay. You can touch memory-mapped IO, but + * the pointer for it has to have been stored in kmalloc'd memory. The + * reason for this is simple: every now and then Linux turns off interrupts + * and reorders the paging tables. If a FIQ happens during this time, the + * virtual memory space can be partly or entirely disordered or missing. + * + * 2) Because vmalloc() is used when a module is inserted, THIS FIQ + * ISR HAS TO BE IN THE MONOLITHIC KERNEL, not a module. But the way + * it is set up, you can all to enable and disable it from your module + * and intercommunicate with it through struct fiq_ipc + * fiq_ipc which you can define in + * asm/archfiq_ipc_type.h. The reason is the same as above, a + * FIQ could happen while even the ISR is not present in virtual memory + * space due to pagetables being changed at the time. + * + * 3) You can't call any Linux API code except simple macros + * - understand that FIQ can come in at any time, no matter what + * state of undress the kernel may privately be in, thinking it + * locked the door by turning off interrupts... FIQ is an + * unstoppable monster force (which is its value) + * - they are not vmalloc()'d memory safe + * - they might do crazy stuff like sleep: FIQ pisses fire and + * is not interested in 'sleep' that the weak seem to need + * - calling APIs from FIQ can re-enter un-renterable things + * - summary: you cannot interoperate with linux APIs directly in the FIQ ISR + * + * If you follow these rules, it is fantastic, an extremely powerful, solid, + * genuine hard realtime feature. + */ + +static void (*current_fiq_c_isr)(void); +#define FIQ_C_ISR_STACK_SIZE 256 + +static void __attribute__((naked)) __jump_to_isr(void) +{ + asm __volatile__ ("mov pc, r8"); +} + + +static void __attribute__((naked)) __actual_isr(void) +{ + asm __volatile__ ( + "stmdb sp!, {r0-r12, lr};" + "mov fp, sp;" + ); + + current_fiq_c_isr(); + + asm __volatile__ ( + "ldmia sp!, {r0-r12, lr};" + "subs pc, lr, #4;" + ); +} + +void set_fiq_c_handler(void (*isr)(void)) +{ + struct pt_regs regs; + + memset(®s, 0, sizeof(regs)); + regs.ARM_r8 = (unsigned long) __actual_isr; + regs.ARM_sp = 0xffff001c + FIQ_C_ISR_STACK_SIZE; + + set_fiq_handler(__jump_to_isr, 4); + + current_fiq_c_isr = isr; + + set_fiq_regs(®s); +} +/* -------- FIQ handler in C ---------*/ + int claim_fiq(struct fiq_handler *f) { int ret = 0; diff --git a/arch/arm/mach-s3c2410/include/mach/gpio.h b/arch/arm/mach-s3c2410/include/mach/gpio.h index 15f0b3e7ce6..0b53cad80fe 100644 --- a/arch/arm/mach-s3c2410/include/mach/gpio.h +++ b/arch/arm/mach-s3c2410/include/mach/gpio.h @@ -20,10 +20,11 @@ * devices that need GPIO. */ -#define ARCH_NR_GPIOS (256 + CONFIG_S3C24XX_GPIO_EXTRA) +#define ARCH_NR_GPIOS (512 + CONFIG_S3C24XX_GPIO_EXTRA) #include <asm-generic/gpio.h> #include <mach/gpio-nrs.h> #include <mach/gpio-fns.h> +#include <mach/regs-gpioj.h> -#define S3C_GPIO_END (S3C2410_GPIO_BANKH + 32) +#define S3C_GPIO_END (S3C2440_GPIO_BANKJ + 32) diff --git a/arch/arm/mach-s3c2410/include/mach/irqs.h b/arch/arm/mach-s3c2410/include/mach/irqs.h index 6c12c6312ad..6986d02fd4d 100644 --- a/arch/arm/mach-s3c2410/include/mach/irqs.h +++ b/arch/arm/mach-s3c2410/include/mach/irqs.h @@ -153,9 +153,9 @@ #define IRQ_S3C2443_AC97 S3C2410_IRQSUB(28) #ifdef CONFIG_CPU_S3C2443 -#define NR_IRQS (IRQ_S3C2443_AC97+1) +#define S3C2410_NR_INTERNAL_IRQS (IRQ_S3C2443_AC97+1) #else -#define NR_IRQS (IRQ_S3C2440_AC97+1) +#define S3C2410_NR_INTERNAL_IRQS (IRQ_S3C2440_AC97+1) #endif /* compatibility define. */ @@ -173,4 +173,22 @@ /* Our FIQs are routable from IRQ_EINT0 to IRQ_ADCPARENT */ #define FIQ_START IRQ_EINT0 +/* Board specific IRQs + * If your board needs a extra set of IRQ numbers add it to the list here. + * Make sure that the numbers are kept in descending, so if multiple boards are + * selected the maximum will be used and there are enough IRQ numbers available + * for each board. + */ + +#if defined(CONFIG_MACH_NEO1973_GTA02) +#define S3C2410_NR_BOARD_IRQS 9 +#else +#define S3C2410_NR_BOARD_IRQS 0 +#endif + +#define S3C2410_BOARD_IRQ_START S3C2410_NR_INTERNAL_IRQS +#define S3C2410_BOARD_IRQ_END (S3C2410_BOARD_IRQ_START + S3C2410_NR_BOARD_IRQS) + +#define NR_IRQS S3C2410_BOARD_IRQ_END + #endif /* __ASM_ARCH_IRQ_H */ diff --git a/arch/arm/plat-s3c/include/plat/nand.h b/arch/arm/plat-s3c/include/plat/nand.h index 18f958801e6..723ab032059 100644 --- a/arch/arm/plat-s3c/include/plat/nand.h +++ b/arch/arm/plat-s3c/include/plat/nand.h @@ -47,6 +47,7 @@ struct s3c2410_platform_nand { int twrph1; /* time for release CLE/ALE from nWE/nOE inactive */ unsigned int ignore_unset_ecc:1; + unsigned int software_ecc:1; /* force software ecc at runtime */ int nr_sets; struct s3c2410_nand_set *sets; diff --git a/arch/arm/plat-s3c/include/plat/uncompress.h b/arch/arm/plat-s3c/include/plat/uncompress.h index dc66a477f62..8e02073e307 100644 --- a/arch/arm/plat-s3c/include/plat/uncompress.h +++ b/arch/arm/plat-s3c/include/plat/uncompress.h @@ -37,6 +37,8 @@ static void arch_detect_cpu(void); /* how many bytes we allow into the FIFO at a time in FIFO mode */ #define FIFO_MAX (14) +#if CONFIG_S3C_LOWLEVEL_UART_PORT >= 0 + #define uart_base S3C_PA_UART + (S3C_UART_OFFSET * CONFIG_S3C_LOWLEVEL_UART_PORT) static __inline__ void @@ -86,10 +88,6 @@ static void putc(int ch) uart_wr(S3C2410_UTXH, ch); } -static inline void flush(void) -{ -} - #define __raw_writel(d, ad) \ do { \ *((volatile unsigned int __force *)(ad)) = (d); \ @@ -163,6 +161,19 @@ static inline void arch_enable_uart_fifo(void) #define arch_enable_uart_fifo() do { } while(0) #endif +#else +static inline void putc(int ch) +{ +} + +#define arch_enable_uart_fifo() do { } while(0) +#define arch_decomp_wdog_start() +#define arch_decomp_wdog() +#endif + +static inline void flush(void) +{ +} static void arch_decomp_setup(void) diff --git a/arch/arm/plat-s3c/pwm.c b/arch/arm/plat-s3c/pwm.c index 4fdc5b307fd..2a5275d98ca 100644 --- a/arch/arm/plat-s3c/pwm.c +++ b/arch/arm/plat-s3c/pwm.c @@ -21,6 +21,8 @@ #include <mach/irqs.h> #include <mach/map.h> +#include <mach/gpio.h> +#include <mach/regs-gpio.h> #include <plat/devs.h> #include <plat/regs-timer.h> @@ -40,6 +42,10 @@ struct pwm_device { unsigned char running; unsigned char use_count; unsigned char pwm_id; + + /* Used for saving state during suspend */ + unsigned long tcon; + unsigned char routed_to_gpio:1; }; #define pwm_dbg(_pwm, msg...) dev_dbg(&(_pwm)->pdev->dev, msg) @@ -130,6 +136,7 @@ void pwm_free(struct pwm_device *pwm) EXPORT_SYMBOL(pwm_free); +#define pwm_tcon_mask(pwm) (0xf << (pwm->tcon_base)) #define pwm_tcon_start(pwm) (1 << (pwm->tcon_base + 0)) #define pwm_tcon_invert(pwm) (1 << (pwm->tcon_base + 2)) #define pwm_tcon_autoreload(pwm) (1 << (pwm->tcon_base + 3)) @@ -379,10 +386,75 @@ static int s3c_pwm_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM + +static int s3c_pwm_suspend(struct device *dev) +{ + struct pwm_device *pwm = dev_get_drvdata(dev); + unsigned int gpio_cfg = s3c2410_gpio_getcfg(S3C2410_GPB(pwm->pwm_id)); + + /* During suspend the pwm registers are reseted. If the invert bit is not + * set the output of TOUTn is high when the pwm is not active. In order to + * prevent spurious high output on the gpio at resume time, we stop routing + * the pwm signal to the gpio until tcon has been restored. */ + if (gpio_cfg == S3C2410_GPIO_SFN2) { + s3c2410_gpio_cfgpin(S3C2410_GPB(pwm->pwm_id), S3C2410_GPIO_INPUT); + pwm->routed_to_gpio = 1; + } else { + pwm->routed_to_gpio = 0; + } + + pwm->tcon = __raw_readl(S3C2410_TCON); + + return 0; +} + +static int s3c_pwm_resume(struct device *dev) +{ + struct pwm_device *pwm = dev_get_drvdata(dev); + unsigned long flags; + unsigned long tcon; + int duty_ns, period_ns; + + duty_ns = pwm->duty_ns; + period_ns = pwm->period_ns; + pwm->duty_ns = -1; + pwm->period_ns = -1; + pwm_config(pwm, duty_ns, period_ns); + + local_irq_save(flags); + tcon = __raw_readl(S3C2410_TCON); + tcon = __raw_readl(S3C2410_TCON); + tcon &= ~pwm_tcon_mask(pwm); + tcon |= pwm->tcon; + __raw_writel(tcon, S3C2410_TCON); + local_irq_restore(flags); + + if (pwm->routed_to_gpio) + s3c2410_gpio_cfgpin(S3C2410_GPB(pwm->pwm_id), S3C2410_GPIO_SFN2); + + return 0; +} + +struct dev_pm_ops s3c_pwm_pm_ops = { + .suspend = s3c_pwm_suspend, + .resume = s3c_pwm_resume, + .freeze = s3c_pwm_suspend, + .thaw = s3c_pwm_resume, +}; + +#define S3C_PWM_PM_OPS (&s3c_pwm_pm_ops) + +#else +#define S3C_PWM_PM_OPS NULL +#endif + + static struct platform_driver s3c_pwm_driver = { .driver = { .name = "s3c24xx-pwm", .owner = THIS_MODULE, + .pm = S3C_PWM_PM_OPS, }, .probe = s3c_pwm_probe, .remove = __devexit_p(s3c_pwm_remove), diff --git a/arch/arm/plat-s3c24xx/gpiolib.c b/arch/arm/plat-s3c24xx/gpiolib.c index 6d7a961d326..c2dd900660b 100644 --- a/arch/arm/plat-s3c24xx/gpiolib.c +++ b/arch/arm/plat-s3c24xx/gpiolib.c @@ -26,6 +26,7 @@ #include <plat/pm.h> #include <mach/regs-gpio.h> +#include <mach/regs-gpioj.h> static int s3c24xx_gpiolib_banka_input(struct gpio_chip *chip, unsigned offset) { @@ -160,8 +161,16 @@ struct s3c_gpio_chip s3c24xx_gpios[] = { .label = "GPIOH", .ngpio = 11, }, - }, -}; + }, { + .base = S3C2440_GPJCON, + .pm = __gpio_pm(&s3c_gpio_pm_2bit), + .chip = { + .base = S3C2440_GPJ0, + .owner = THIS_MODULE, + .label = "GPIOJ", + .ngpio = 11, + }, + },}; static __init int s3c24xx_gpiolib_init(void) { diff --git a/arch/arm/plat-s3c24xx/include/plat/irq.h b/arch/arm/plat-s3c24xx/include/plat/irq.h index 69e1be8bec3..11a866466d8 100644 --- a/arch/arm/plat-s3c24xx/include/plat/irq.h +++ b/arch/arm/plat-s3c24xx/include/plat/irq.h @@ -12,6 +12,7 @@ #include <linux/io.h> +#include <mach/irqs.h> #include <mach/hardware.h> #include <mach/regs-irq.h> #include <mach/regs-gpio.h> @@ -31,8 +32,15 @@ s3c_irqsub_mask(unsigned int irqno, unsigned int parentbit, { unsigned long mask; unsigned long submask; +#ifdef CONFIG_S3C2440_C_FIQ + unsigned long flags; +#endif submask = __raw_readl(S3C2410_INTSUBMSK); +#ifdef CONFIG_S3C2440_C_FIQ + local_save_flags(flags); + local_fiq_disable(); +#endif mask = __raw_readl(S3C2410_INTMSK); submask |= (1UL << (irqno - IRQ_S3CUART_RX0)); @@ -45,6 +53,9 @@ s3c_irqsub_mask(unsigned int irqno, unsigned int parentbit, /* write back masks */ __raw_writel(submask, S3C2410_INTSUBMSK); +#ifdef CONFIG_S3C2440_C_FIQ + local_irq_restore(flags); +#endif } @@ -53,8 +64,15 @@ s3c_irqsub_unmask(unsigned int irqno, unsigned int parentbit) { unsigned long mask; unsigned long submask; +#ifdef CONFIG_S3C2440_C_FIQ + unsigned long flags; +#endif submask = __raw_readl(S3C2410_INTSUBMSK); +#ifdef CONFIG_S3C2440_C_FIQ + local_save_flags(flags); + local_fiq_disable(); +#endif mask = __raw_readl(S3C2410_INTMSK); submask &= ~(1UL << (irqno - IRQ_S3CUART_RX0)); @@ -63,6 +81,9 @@ s3c_irqsub_unmask(unsigned int irqno, unsigned int parentbit) /* write back masks */ __raw_writel(submask, S3C2410_INTSUBMSK); __raw_writel(mask, S3C2410_INTMSK); +#ifdef CONFIG_S3C2440_C_FIQ + local_irq_restore(flags); +#endif } diff --git a/arch/arm/plat-s3c24xx/irq.c b/arch/arm/plat-s3c24xx/irq.c index d02f5f02045..14aa528d985 100644 --- a/arch/arm/plat-s3c24xx/irq.c +++ b/arch/arm/plat-s3c24xx/irq.c @@ -28,6 +28,8 @@ #include <asm/mach/irq.h> #include <plat/regs-irqtype.h> +#include <mach/regs-irq.h> +#include <mach/regs-gpio.h> #include <plat/cpu.h> #include <plat/pm.h> @@ -37,12 +39,20 @@ static void s3c_irq_mask(unsigned int irqno) { unsigned long mask; - +#ifdef CONFIG_S3C2440_C_FIQ + unsigned long flags; +#endif irqno -= IRQ_EINT0; - +#ifdef CONFIG_S3C2440_C_FIQ + local_save_flags(flags); + local_fiq_disable(); +#endif mask = __raw_readl(S3C2410_INTMSK); mask |= 1UL << irqno; __raw_writel(mask, S3C2410_INTMSK); +#ifdef CONFIG_S3C2440_C_FIQ + local_irq_restore(flags); +#endif } static inline void @@ -59,9 +69,19 @@ s3c_irq_maskack(unsigned int irqno) { unsigned long bitval = 1UL << (irqno - IRQ_EINT0); unsigned long mask; +#ifdef CONFIG_S3C2440_C_FIQ + unsigned long flags; +#endif +#ifdef CONFIG_S3C2440_C_FIQ + local_save_flags(flags); + local_fiq_disable(); +#endif mask = __raw_readl(S3C2410_INTMSK); __raw_writel(mask|bitval, S3C2410_INTMSK); +#ifdef CONFIG_S3C2440_C_FIQ + local_irq_restore(flags); +#endif __raw_writel(bitval, S3C2410_SRCPND); __raw_writel(bitval, S3C2410_INTPND); @@ -72,15 +92,25 @@ static void s3c_irq_unmask(unsigned int irqno) { unsigned long mask; +#ifdef CONFIG_S3C2440_C_FIQ + unsigned long flags; +#endif if (irqno != IRQ_TIMER4 && irqno != IRQ_EINT8t23) irqdbf2("s3c_irq_unmask %d\n", irqno); irqno -= IRQ_EINT0; +#ifdef CONFIG_S3C2440_C_FIQ + local_save_flags(flags); + local_fiq_disable(); +#endif mask = __raw_readl(S3C2410_INTMSK); mask &= ~(1UL << irqno); __raw_writel(mask, S3C2410_INTMSK); +#ifdef CONFIG_S3C2440_C_FIQ + local_irq_restore(flags); +#endif } struct irq_chip s3c_irq_level_chip = { @@ -559,26 +589,26 @@ void __init s3c24xx_init_irq(void) last = 0; for (i = 0; i < 4; i++) { - pend = __raw_readl(S3C2410_INTPND); + pend = __raw_readl(S3C2410_SUBSRCPND); if (pend == 0 || pend == last) break; - __raw_writel(pend, S3C2410_SRCPND); - __raw_writel(pend, S3C2410_INTPND); - printk("irq: clearing pending status %08x\n", (int)pend); + printk("irq: clearing subpending status %08x\n", (int)pend); + __raw_writel(pend, S3C2410_SUBSRCPND); last = pend; } last = 0; for (i = 0; i < 4; i++) { - pend = __raw_readl(S3C2410_SUBSRCPND); + pend = __raw_readl(S3C2410_INTPND); if (pend == 0 || pend == last) break; - printk("irq: clearing subpending status %08x\n", (int)pend); - __raw_writel(pend, S3C2410_SUBSRCPND); + __raw_writel(pend, S3C2410_SRCPND); + __raw_writel(pend, S3C2410_INTPND); + printk("irq: clearing pending status %08x\n", (int)pend); last = pend; } |