From: Philipp R. <pr...@pa...> - 2000-12-08 23:30:50
|
Hello, this patch adds support for some of the basic functionality of the EC3104 companion chip, which is used in the Compaq Aero 8000 wince laptop series (at least some of them, that is). This includes interrupt support, support for byte IO operations on the ISA bus, and support for the serial ports (using the generic driver). 2000-12-09 Philipp Rumpf <pr...@tu...> * arch/sh/kernel/mach_ec3104.c, arch/sh/kernel/io_ec3104.c, arch/sh/kernel/setup_ec3104.c, include/asm-sh/ec3104.h, include/asm-sh/io_ec3104.h, include/asm-sh/serial-ec3104.h: New files * arch/sh/config.in, arch/sh/kernel/Makefile, include/asm-sh/io.h, include/asm-sh/irq.h, include/asm-sh/machvec.h: Added support for the EC3104 companion chip. * include/asm-sh/serial.h [CONFIG_SH_EC3104]: Use alternate header file for EC3104. If anyone's interested, I'm running Linux on the Aero 8000 quite nicely, but some of the drivers are currently being cleaned up. In particular, I expect to release the following things shortly: - support for the aero 8000 keyboard controller - support for the ec3104 ps2 port (aero 8000 touch pad) - support for the epson 1355 lcd/crt controller - a boot loader Philipp Rumpf diff -urNx CVS linuxsh/kernel/arch/sh/config.in linux-aero/arch/sh/config.in --- linuxsh/kernel/arch/sh/config.in Fri Dec 8 13:30:10 2000 +++ linux-aero/arch/sh/config.in Fri Dec 8 13:11:46 2000 @@ -33,6 +33,7 @@ HP690 CONFIG_SH_HP690 \ CqREEK CONFIG_SH_CQREEK \ FOOBAR CONFIG_SH_FOOBAR \ + EC3104 CONFIG_SH_EC3104 \ BareCPU CONFIG_SH_UNKNOWN" Generic if [ "$CONFIG_SH_HP620" = "y" -o "$CONFIG_SH_HP680" = "y" -o \ diff -urNx CVS linuxsh/kernel/arch/sh/kernel/Makefile linux-aero/arch/sh/kernel/Makefile --- linuxsh/kernel/arch/sh/kernel/Makefile Fri Dec 8 13:30:11 2000 +++ linux-aero/arch/sh/kernel/Makefile Fri Dec 8 13:38:29 2000 @@ -61,6 +61,10 @@ O_OBJS += setup_hd64465.o io_hd64465.o hd64465_gpio.o endif +ifneq ($(CONFIG_SH_EC3104),) +O_OBJS += mach_ec3104.o setup_ec3104.o io_ec3104.o io_generic.o +endif + ifdef CONFIG_SH_STANDARD_BIOS O_OBJS += sh_bios.o endif diff -urNx CVS linuxsh/kernel/arch/sh/kernel/io_ec3104.c linux-aero/arch/sh/kernel/io_ec3104.c --- linuxsh/kernel/arch/sh/kernel/io_ec3104.c Wed Dec 31 16:00:00 1969 +++ linux-aero/arch/sh/kernel/io_ec3104.c Fri Dec 8 14:30:24 2000 @@ -0,0 +1,83 @@ +/* + * linux/arch/sh/kernel/io_ec3104.c + * EC3104 companion chip support + * + * Copyright (C) 2000 Philipp Rumpf <pr...@tu...> + * + */ +/* EC3104 note: + * This code was written without any documentation about the EC3104 chip. While + * I hope I got most of the basic functionality right, the register names I use + * are most likely completely different from those in the chip documentation. + * + * If you have any further information about the EC3104, please tell me + * (pr...@tu...). + */ + + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <asm/io.h> +#include <asm/page.h> +#include <asm/ec3104.h> + +/* + * EC3104 has a real ISA bus which we redirect low port accesses to (the + * actual device on mine is a ESS 1868, and I don't want to hack the driver + * more than strictly necessary). I am not going to duplicate the + * hard coding of PC addresses (for the 16550s aso) here though; it's just + * too ugly. + */ + +#define low_port(port) ((port) < 0x10000) + +static inline unsigned long port2addr(unsigned long port) +{ + switch(port >> 16) { + case 0: + return EC3104_ISA_BASE + port * 2; + + /* XXX hack. it's unclear what to do about the serial ports */ + case 1: + return EC3104_BASE + (port&0xffff) * 4; + + default: + /* XXX PCMCIA */ + return 0; + } +} + +unsigned char ec3104_inb(unsigned long port) +{ + u8 ret; + + ret = *(volatile u8 *)port2addr(port); + + return ret; +} + +unsigned short ec3104_inw(unsigned long port) +{ + BUG(); +} + +unsigned long ec3104_inl(unsigned long port) +{ + BUG(); +} + +void ec3104_outb(unsigned char data, unsigned long port) +{ + *(volatile u8 *)port2addr(port) = data; +} + +void ec3104_outw(unsigned short data, unsigned long port) +{ + BUG(); +} + +void ec3104_outl(unsigned long data, unsigned long port) +{ + BUG(); +} diff -urNx CVS linuxsh/kernel/arch/sh/kernel/mach_ec3104.c linux-aero/arch/sh/kernel/mach_ec3104.c --- linuxsh/kernel/arch/sh/kernel/mach_ec3104.c Wed Dec 31 16:00:00 1969 +++ linux-aero/arch/sh/kernel/mach_ec3104.c Fri Dec 8 14:30:18 2000 @@ -0,0 +1,66 @@ +/* + * linux/arch/sh/kernel/mach_ec3104.c + * EC3104 companion chip support + * + * Copyright (C) 2000 Philipp Rumpf <pr...@tu...> + * + */ +/* EC3104 note: + * This code was written without any documentation about the EC3104 chip. While + * I hope I got most of the basic functionality right, the register names I use + * are most likely completely different from those in the chip documentation. + * + * If you have any further information about the EC3104, please tell me + * (pr...@tu...). + */ + +#include <linux/init.h> + +#include <asm/machvec.h> +#include <asm/machvec_init.h> + +#include <asm/io.h> +#include <asm/irq.h> + +/* + * The Machine Vector + */ + +struct sh_machine_vector mv_ec3104 __initmv = { + mv_name: "EC3104", + + mv_nr_irqs: 96, + + mv_inb: ec3104_inb, + mv_inw: ec3104_inw, + mv_inl: ec3104_inl, + mv_outb: ec3104_outb, + mv_outw: ec3104_outw, + mv_outl: ec3104_outl, + + mv_inb_p: generic_inb_p, + mv_inw_p: generic_inw, + mv_inl_p: generic_inl, + mv_outb_p: generic_outb_p, + mv_outw_p: generic_outw, + mv_outl_p: generic_outl, + + mv_insb: generic_insb, + mv_insw: generic_insw, + mv_insl: generic_insl, + mv_outsb: generic_outsb, + mv_outsw: generic_outsw, + mv_outsl: generic_outsl, + + mv_readb: generic_readb, + mv_readw: generic_readw, + mv_readl: generic_readl, + mv_writeb: generic_writeb, + mv_writew: generic_writew, + mv_writel: generic_writel, + + mv_irq_demux: ec3104_irq_demux, + +}; + +ALIAS_MV(ec3104) diff -urNx CVS linuxsh/kernel/arch/sh/kernel/setup_ec3104.c linux-aero/arch/sh/kernel/setup_ec3104.c --- linuxsh/kernel/arch/sh/kernel/setup_ec3104.c Wed Dec 31 16:00:00 1969 +++ linux-aero/arch/sh/kernel/setup_ec3104.c Fri Dec 8 14:29:51 2000 @@ -0,0 +1,165 @@ +/* + * linux/arch/sh/kernel/setup_ec3104.c + * EC3104 companion chip support + * + * Copyright (C) 2000 Philipp Rumpf <pr...@tu...> + * + */ +/* EC3104 note: + * This code was written without any documentation about the EC3104 chip. While + * I hope I got most of the basic functionality right, the register names I use + * are most likely completely different from those in the chip documentation. + * + * If you have any further information about the EC3104, please tell me + * (pr...@tu...). + */ + +#include <linux/config.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/types.h> + +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/ec3104.h> + +/* I used the register names from another interrupt controller I worked with, + * since it seems to be identical to the ec3104 except that all bits are + * inverted: + * + * IRR: Interrupt Request Register (pending and enabled interrupts) + * IMR: Interrupt Mask Register (which interrupts are enabled) + * IPR: Interrupt Pending Register (pending interrupts, even disabled ones) + * + * 0 bits mean pending or enabled, 1 bits mean not pending or disabled. all + * IRQs seem to be level-triggered. + */ + +#define EC3104_IRR (EC3104_BASE + 0x1000) +#define EC3104_IMR (EC3104_BASE + 0x1004) +#define EC3104_IPR (EC3104_BASE + 0x1008) + +#define ctrl_readl(addr) (*(volatile u32 *)(addr)) +#define ctrl_writel(data,addr) (*(volatile u32 *)(addr) = (data)) +#define ctrl_readb(addr) (*(volatile u8 *)(addr)) + +static inline u32 ec3104_irq2mask(unsigned int irq) +{ + return (1 << (irq - EC3104_IRQBASE)); +} + +static inline void mask_ec3104_irq(unsigned int irq) +{ + u32 mask; + + mask = ctrl_readl(EC3104_IMR); + + mask |= ec3104_irq2mask(irq); + + ctrl_writel(mask, EC3104_IMR); +} + +static inline void unmask_ec3104_irq(unsigned int irq) +{ + u32 mask; + + mask = ctrl_readl(EC3104_IMR); + + mask &= ~ec3104_irq2mask(irq); + + ctrl_writel(mask, EC3104_IMR); +} + +static void disable_ec3104_irq(unsigned int irq) +{ + mask_ec3104_irq(irq); +} + +static void enable_ec3104_irq(unsigned int irq) +{ + unmask_ec3104_irq(irq); +} + +static void mask_and_ack_ec3104_irq(unsigned int irq) +{ + mask_ec3104_irq(irq); +} + +static void end_ec3104_irq(unsigned int irq) +{ + unmask_ec3104_irq(irq); +} + +static unsigned int startup_ec3104_irq(unsigned int irq) +{ + unmask_ec3104_irq(irq); + + return 0; +} + +static void shutdown_ec3104_irq(unsigned int irq) +{ + mask_ec3104_irq(irq); + +} + +static struct hw_interrupt_type ec3104_int = { + typename: "EC3104", + enable: enable_ec3104_irq, + disable: disable_ec3104_irq, + ack: mask_and_ack_ec3104_irq, + end: end_ec3104_irq, + startup: startup_ec3104_irq, + shutdown: shutdown_ec3104_irq, +}; + +/* Yuck. the _demux API is ugly */ +int ec3104_irq_demux(int irq) +{ + if (irq == EC3104_IRQ) { + unsigned int mask; + + mask = ctrl_readl(EC3104_IRR); + + if (mask == 0xffffffff) + return EC3104_IRQ; + else + return EC3104_IRQBASE + ffz(mask); + } + + return irq; +} + +int __init setup_ec3104(void) +{ + char str[8]; + int i; + + if (!MACH_EC3104) + printk("!MACH_EC3104\n"); + + if (0) + return 0; + + for (i=0; i<8; i++) + str[i] = ctrl_readb(EC3104_BASE + i); + + for (i = EC3104_IRQBASE; i < EC3104_IRQBASE + 32; i++) + irq_desc[i].handler = &ec3104_int; + + printk("initializing EC3104 \"%.8s\" at %08x, IRQ %d, IRQ base %d\n", + str, EC3104_BASE, EC3104_IRQ, EC3104_IRQBASE); + + + /* mask all interrupts. this should have been done by the boot + * loader for us but we want to be sure ... */ + ctrl_writel(0xffffffff, EC3104_IMR); + + return 0; +} + +module_init(setup_ec3104); diff -urNx CVS linuxsh/kernel/include/asm-sh/ec3104.h linux-aero/include/asm-sh/ec3104.h --- linuxsh/kernel/include/asm-sh/ec3104.h Wed Dec 31 16:00:00 1969 +++ linux-aero/include/asm-sh/ec3104.h Fri Dec 8 13:11:46 2000 @@ -0,0 +1,43 @@ +#ifndef __ASM_EC3104_H +#define __ASM_EC3104_H + + +/* + * Most of the register set is at 0xb0ec0000 - 0xb0ecffff. + * + * as far as I've figured it out the register map is: + * 0xb0ec0000 - id string + * 0xb0ec0XXX - power management + * 0xb0ec1XXX - interrupt control + * 0xb0ec3XXX - ps2 port (touch pad on aero 8000) + * 0xb0ec6XXX - i2c + * 0xb0ec7000 - first serial port (proprietary connector on aero 8000) + * 0xb0ec8000 - second serial port + * 0xb0ec9000 - third serial port + * 0xb0eca000 - fourth serial port (keyboard controller on aero 8000) + * 0xb0eccXXX - GPIO + * 0xb0ecdXXX - GPIO + */ + +#define EC3104_BASE 0xb0ec0000 + +#define EC3104_SER4_DATA (EC3104_BASE+0xa000) +#define EC3104_SER4_IIR (EC3104_BASE+0xa008) +#define EC3104_SER4_MCR (EC3104_BASE+0xa010) +#define EC3104_SER4_LSR (EC3104_BASE+0xa014) +#define EC3104_SER4_MSR (EC3104_BASE+0xa018) + +/* + * our ISA bus. this seems to be real ISA. + */ +#define EC3104_ISA_BASE 0xa5000000 + +#define EC3104_IRQ 11 +#define EC3104_IRQBASE 64 + +#define EC3104_IRQ_SER1 EC3104_IRQBASE + 7 +#define EC3104_IRQ_SER2 EC3104_IRQBASE + 8 +#define EC3104_IRQ_SER3 EC3104_IRQBASE + 9 +#define EC3104_IRQ_SER4 EC3104_IRQBASE + 10 + +#endif /* __ASM_EC3104_H */ diff -urNx CVS linuxsh/kernel/include/asm-sh/io.h linux-aero/include/asm-sh/io.h --- linuxsh/kernel/include/asm-sh/io.h Fri Dec 8 13:32:56 2000 +++ linux-aero/include/asm-sh/io.h Fri Dec 8 14:38:23 2000 @@ -115,6 +115,8 @@ # include <asm/io_se.h> # elif defined(CONFIG_SH_FOOBAR) # include <asm/io_hd64465.h> +# elif defined(CONFIG_SH_EC3104) +# include <asm/io_ec3104.h> # elif defined(CONFIG_SH_UNKNOWN) # include <asm/io_unknown.h> # else diff -urNx CVS linuxsh/kernel/include/asm-sh/io_ec3104.h linux-aero/include/asm-sh/io_ec3104.h --- linuxsh/kernel/include/asm-sh/io_ec3104.h Wed Dec 31 16:00:00 1969 +++ linux-aero/include/asm-sh/io_ec3104.h Fri Dec 8 14:38:23 2000 @@ -0,0 +1,54 @@ +#ifndef _ASM_SH_IO_EC3104_H +#define _ASM_SH_IO_EC3104_H + +#include <asm/io_generic.h> +#include <linux/types.h> + +extern unsigned char ec3104_inb(unsigned long port); +extern unsigned short ec3104_inw(unsigned long port); +extern unsigned long ec3104_inl(unsigned long port); + +extern void ec3104_outb(unsigned char value, unsigned long port); +extern void ec3104_outw(unsigned short value, unsigned long port); +extern void ec3104_outl(unsigned long value, unsigned long port); + +extern int ec3104_irq_demux(int irq); + +#ifdef __WANT_IO_DEF + +# define __inb ec3104_inb +# define __inw ec3104_inw +# define __inl ec3104_inl +# define __outb ec3104_outb +# define __outw ec3104_outw +# define __outl ec3104_outl + +# define __inb_p ec3104_inb +# define __inw_p ec3104_inw +# define __inl_p ec3104_inl +# define __outb_p ec3104_outb +# define __outw_p ec3104_outw +# define __outl_p ec3104_outl + +# define __insb generic_insb +# define __insw generic_insw +# define __insl generic_insl +# define __outsb generic_outsb +# define __outsw generic_outsw +# define __outsl generic_outsl + +# define __readb generic_readb +# define __readw generic_readw +# define __readl generic_readl +# define __writeb generic_writeb +# define __writew generic_writew +# define __writel generic_writel + +# define __isa_port2addr generic_isa_port2addr +# define __ioremap generic_ioremap +# define __ioremap_nocache generic_ioremap_nocache +# define __iounmap generic_iounmap + +#endif + +#endif /* _ASM_SH_IO_EC3104_H */ diff -urNx CVS linuxsh/kernel/include/asm-sh/irq.h linux-aero/include/asm-sh/irq.h --- linuxsh/kernel/include/asm-sh/irq.h Fri Dec 8 13:32:57 2000 +++ linux-aero/include/asm-sh/irq.h Fri Dec 8 14:38:30 2000 @@ -85,6 +85,8 @@ # if defined(__SH4__) # ifdef CONFIG_HD64465 # define NR_IRQS 80 /* HD64465_IRQBASE+16, see hd64465.h */ +# elif defined (CONFIG_SH_EC3104) +# define NR_IRQS 96 # else /* * 48 = 32+16 @@ -208,6 +210,11 @@ extern int hd64465_irq_demux(int irq); #define irq_demux(irq) hd64465_irq_demux(irq) + +#elif defined(CONFIG_SH_EC3104) + +extern int ec3104_irq_demux(int irq); +#define irq_demux ec3104_irq_demux #else diff -urNx CVS linuxsh/kernel/include/asm-sh/machvec.h linux-aero/include/asm-sh/machvec.h --- linuxsh/kernel/include/asm-sh/machvec.h Fri Dec 8 13:32:58 2000 +++ linux-aero/include/asm-sh/machvec.h Fri Dec 8 14:38:23 2000 @@ -117,6 +117,11 @@ # else # define MACH_HD64465 0 # endif +# ifdef CONFIG_SH_EC3104 +# define MACH_EC3104 1 +# else +# define MACH_EC3104 0 +# endif #endif #endif /* _ASM_SH_MACHVEC_H */ diff -urNx CVS linuxsh/kernel/include/asm-sh/serial-ec3104.h linux-aero/include/asm-sh/serial-ec3104.h --- linuxsh/kernel/include/asm-sh/serial-ec3104.h Wed Dec 31 16:00:00 1969 +++ linux-aero/include/asm-sh/serial-ec3104.h Fri Dec 8 13:11:46 2000 @@ -0,0 +1,24 @@ +#include <asm/ec3104.h> +/* Naturally we don't know the exact value but 115200 baud has a divisor + * of 9 and 19200 baud has a divisor of 52, so this seems like a good + * guess. */ +#define BASE_BAUD (16800000 / 16) + +#define RS_TABLE_SIZE 3 + +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST) + +/* there is a fourth serial port with the expected values as well, but + * it's got the keyboard controller behind it so we can't really use it + * (without moving the keyboard driver to userspace, which doesn't sound + * like a very good idea) */ +#define STD_SERIAL_PORT_DEFNS \ + /* UART CLK PORT IRQ FLAGS */ \ + { 0, BASE_BAUD, 0x11C00, EC3104_IRQBASE+7, STD_COM_FLAGS }, /* ttyS0 */ \ + { 0, BASE_BAUD, 0x12000, EC3104_IRQBASE+8, STD_COM_FLAGS }, /* ttyS1 */ \ + { 0, BASE_BAUD, 0x12400, EC3104_IRQBASE+9, STD_COM_FLAGS }, /* ttyS2 */ + +#define SERIAL_PORT_DFNS STD_SERIAL_PORT_DEFNS + +/* XXX: This should be moved ino irq.h */ +#define irq_cannonicalize(x) (x) diff -urNx CVS linuxsh/kernel/include/asm-sh/serial.h linux-aero/include/asm-sh/serial.h --- linuxsh/kernel/include/asm-sh/serial.h Fri Dec 8 13:32:58 2000 +++ linux-aero/include/asm-sh/serial.h Fri Dec 8 14:39:54 2000 @@ -7,6 +7,13 @@ #ifndef _ASM_SERIAL_H #define _ASM_SERIAL_H +#include <linux/config.h> +#include <linux/kernel.h> + +#ifdef CONFIG_SH_EC3104 +#include <asm/serial-ec3104.h> +#else + /* * This assumes you have a 1.8432 MHz clock for your UART. * @@ -30,4 +37,5 @@ /* XXX: This should be moved ino irq.h */ #define irq_cannonicalize(x) (x) +#endif #endif /* _ASM_SERIAL_H */ |