From: M. R. B. <mr...@us...> - 2001-02-21 15:21:11
|
Update of /cvsroot/linuxdc/linux/arch/sh/kernel In directory usw-pr-cvs1:/tmp/cvs-serv1015/kernel Modified Files: Makefile Added Files: io_powervr2dc.c mach_dreamcast.c setup_powervr2dc.c Log Message: Added initial Dreamcast PowerVR2DC and hardware support --- NEW FILE --- /* * linux/arch/sh/kernel/io_powervr2dc.c * * I/O routines for the Dreamcast PowerVR2DC ASIC. * * Copyright (c) 2001 M. R. Brown <mr...@0x...> * */ /* * Until I get some info on bITmASTER's ISA/IDE interface to the DC's * expansion port, this file will remain unimplemented. Until then, * you won't get much use out of ISA code :) */ /* BTW, PCI is coming real soon :) */ /* I hope I don't get hanged for this ;) */ #include <linux/config.h> #include <linux/kernel.h> #include <linux/types.h> #include <asm/io.h> #include <asm/page.h> #include <asm/powervr2dc.h> /* unsigned char powervr2dc_inb(unsigned long port) { BUG(); return 0; } unsigned short powervr2dc_inw(unsigned long port) { BUG(); return 0; } unsigned int powervr2dc_inl(unsigned long port) { BUG(); return 0; } void powervr2dc_outb(unsigned char data, unsigned long port) { BUG(); } void powervr2dc_outw(unsigned short data, unsigned long port) { BUG(); } void powervr2dc_outl(unsigned int data, unsigned long port) { BUG(); } */ --- NEW FILE --- /* * linux/arch/sh/kernel/mach_dreamcast.c * * Copyright (c) 2001 M. R. Brown <mr...@0x...> * * Machine vector for the Sega Dreamcast. */ #include <linux/config.h> #include <linux/init.h> #include <asm/machvec.h> #include <asm/rtc.h> #include <asm/machvec_init.h> #include <asm/io.h> #include <asm/irq.h> /* * The Machine Vector */ struct sh_machine_vector mv_dreamcast __initmv = { mv_name: "Dreamcast", mv_nr_irqs: 160, /* PVR2DC_IRQ_BASE + PVR2DC_IRQ_NUM */ mv_inb: generic_inb, mv_inw: generic_inw, mv_inl: generic_inl, mv_outb: generic_outb, mv_outw: generic_outw, mv_outl: generic_outl, mv_inb_p: generic_inb_p, mv_inw_p: generic_inw_p, mv_inl_p: generic_inl_p, mv_outb_p: generic_outb_p, mv_outw_p: generic_outw_p, mv_outl_p: generic_outl_p, 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: powervr2dc_irq_demux, /* These are in setup_powervr2dc.c */ mv_rtc_gettimeofday: dc_aica_rtc_gettimeofday, mv_rtc_settimeofday: dc_aica_rtc_settimeofday, mv_hw_dreamcast: 1, }; ALIAS_MV(dreamcast) --- NEW FILE --- /* * linux/arch/sh/kernel/setup_powervr2dc.c * * Interrupt handling and misc. routines for the Dreamcast * PowerVR2DC ASIC. * * Copyright (c) 2001 M. R. Brown <mr...@0x...> * * This code is based on both setup_hd64465.c and setup_ec3104.c, by * Greg Banks and Philipp Rumpf, respectively. */ /* * This a preliminary PowerVR2DC interrupt handling mechanism. NOTE: This code * needs *MAJOR* testing. I need to make sure the "bitfield" semantics are correct, * and that powervr2dc_irq_demux() does what it's intended to. */ #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/time.h> #include <linux/types.h> #include <asm/io.h> #include <asm/irq.h> #include <asm/powervr2dc.h> #include <asm/aica.h> /* * The AICA RTC routines are in here because there isn't really a proper * place for them, based on what they're needed for (they serve two purposes, * RTC manipulation and timer calibration). There isn't even enough for a * full-fledged "misc" driver, and there doesn't need to be. */ /* * The AICA RTC epoch is January 1st, 1950, so we need to add/subtract this * value to convert from/to the Unix epoch. */ #define TWENTY_YEARS 631152000 void dc_aica_rtc_gettimeofday(struct timeval *tv) { u32 secs; u32 val; do { secs = ((ctrl_inl(AICA_REG_RTCH) & 0xffff) << 16) | (ctrl_inl(AICA_REG_RTCL) & 0xffff); val = ((ctrl_inl(AICA_REG_RTCH) & 0xffff) << 16) | (ctrl_inl(AICA_REG_RTCL) & 0xffff); } while (secs != val); tv->tv_sec = secs - TWENTY_YEARS; tv->tv_usec = 0; } int dc_aica_rtc_settimeofday(const struct timeval *tv) { u32 val; u32 secs = tv->tv_sec + TWENTY_YEARS; do { ctrl_outl((secs >> 16) & 0xffff, AICA_REG_RTCH); ctrl_outl(secs & 0xffff, AICA_REG_RTCL); val = ((ctrl_inl(AICA_REG_RTCH) & 0xffff) << 16) | (ctrl_inl(AICA_REG_RTCL) & 0xffff); } while (secs != val); return 0; } /* PowerVR2DC Interrupt-handling */ /* Hmm, I treat the IPR/IMR as a giant bitfield (96 bits) as each interrupt is * masked/acknowledged by a single bit position. */ static inline void disable_powervr2dc_irq(unsigned int irq) { clear_bit(irq, (volatile unsigned long *)PVR2DC_REG_IMRA); } static inline void enable_powervr2dc_irq(unsigned int irq) { set_bit(irq, (volatile unsigned long *)PVR2DC_REG_IMRA); } /* * To acknowledge an interrupt, we write back the mask onto the IPR* * registers. You can actually acknowledge as many or as few interrupts * as you like, but we only handle one request here. */ static void ack_powervr2dc_irq(unsigned int irq) { set_bit(irq, (volatile unsigned long *)PVR2DC_REG_IPRA); } /* XXX: These next three were done pretty blindly */ static void end_powervr2dc_irq(unsigned int irq) { enable_powervr2dc_irq(irq); } static unsigned int startup_powervr2dc_irq(unsigned int irq) { enable_powervr2dc_irq(irq); return 0; } static void shutdown_powervr2dc_irq(unsigned int irq) { disable_powervr2dc_irq(irq); } static struct hw_interrupt_type powervr2dc_int = { typename: "PowerVR2DC", startup: startup_powervr2dc_irq, shutdown: shutdown_powervr2dc_irq, enable: enable_powervr2dc_irq, disable: disable_powervr2dc_irq, ack: ack_powervr2dc_irq, end: end_powervr2dc_irq, }; /* Just to satisfy my own curiosities :) */ static void powervr2dc_interrupt(int irq, void *dev_id, struct pt_regs *regs) { printk(KERN_INFO "PowerVR2DC: spurious interrupt, IPRA: %08x IPRB: %08x IPRC: %08x" "IMRA: %08x IMRB: %08x IMRC: %08x\n", ctrl_inl(PVR2DC_REG_IPRA), ctrl_inl(PVR2DC_REG_IPRB), ctrl_inl(PVR2DC_REG_IPRC), ctrl_inl(PVR2DC_REG_IMRA), ctrl_inl(PVR2DC_REG_IMRB), ctrl_inl(PVR2DC_REG_IMRC)); } static struct irqaction irq9 = { powervr2dc_interrupt, SA_INTERRUPT, 0, "PowerVR2DC", NULL, NULL }; /* * That handy bitfield again. To demux IRQ9, just find the first non-zero bit * to use as the source. There's only one problem: if you get multiple interrupt * sources per IRQ, how do you demux those? */ /* XXX: Need to make sure all int. sources are being handled per IRQ! Is this * handled in irq.c:handle_IRQ_event()/do_IRQ() ? */ int powervr2dc_irq_demux(int irq) { if (irq == PVR2DC_IRQ) { u32 i = 0, bit; u32 ipr, req; /* Brute force scan each int. pending register. * This also needs fixing. */ for (ipr = PVR2DC_REG_IPRA ; ipr <= PVR2DC_REG_IPRC ; ipr += 4) { req = ctrl_inl(ipr); for (bit = 1 ; i < PVR2DC_IRQ_NUM ; bit <<= 1, i++) if (req & bit) goto found; } found: if (i < PVR2DC_IRQ_NUM) irq = PVR2DC_IRQ_BASE + i; } return irq; } int __init setup_powervr2dc(void) { int i; int ver_major, ver_minor; if (!MACH_DREAMCAST) { printk("!MACH_DREAMCAST\n"); return 0; } for (i = PVR2DC_IRQ_BASE; i < PVR2DC_IRQ_BASE + PVR2DC_IRQ_NUM; i++) irq_desc[i].handler = &powervr2dc_int; ver_major = (ctrl_inl(PVR2DC_REG_VERSION) >> 4) & 0x0f; ver_minor = ctrl_inl(PVR2DC_REG_VERSION) & 0x0f; /* XXX: What other initialization needs to be done (besides interrupts)? */ printk("Initializing PowerVR2DC v%d.%d at 0x%08x (interrupts on IRQ %d, %d total)\n", ver_major, ver_minor, PVR2DC_BASE, PVR2DC_IRQ, PVR2DC_IRQ_NUM); setup_irq(PVR2DC_IRQ, &irq9); /* Mask all interrupts. */ ctrl_outl(0, PVR2DC_REG_IMRA); ctrl_outl(0, PVR2DC_REG_IMRB); ctrl_outl(0, PVR2DC_REG_IMRC); /* Acknowledge any previous interrupts */ ctrl_inl(PVR2DC_REG_IPRA); ctrl_outl(0xffffffff, PVR2DC_REG_IPRA); ctrl_inl(PVR2DC_REG_IPRB); ctrl_outl(0xffffffff, PVR2DC_REG_IPRB); ctrl_inl(PVR2DC_REG_IPRC); ctrl_outl(0xffffffff, PVR2DC_REG_IPRC); return 0; } module_init(setup_powervr2dc); Index: Makefile =================================================================== RCS file: /cvsroot/linuxdc/linux/arch/sh/kernel/Makefile,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -r1.3 -r1.4 *** Makefile 2001/02/09 16:27:14 1.3 --- Makefile 2001/02/21 15:22:11 1.4 *************** *** 53,57 **** obj-$(CONFIG_SH_DMIDA) += mach_dmida.o obj-$(CONFIG_SH_EC3104) += setup_ec3104.o io_ec3104.o mach_ec3104.o ! obj-$(CONFIG_SH_DREAMCAST) += mach_dc.o setup_dc.o io_dc.o ifeq ($(CONFIG_SH_GENERIC),y) --- 53,57 ---- obj-$(CONFIG_SH_DMIDA) += mach_dmida.o obj-$(CONFIG_SH_EC3104) += setup_ec3104.o io_ec3104.o mach_ec3104.o ! obj-$(CONFIG_SH_DREAMCAST) += mach_dreamcast.o setup_powervr2dc.o io_powervr2dc.o ifeq ($(CONFIG_SH_GENERIC),y) |