/* * linux/arch/arm/mach-sa1100/system3.c * * Copyright (C) 2001 Stefan Eletzhofer * * $Id: system3.c,v 1.1.6.1 2001/12/04 17:28:06 seletz Exp $ * * This file contains all PT Sytsem 3 tweaks. Based on original work from * Nicolas Pitre's assabet fixes * * 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. * * $Log: system3.c,v $ * Revision 1.1.6.1 2001/12/04 17:28:06 seletz * - merged from previous branch * * Revision 1.1.4.3 2001/12/04 15:16:31 seletz * - merged from linux_2_4_13_ac5_rmk2 * * Revision 1.1.4.2 2001/11/19 17:18:57 seletz * - more code cleanups * * Revision 1.1.4.1 2001/11/16 13:52:05 seletz * - PT Digital Board Support Code * * Revision 1.1.2.2 2001/11/05 16:46:18 seletz * - cleanups * * Revision 1.1.2.1 2001/10/15 16:00:43 seletz * - first revision working with new board * * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "generic.h" #include #define DEBUG 1 #ifdef DEBUG # define DPRINTK( x, args... ) printk( "%s: line %d: "x, __FUNCTION__, __LINE__, ## args ); #else # define DPRINTK( x, args... ) /* nix */ #endif /********************************************************************** * prototypes */ /* init funcs */ static int __init system3_init(void); static void __init system3_init_irq(void); static void __init system3_map_io(void); static u_int system3_get_mctrl(struct uart_port *port); static void system3_set_mctrl(struct uart_port *port, u_int mctrl); static void system3_uart_pm(struct uart_port *port, u_int state, u_int oldstate); static int sdram_notifier(struct notifier_block *nb, unsigned long event, void *data); static void system3_lcd_power(int on); static void system3_backlight_power(int on); /********************************************************************** * global data */ /********************************************************************** * static data */ static struct map_desc system3_io_desc[] __initdata = { /* virtual physical length type */ { 0xf3000000, PT_CPLD_BASE, 0x00100000, MT_DEVICE }, /* System Registers */ { 0xf4000000, PT_SA1111_BASE, 0x00100000, MT_DEVICE } /* SA-1111 */ }; static struct sa1100_port_fns system3_port_fns __initdata = { .set_mctrl = system3_set_mctrl, .get_mctrl = system3_get_mctrl, .pm = system3_uart_pm, }; static struct notifier_block system3_clkchg_block = { .notifier_call = sdram_notifier, }; /********************************************************************** * Static functions */ static void __init system3_map_io(void) { DPRINTK( "%s\n", "START" ); sa1100_map_io(); iotable_init(system3_io_desc, ARRAY_SIZE(system3_io_desc)); sa1100_register_uart_fns(&system3_port_fns); sa1100_register_uart(0, 1); /* com port */ sa1100_register_uart(1, 2); sa1100_register_uart(2, 3); /* radio module */ Ser1SDCR0 |= SDCR0_SUS; } /********************************************************************* * Install IRQ handler */ static void system3_irq_handler(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) { u_char irr; //DPRINTK( "irq=%d, desc=%p, regs=%p\n", irq, desc, regs ); while (1) { struct irqdesc *d; /* * Acknowledge the parent IRQ. */ desc->chip->ack(irq); /* * Read the interrupt reason register. Let's have all * active IRQ bits high. Note: there is a typo in the * Neponset user's guide for the SA1111 IRR level. */ //irr = PT_IRQSR & (PT_IRR_LAN | PT_IRR_SA1111); irr = PT_IRQSR & (PT_IRR_SA1111); /* SMC IRQ is low-active, so "switch" bit over */ //irr ^= (PT_IRR_LAN); //DPRINTK( "irr=0x%02x\n", irr ); if ((irr & (PT_IRR_LAN | PT_IRR_SA1111)) == 0) break; /* * Since there is no individual mask, we have to * mask the parent IRQ. This is safe, since we'll * recheck the register for any pending IRQs. */ if (irr & (PT_IRR_LAN)) { desc->chip->mask(irq); if (irr & PT_IRR_LAN) { //DPRINTK( "SMC9196, irq=%d\n", IRQ_SYSTEM3_SMC9196 ); d = irq_desc + IRQ_SYSTEM3_SMC9196; d->handle(IRQ_SYSTEM3_SMC9196, d, regs); } #if 0 /* no SSP yet on system 4 */ if (irr & IRR_USAR) { d = irq_desc + IRQ_NEPONSET_USAR; d->handle(IRQ_NEPONSET_USAR, d, regs); } #endif desc->chip->unmask(irq); } if (irr & PT_IRR_SA1111) { //DPRINTK( "SA1111, irq=%d\n", IRQ_SYSTEM3_SA1111 ); d = irq_desc + IRQ_SYSTEM3_SA1111; d->handle(IRQ_SYSTEM3_SA1111, d, regs); } } } static void __init system3_init_irq(void) { /* * Install handler for GPIO25. */ set_irq_type(IRQ_GPIO25, IRQT_RISING); set_irq_chained_handler(IRQ_GPIO25, system3_irq_handler); /* * install eth irq */ set_irq_handler(IRQ_SYSTEM3_SMC9196, do_simple_IRQ); set_irq_flags(IRQ_SYSTEM3_SMC9196, IRQF_VALID | IRQF_PROBE); } /********************************************************************** * On system 3 limit cpu frequency to 206 Mhz */ static int sdram_notifier(struct notifier_block *nb, unsigned long event, void *data) { struct cpufreq_policy *policy = data; switch (event) { case CPUFREQ_ADJUST: case CPUFREQ_INCOMPATIBLE: cpufreq_verify_within_limits(policy, 147500, 206000); break; case CPUFREQ_NOTIFY: if ((policy->min < 147500) || (policy->max > 206000)) panic("cpufreq failed to limit the speed\n"); break; } return 0; } /** * system3_uart_pm - powermgmt callback function for system 3 UART * @port: uart port structure * @state: pm state * @oldstate: old pm state * */ static void system3_uart_pm(struct uart_port *port, u_int state, u_int oldstate) { /* TODO: switch on/off uart in powersave mode */ } /* * Note! this can be called from IRQ context. * FIXME: Handle PT Digital Board CTRL regs irq-safe. * * NB: system3 uses COM_RTS and COM_DTR for both UART1 (com port) * and UART3 (radio module). We only handle them for UART1 here. */ static void system3_set_mctrl(struct uart_port *port, u_int mctrl) { if (port->mapbase == _Ser1UTCR0) { u_int set = 0, clear = 0; if (mctrl & TIOCM_RTS) set |= PT_CTRL2_RS1_RTS; else clear |= PT_CTRL2_RS1_RTS; if (mctrl & TIOCM_DTR) set |= PT_CTRL2_RS1_DTR; else clear |= PT_CTRL2_RS1_DTR; PTCTRL2_clear(clear); PTCTRL2_set(set); } } static u_int system3_get_mctrl(struct uart_port *port) { u_int ret = 0; u_int irqsr = PT_IRQSR; /* need 2 reads to read current value */ irqsr = PT_IRQSR; /* TODO: check IRQ source register for modem/com status lines and set them correctly. */ ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR; return ret; } /** * system3_lcd_backlight_on - switch system 3 lcd backlight on * */ int system3_lcd_backlight_on( void ) { PTCTRL0_set( PT_CTRL0_LCD_BL ); return 0; } /** * system3_lcd_backlight_off - switch system 3 lcd backlight off * */ static void system3_lcd_backlight_off(void) { PTCTRL0_clear( PT_CTRL0_LCD_BL ); } /** * system3_lcd_on - switch system 3 lcd on * */ static void system3_lcd_on(void) { DPRINTK( "%s\n", "START" ); PTCTRL0_set( PT_CTRL0_LCD_EN ); /* brightness / contrast */ sa1111_enable_device(SKPCR_PWMCLKEN); PB_DDR = 0xFFFFFFFF; SKPEN0 = 1; SKPEN1 = 1; } /** * system3_lcd_off - switch system 3 lcd off * */ static void system3_lcd_off(void) { DPRINTK( "%s\n", "START" ); PTCTRL0_clear( PT_CTRL0_LCD_EN ); SKPEN0 = 0; SKPEN1 = 0; sa1111_disable_device(SKPCR_PWMCLKEN); } /** * system3_lcd_contrast - set system 3 contrast * @value: the new contrast * */ static void system3_lcd_contrast(unsigned char value) { DPRINTK( "value=0x%02x\n", value ); SYS3LCDCONTR = value; } /** * system3_lcd_brightness - set system 3 brightness * @value: the new brightness * */ static void system3_lcd_brightness(unsigned char value) { DPRINTK( "value=0x%02x\n", value ); SYS3LCDBRIGHT = value; } static void system3_lcd_power(int on) { if (on) { system3_lcd_on(); } else { system3_lcd_off(); } } static void system3_backlight_power(int on) { if (on) { system3_lcd_backlight_on(); system3_lcd_contrast(0x95); system3_lcd_brightness(240); } else { system3_lcd_backlight_off(); } } static struct resource sa1111_resources[] = { [0] = { .start = PT_SA1111_BASE, .end = PT_SA1111_BASE + 0x00001fff, .flags = IORESOURCE_MEM, }, [1] = { .start = IRQ_SYSTEM3_SA1111, .end = IRQ_SYSTEM3_SA1111, .flags = IORESOURCE_IRQ, }, }; static u64 sa1111_dmamask = 0xffffffffUL; static struct platform_device sa1111_device = { .name = "sa1111", .id = 0, .dev = { .dma_mask = &sa1111_dmamask, .coherent_dma_mask = 0xffffffff, }, .num_resources = ARRAY_SIZE(sa1111_resources), .resource = sa1111_resources, }; static struct platform_device *devices[] __initdata = { &sa1111_device, }; static int __init system3_init(void) { int ret = 0; DPRINTK( "%s\n", "START" ); if ( !machine_is_pt_system3() ) { ret = -EINVAL; goto DONE; } sa1100fb_lcd_power = system3_lcd_power; sa1100fb_backlight_power = system3_backlight_power; /* init control register */ PT_CTRL0 = PT_CTRL0_INIT; PT_CTRL1 = 0x02; PT_CTRL2 = 0x00; DPRINTK( "CTRL[0]=0x%02x\n", PT_CTRL0 ); DPRINTK( "CTRL[1]=0x%02x\n", PT_CTRL1 ); DPRINTK( "CTRL[2]=0x%02x\n", PT_CTRL2 ); /* * Ensure that the memory bus request/grant signals are setup, * and the grant is held in its inactive state. */ sa1110_mb_disable(); system3_init_irq(); /* * Probe for a SA1111. */ ret = platform_add_devices(devices, ARRAY_SIZE(devices)); if (ret < 0) { printk( KERN_WARNING"PT Digital Board: no SA1111 found!\n" ); goto DONE; } #ifdef CONFIG_CPU_FREQ ret = cpufreq_register_notifier(&system3_clkchg_block); if ( ret != 0 ) { printk( KERN_WARNING"PT Digital Board: could not register clock scale callback\n" ); goto DONE; } #endif ret = 0; DONE: DPRINTK( "ret=%d\n", ret ); return ret; } /********************************************************************** * Exported Functions */ /********************************************************************** * kernel magic macros */ arch_initcall(system3_init); MACHINE_START(PT_SYSTEM3, "PT System 3") BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000) BOOT_PARAMS(0xc0000100) MAPIO(system3_map_io) INITIRQ(sa1100_init_irq) INITTIME(sa1100_init_time) MACHINE_END