From: M. R. B. <mr...@us...> - 2001-03-11 11:08:42
|
Update of /cvsroot/linuxdc/linux/arch/sh/kernel In directory usw-pr-cvs1:/tmp/cvs-serv31881/arch/sh/kernel Modified Files: Makefile io_powervr2dc.c mach_dreamcast.c Added Files: pci_gaps.c Log Message: Initial PCI and BBA support --- NEW FILE --- /* * linux/arch/sh/kernel/pci_gaps.c * * Copyright (c) 2001 M. R. Brown <mr...@li...> * * This file is part of the LinuxDC project, goto http://linuxdc.org/ for * project details. It may be copied or modified under the terms of the GNU * General Public License. See the file linux/COPYING for more information. * * Low-level access to the GAPS PCI bridge (found on Sega's Broadband * Adapter). */ /* * Much of the GAPS PCI code is adapted from Jason R. Thorpe's and Marcus * Comstedt's NetBSD Dreamcast port at http://www.netbsd.org/Ports/dreamcast/ * * The rest is templated from pci_st40.c by David J. Mckay. */ #include <linux/config.h> #include <linux/kernel.h> #include <linux/smp.h> #include <linux/smp_lock.h> #include <linux/init.h> #include <linux/errno.h> #include <linux/pci.h> #include <linux/irq.h> #include <linux/delay.h> #include <linux/types.h> #include <linux/string.h> #include <asm/addrspace.h> #include <asm/io.h> #include <asm/pci.h> #define GAPSPCI_BASE_ADDR 0xa1001400 #define GAPSPCI_PCI_ADDR 0xa1001600 #define GAPSPCI_DEVICE_ADDR 0x01001700 #define GAPSPCI_DMA_BASE 0x01840000 #define GAPSPCI_DMA_SIZE 32768 #define GAPSPCI_INIT_MAGIC 0x5a14a501 /* * We only support one device. That means that any other "detected" devices * are spurious, and we want to act like they never happened. * XXX - This is evil hackery, is there a better way to prevent the kernel * from scanning all possible slots? */ #define CHECK_DEVFN do { \ if (dev->devfn != 0) return PCIBIOS_DEVICE_NOT_FOUND; \ } while (0) static int gapspci_read_config_byte(struct pci_dev *dev,int where,u8 *val) { CHECK_DEVFN; *val = readb(GAPSPCI_PCI_ADDR + where); return PCIBIOS_SUCCESSFUL; } static int gapspci_read_config_word(struct pci_dev *dev, int where, u16 * val) { CHECK_DEVFN; *val = readw(GAPSPCI_PCI_ADDR + where); return PCIBIOS_SUCCESSFUL; } /* * Special case: since we only support one device (the BBA), if the kernel * is accessing the BAR we just return the address of the BBA. */ static int gapspci_read_config_dword(struct pci_dev *dev, int where, u32 * val) { CHECK_DEVFN; if (where == PCI_BASE_ADDRESS_1) { *val = GAPSPCI_DEVICE_ADDR; } else { *val = readl(GAPSPCI_PCI_ADDR + where); } return PCIBIOS_SUCCESSFUL; } static int gapspci_write_config_byte(struct pci_dev *dev, int where, u8 val) { CHECK_DEVFN; writeb(val, GAPSPCI_PCI_ADDR + where); return PCIBIOS_SUCCESSFUL; } static int gapspci_write_config_word(struct pci_dev *dev, int where, u16 val) { CHECK_DEVFN; writew(val, GAPSPCI_PCI_ADDR + where); return PCIBIOS_SUCCESSFUL; } /* * Special case: only one device is supported, so don't allow writes to the * BAR. */ static int gapspci_write_config_dword(struct pci_dev *dev, int where, u32 val) { CHECK_DEVFN; if (where == PCI_BASE_ADDRESS_1 && val != 0x01000000) { ; } else { writel(val, GAPSPCI_PCI_ADDR + where); } return PCIBIOS_SUCCESSFUL; } static struct pci_ops pci_config_ops = { gapspci_read_config_byte, gapspci_read_config_word, gapspci_read_config_dword, gapspci_write_config_byte, gapspci_write_config_word, gapspci_write_config_dword }; /* Everything hangs off this */ static struct pci_bus *pci_root_bus; static u8 __init no_swizzle(struct pci_dev *dev, u8 * pin) { return PCI_SLOT(dev->devfn); } /* * Interrupt handling: reuse the IRQ9 routines from setup_powervr2dc.c. * Since the GAPS IRQ enable register doesn't map with the others, it's * better to have this here (especially since it's another IRQ). */ #define GAPSPCI_REG_IMR 0xa05f6924 #define GAPSPCI_IRQ_ENA (1<<3) static inline void disable_gapspci_irq(unsigned int irq) { *(volatile unsigned long *)(GAPSPCI_REG_IMR) &= ~GAPSPCI_IRQ_ENA; } static inline void enable_gapspci_irq(unsigned int irq) { *(volatile unsigned long *)(GAPSPCI_REG_IMR) |= GAPSPCI_IRQ_ENA; } static void ack_gapspci_irq(unsigned int irq) { disable_gapspci_irq(irq); } static void end_gapspci_irq(unsigned int irq) { enable_gapspci_irq(irq); } static unsigned int startup_gapspci_irq(unsigned int irq) { enable_gapspci_irq(irq); return 0; } static void shutdown_gapspci_irq(unsigned int irq) { disable_gapspci_irq(irq); } static struct hw_interrupt_type gapspci_int = { typename: "GAPS PCI bridge IRQ", startup: startup_gapspci_irq, shutdown: shutdown_gapspci_irq, enable: enable_gapspci_irq, disable: disable_gapspci_irq, ack: ack_gapspci_irq, end: end_gapspci_irq, }; #define GAPSPCI_IRQ 11 /* We only support one IRQ */ static int __init map_gapspci_irq(struct pci_dev *dev, u8 slot, u8 pin) { switch (slot) { case 0: return GAPSPCI_IRQ; default: return -1; } } void __init pcibios_fixup_pbus_ranges(struct pci_bus *bus, struct pbus_set_ranges_data *ranges) { ranges->io_start -= bus->resource[0]->start; ranges->io_end -= bus->resource[0]->start; ranges->mem_start -= bus->resource[1]->start; ranges->mem_end -= bus->resource[1]->start; } #define GAPSPCI_REG_UNKN_14 (GAPSPCI_BASE_ADDR + 0x14) #define GAPSPCI_REG_STATUS (GAPSPCI_BASE_ADDR + 0x18) #define GAPSPCI_REG_UNKN_20 (GAPSPCI_BASE_ADDR + 0x20) #define GAPSPCI_REG_UNKN_24 (GAPSPCI_BASE_ADDR + 0x24) #define GAPSPCI_REG_DMASTART (GAPSPCI_BASE_ADDR + 0x28) #define GAPSPCI_REG_DMAEND (GAPSPCI_BASE_ADDR + 0x2c) #define GAPSPCI_REG_UNKN_34 (GAPSPCI_BASE_ADDR + 0x34) /* * Initialize the GAPS PCI bridge. GAPS initialization code adapted from * Dan Potter's BB1 code. */ void __init gaps_init_pci(void) { struct pci_dev *dev; u16 tmp = 10000; if (strncmp((const char*)GAPSPCI_BASE_ADDR, "GAPSPCI_BRIDGE_2", 16)) { return; } printk("Sega GAPS PCI bridge found at 0x%08x\n", GAPSPCI_BASE_ADDR); /* Bring the GAPS online */ writel(GAPSPCI_INIT_MAGIC, GAPSPCI_REG_STATUS); while (!(readl(GAPSPCI_REG_STATUS) & 1) && tmp > 0) tmp--; if (!(readl(GAPSPCI_REG_STATUS) & 1)) panic("gaps_init_pci: GAPS not responding!\n"); writel(0x1000000, GAPSPCI_REG_UNKN_20); writel(0x1000000, GAPSPCI_REG_UNKN_24); writel(GAPSPCI_DMA_BASE, GAPSPCI_REG_DMASTART); writel(GAPSPCI_DMA_BASE + GAPSPCI_DMA_SIZE, GAPSPCI_REG_DMAEND); writel(1, GAPSPCI_REG_UNKN_14); writel(1, GAPSPCI_REG_UNKN_34); writew(0xf900, GAPSPCI_PCI_ADDR + PCI_STATUS); writel(0, GAPSPCI_PCI_ADDR + PCI_ROM_ADDRESS); writeb(0, GAPSPCI_PCI_ADDR + PCI_INTERRUPT_LINE); writeb(0xf0, GAPSPCI_PCI_ADDR + PCI_LATENCY_TIMER); tmp = readw(0xa1000004); writew(0x0006, GAPSPCI_PCI_ADDR + PCI_COMMAND); writel(0x01000000, GAPSPCI_PCI_ADDR + PCI_BASE_ADDRESS_1); tmp = readb(GAPSPCI_PCI_ADDR + 0x50); /* ??? - undocumented */ irq_desc[GAPSPCI_IRQ].handler = &gapspci_int; /* Initialize the bus and scan for devices */ pci_root_bus = pci_scan_bus(0, &pci_config_ops, NULL); /* Manually assign resources */ /* PIO and MMIO mean the same thing here */ dev = pci_find_slot(0, PCI_DEVFN(0, 0)); dev->resource[0].start = P2SEGADDR(GAPSPCI_DEVICE_ADDR); dev->resource[0].end = P2SEGADDR(GAPSPCI_DEVICE_ADDR + 0xff); dev->resource[0].flags = IORESOURCE_IO; dev->resource[1].start = P2SEGADDR(GAPSPCI_DEVICE_ADDR); dev->resource[1].end = P2SEGADDR(GAPSPCI_DEVICE_ADDR + 0xff); dev->resource[1].flags = IORESOURCE_MEM; pci_fixup_irqs(no_swizzle, map_gapspci_irq); } int pcibios_enable_device(struct pci_dev *dev) { u16 cmd, old_cmd; int idx; struct resource *r; pci_read_config_word(dev, PCI_COMMAND, &cmd); old_cmd = cmd; for (idx = 0; idx < 6; idx++) { r = dev->resource + idx; if (!r->start && r->end) { printk(KERN_ERR "PCI: Device %s not available because" " of resource collisions\n", dev->slot_name); return -EINVAL; } if (r->flags & IORESOURCE_IO) cmd |= PCI_COMMAND_IO; if (r->flags & IORESOURCE_MEM) cmd |= PCI_COMMAND_MEMORY; } if (cmd != old_cmd) { printk("PCI: enabling device %s (%04x -> %04x)\n", dev->slot_name, old_cmd, cmd); pci_write_config_word(dev, PCI_COMMAND, cmd); } return 0; } void __init pcibios_fixup_bus(struct pci_bus *bus) { } void pcibios_align_resource(void *data, struct resource *res, unsigned long size) { } void __init pcibios_update_resource(struct pci_dev *dev, struct resource *root, struct resource *res, int resource) { unsigned long where, size; u32 reg; printk("PCI: Assigning %3s %08lx to %s\n", res->flags & IORESOURCE_IO ? "IO" : "MEM", res->start, dev->name); where = PCI_BASE_ADDRESS_0 + resource * 4; size = res->end - res->start; pci_read_config_dword(dev, where, ®); reg = (reg & size) | (((u32) (res->start - root->start)) & ~size); pci_write_config_dword(dev, where, reg); } void __init pcibios_update_irq(struct pci_dev *dev, int irq) { printk("PCI: Assigning IRQ %02d to %s\n", irq, dev->name); /*pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);*/ } /* * XXX - GAPS does have 32K of DMA memory, its allocation needs to be * implemented here... */ void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, dma_addr_t * dma_handle) { void *ret; int gfp = GFP_ATOMIC; ret = (void *) __get_free_pages(gfp, get_order(size)); if (ret != NULL) { /* Is it neccessary to do the memset? */ memset(ret, 0, size); *dma_handle = virt_to_bus(ret); } /* We must flush the cache before we pass it on to the device */ flush_cache_all(); return P2SEGADDR(ret); } void pci_free_consistent(struct pci_dev *hwdev, size_t size, void *vaddr, dma_addr_t dma_handle) { unsigned long p1addr=P1SEGADDR((unsigned long)vaddr); free_pages(p1addr, get_order(size)); } Index: Makefile =================================================================== RCS file: /cvsroot/linuxdc/linux/arch/sh/kernel/Makefile,v retrieving revision 1.4 retrieving revision 1.5 diff -C2 -r1.4 -r1.5 *** Makefile 2001/02/21 15:22:11 1.4 --- Makefile 2001/03/11 11:10:33 1.5 *************** *** 27,30 **** --- 27,31 ---- obj-y += pci-sh.o obj-$(CONFIG_CPU_SUBTYPE_ST40STB1)+= pci_st40.o + obj-$(CONFIG_SH_DREAMCAST) += pci_gaps.o endif Index: io_powervr2dc.c =================================================================== RCS file: /cvsroot/linuxdc/linux/arch/sh/kernel/io_powervr2dc.c,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -r1.1 -r1.2 *** io_powervr2dc.c 2001/02/21 15:22:11 1.1 --- io_powervr2dc.c 2001/03/11 11:10:33 1.2 *************** *** 8,21 **** */ - /* - * 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> --- 8,11 ---- *************** *** 27,61 **** /* 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(); } - */ --- 17,131 ---- /* + * PowerVR2DC I/O routines. These implement G2 bus locking/unlocking for each + * access to I/O memory. We do these for the all I/O operations because PC + * needs them, but some other DC periph. modules (perhaps AICA) may need these + * as well. + * + * If it seems like unnecessary dupication, well, some drivers may only use + * the inX/outX interface, which may be mapped to ISA eventually. Right now + * everything is just direct access. + * + * These routines have been adapted from Marcus Comstedt's NetBSD DC port. + */ + + /* Block until G2 I/O has completed. */ + /* + * XXX - Once we get an implementation of G2 DMA (ref. bITmASTER), + * suspend it here. + */ + static inline void g2_bus_lock(void) + { + cli(); + while (ctrl_inl(PVR2DC_G2_STATUS) & 32) + ; + } + + /* XXX - G2 DMA would be resumed here */ + static inline void g2_bus_unlock(void) + { + sti(); + } + unsigned char powervr2dc_inb(unsigned long port) { ! return readb(port); } unsigned short powervr2dc_inw(unsigned long port) { ! return readw(port); } unsigned int powervr2dc_inl(unsigned long port) + { + return readl(port); + } + + void powervr2dc_outb(unsigned char value, unsigned long port) + { + writeb(value, port); + } + + void powervr2dc_outw(unsigned short value, unsigned long port) + { + writew(value, port); + } + + void powervr2dc_outl(unsigned int value, unsigned long port) + { + writel(value, port); + } + + unsigned char powervr2dc_readb(unsigned long addr) { ! unsigned char ret; ! ! g2_bus_lock(); ! ret = *(volatile unsigned char*)addr; ! g2_bus_unlock(); ! ! return ret; ! } ! ! unsigned short powervr2dc_readw(unsigned long addr) ! { ! unsigned short ret; ! ! g2_bus_lock(); ! ret = *(volatile unsigned short*)addr; ! g2_bus_unlock(); ! ! return ret; ! } ! ! unsigned int powervr2dc_readl(unsigned long addr) ! { ! unsigned long ret; ! ! g2_bus_lock(); ! ret = *(volatile unsigned long*)addr; ! g2_bus_unlock(); ! ! return ret; } ! void powervr2dc_writeb(unsigned char value, unsigned long addr) { ! g2_bus_lock(); ! *(volatile unsigned char*)addr = value; ! g2_bus_unlock(); } ! void powervr2dc_writew(unsigned short value, unsigned long addr) { ! g2_bus_lock(); ! *(volatile unsigned short*)addr = value; ! g2_bus_unlock(); } ! void powervr2dc_writel(unsigned int value, unsigned long addr) { ! g2_bus_lock(); ! *(volatile unsigned long*)addr = value; ! g2_bus_unlock(); } Index: mach_dreamcast.c =================================================================== RCS file: /cvsroot/linuxdc/linux/arch/sh/kernel/mach_dreamcast.c,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -r1.1 -r1.2 *** mach_dreamcast.c 2001/02/21 15:22:11 1.1 --- mach_dreamcast.c 2001/03/11 11:10:33 1.2 *************** *** 26,35 **** 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, --- 26,35 ---- mv_nr_irqs: 160, /* PVR2DC_IRQ_BASE + PVR2DC_IRQ_NUM */ ! mv_inb: powervr2dc_inb, ! mv_inw: powervr2dc_inw, ! mv_inl: powervr2dc_inl, ! mv_outb: powervr2dc_outb, ! mv_outw: powervr2dc_outw, ! mv_outl: powervr2dc_outl, mv_inb_p: generic_inb_p, *************** *** 47,56 **** 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, --- 47,65 ---- mv_outsl: generic_outsl, ! mv_readb: powervr2dc_readb, ! mv_readw: powervr2dc_readw, ! mv_readl: powervr2dc_readl, ! mv_writeb: powervr2dc_writeb, ! mv_writew: powervr2dc_writew, ! mv_writel: powervr2dc_writel, ! ! mv_ioremap: generic_ioremap, ! mv_iounmap: generic_iounmap, ! ! mv_isa_port2addr: generic_isa_port2addr, ! ! #ifdef CONFIG_PCI ! mv_init_pci: gaps_init_pci, ! #endif mv_irq_demux: powervr2dc_irq_demux, |