From patchwork Tue Apr 26 08:54:47 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Hellstrom X-Patchwork-Id: 92894 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 38CAEB6EF7 for ; Tue, 26 Apr 2011 18:55:41 +1000 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752496Ab1DZIzf (ORCPT ); Tue, 26 Apr 2011 04:55:35 -0400 Received: from mail175c2.megamailservers.com ([69.49.111.75]:45851 "EHLO mail175c2.megamailservers.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752427Ab1DZIze (ORCPT ); Tue, 26 Apr 2011 04:55:34 -0400 X-POP-User: ekergarn.gaisler.com Received: from localhost.localdomain (gaisler.se [92.33.28.242]) by mail175c2.megamailservers.com (8.13.6/8.13.1) with ESMTP id p3Q8tR9w025438; Tue, 26 Apr 2011 04:55:28 -0400 From: Daniel Hellstrom To: davem@davemloft.net Cc: sparclinux@vger.kernel.org, sam@ravnborg.org Subject: [PATCH 1/2] sparc32,leon: added LEON-common low-level PCI routines Date: Tue, 26 Apr 2011 10:54:47 +0200 Message-Id: <1303808088-1231-1-git-send-email-daniel@gaisler.com> X-Mailer: git-send-email 1.5.4 X-CSC: 0 X-CHA: v=1.1 cv=DS5eMQQtQ4nFbUAEtHoylkid/yBh9OhBXgXKFyMy8+c= c=1 sm=1 a=vUI3CMDhKmwA:10 a=U62ajLuCel8A:10 a=jXKJviUpWSOlMmIvGrHOfw==:17 a=ebG-ZW-8AAAA:8 a=DsXk949JQXX7m4l9r8UA:9 a=b50cwjP7Yhih8zRw73gA:7 a=cCYF7-FHeg4A:10 a=jXKJviUpWSOlMmIvGrHOfw==:117 Sender: sparclinux-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: sparclinux@vger.kernel.org The LEON architecture does not have a BIOS or bootloader that initializes PCI for us, instead Linux generic PCI layer is used to set up resources and IRQ. Signed-off-by: Daniel Hellstrom --- arch/sparc/include/asm/pci_32.h | 5 + arch/sparc/include/asm/pci_leon.h | 36 ++++++ arch/sparc/include/asm/pcic.h | 2 +- arch/sparc/kernel/Makefile | 4 + arch/sparc/kernel/entry.S | 2 +- arch/sparc/kernel/leon_pci.c | 237 +++++++++++++++++++++++++++++++++++++ drivers/pci/Makefile | 1 + 7 files changed, 285 insertions(+), 2 deletions(-) create mode 100644 arch/sparc/include/asm/pci_leon.h create mode 100644 arch/sparc/kernel/leon_pci.c diff --git a/arch/sparc/include/asm/pci_32.h b/arch/sparc/include/asm/pci_32.h index 332ac9a..27f01cf 100644 --- a/arch/sparc/include/asm/pci_32.h +++ b/arch/sparc/include/asm/pci_32.h @@ -47,7 +47,12 @@ extern struct device_node *pci_device_to_OF_node(struct pci_dev *pdev); #endif /* __KERNEL__ */ +#ifndef CONFIG_SPARC_LEON /* generic pci stuff */ #include +#else +/* LEON PCI cores need its own definitions */ +#include +#endif #endif /* __SPARC_PCI_H */ diff --git a/arch/sparc/include/asm/pci_leon.h b/arch/sparc/include/asm/pci_leon.h new file mode 100644 index 0000000..1341351 --- /dev/null +++ b/arch/sparc/include/asm/pci_leon.h @@ -0,0 +1,36 @@ +/* + * asm/pci_leon.h + * + * Copyright (C) 2011 Aeroflex Gaisler AB, Daniel Hellstrom + */ + +#ifndef _ASM_PCI_LEON_H_ +#define _ASM_PCI_LEON_H_ + +/* On LEON PCI Memory space is mapped 1:1 with physical address space */ + +extern void +pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, + struct resource *res); + +extern void +pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, + struct pci_bus_region *region); + +static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel) +{ + return PCI_IRQ_NONE; +} + +/* PCI related definitions */ +struct leon_pci_info { + struct pci_ops *ops; + struct resource io_space; + struct resource mem_space; + int (*map_irq)(struct pci_dev *dev, u8 slot, u8 pin); +}; + +extern void leon_pci_init(struct platform_device *ofdev, + struct leon_pci_info *info); + +#endif /* _ASM_PCI_LEON_H_ */ diff --git a/arch/sparc/include/asm/pcic.h b/arch/sparc/include/asm/pcic.h index 7eb5d78..aa3df47 100644 --- a/arch/sparc/include/asm/pcic.h +++ b/arch/sparc/include/asm/pcic.h @@ -29,7 +29,7 @@ struct linux_pcic { int pcic_imdim; }; -#ifdef CONFIG_PCI +#if defined(CONFIG_PCI) && !defined(CONFIG_SPARC_LEON) extern int pcic_present(void); extern int pcic_probe(void); extern void pci_time_init(void); diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile index 9cff270..ea19bcc 100644 --- a/arch/sparc/kernel/Makefile +++ b/arch/sparc/kernel/Makefile @@ -73,7 +73,11 @@ obj-$(CONFIG_SPARC64_SMP) += cpumap.o obj-y += dma.o +ifndef CONFIG_SPARC_LEON obj-$(CONFIG_SPARC32_PCI) += pcic.o +else +obj-$(CONFIG_SPARC32_PCI) += leon_pci.o +endif obj-$(CONFIG_SMP) += trampoline_$(BITS).o smp_$(BITS).o obj-$(CONFIG_SPARC32_SMP) += sun4m_smp.o sun4d_smp.o leon_smp.o diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S index 1879739..4511c20 100644 --- a/arch/sparc/kernel/entry.S +++ b/arch/sparc/kernel/entry.S @@ -1567,7 +1567,7 @@ restore_current: retl nop -#ifdef CONFIG_PCI +#if defined(CONFIG_PCI) && !defined(CONFIG_SPARC_LEON) #include .align 4 diff --git a/arch/sparc/kernel/leon_pci.c b/arch/sparc/kernel/leon_pci.c new file mode 100644 index 0000000..761e5b1 --- /dev/null +++ b/arch/sparc/kernel/leon_pci.c @@ -0,0 +1,237 @@ +/* + * leon_pci.c: LEON Host PCI support + * + * Copyright (C) 2011 Aeroflex Gaisler AB, Daniel Hellstrom + * + * Code is partially derived from pcic.c + */ + +#include +#include +#include +#include + +/* The LEON architecture does not rely on a BIOS or bootloader to setup + * PCI for us. The Linux generic routines are used to setup resources, + * reset values of confuration-space registers settings ae preseved. + */ +void leon_pci_init(struct platform_device *ofdev, struct leon_pci_info *info) +{ + struct pci_bus *root_bus; + + root_bus = pci_scan_bus_parented(&ofdev->dev, 0, info->ops, info); + if (root_bus) { + root_bus->resource[0] = &info->io_space; + root_bus->resource[1] = &info->mem_space; + root_bus->resource[2] = NULL; + + /* Init all PCI devices into PCI tree */ + pci_bus_add_devices(root_bus); + + /* Setup IRQs of all devices using custom routines */ + pci_fixup_irqs(pci_common_swizzle, info->map_irq); + + /* Assign devices with resources */ + pci_assign_unassigned_resources(); + } +} + +/* PCI Memory and Prefetchable Memory is direct-mapped. However I/O Space is + * accessed through a Window which is translated to low 64KB in PCI space, the + * first 4KB is not used so 60KB is available. + * + * This function is used by generic code to translate resource addresses into + * PCI addresses. + */ +void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, + struct resource *res) +{ + struct leon_pci_info *info = dev->bus->sysdata; + + region->start = res->start; + region->end = res->end; + + if (res->flags & IORESOURCE_IO) { + region->start -= (info->io_space.start - 0x1000); + region->end -= (info->io_space.start - 0x1000); + } +} +EXPORT_SYMBOL(pcibios_resource_to_bus); + +/* see pcibios_resource_to_bus() comment */ +void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, + struct pci_bus_region *region) +{ + struct leon_pci_info *info = dev->bus->sysdata; + + res->start = region->start; + res->end = region->end; + + if (res->flags & IORESOURCE_IO) { + res->start += (info->io_space.start - 0x1000); + res->end += (info->io_space.start - 0x1000); + } +} +EXPORT_SYMBOL(pcibios_bus_to_resource); + +void __devinit pcibios_fixup_bus(struct pci_bus *pbus) +{ + struct leon_pci_info *info = pbus->sysdata; + struct pci_dev *dev; + int i, has_io, has_mem; + u16 cmd; + + /* Generic PCI bus probing sets these to point at + * &io{port,mem}_resouce which is wrong for us. + */ + if (pbus->self == NULL) { + pbus->resource[0] = &info->io_space; + pbus->resource[1] = &info->mem_space; + pbus->resource[2] = NULL; + } + + list_for_each_entry(dev, &pbus->devices, bus_list) { + /* + * We can not rely on that the bootloader has enabled I/O + * or memory access to PCI devices. Instead we enable it here + * if the device has BARs of respective type. + */ + has_io = has_mem = 0; + for (i = 0; i < PCI_ROM_RESOURCE; i++) { + unsigned long f = dev->resource[i].flags; + if (f & IORESOURCE_IO) + has_io = 1; + else if (f & IORESOURCE_MEM) + has_mem = 1; + } + /* ROM BARs are mapped into 32-bit memory space */ + if (dev->resource[PCI_ROM_RESOURCE].end != 0) { + dev->resource[PCI_ROM_RESOURCE].flags |= + IORESOURCE_ROM_ENABLE; + has_mem = 1; + } + pci_bus_read_config_word(pbus, dev->devfn, PCI_COMMAND, &cmd); + if (has_io && !(cmd & PCI_COMMAND_IO)) { +#ifdef CONFIG_PCI_DEBUG + printk(KERN_INFO "LEONPCI: Enabling I/O for dev %s\n", + pci_name(dev)); +#endif + cmd |= PCI_COMMAND_IO; + pci_bus_write_config_word(pbus, dev->devfn, PCI_COMMAND, + cmd); + } + if (has_mem && !(cmd & PCI_COMMAND_MEMORY)) { +#ifdef CONFIG_PCI_DEBUG + printk(KERN_INFO "LEONPCI: Enabling MEMORY for dev" + "%s\n", pci_name(dev)); +#endif + cmd |= PCI_COMMAND_MEMORY; + pci_bus_write_config_word(pbus, dev->devfn, PCI_COMMAND, + cmd); + } + } +} + +/* + * Other archs parse arguments here. + */ +char * __devinit pcibios_setup(char *str) +{ + return str; +} + +resource_size_t pcibios_align_resource(void *data, const struct resource *res, + resource_size_t size, resource_size_t align) +{ + return res->start; +} + +int pcibios_enable_device(struct pci_dev *dev, int mask) +{ + return pci_enable_resources(dev, mask); +} + +struct device_node *pci_device_to_OF_node(struct pci_dev *pdev) +{ + return pdev->dev.of_node; +} +EXPORT_SYMBOL(pci_device_to_OF_node); + +/* in/out routines taken from pcic.c + * + * This probably belongs here rather than ioport.c because + * we do not want this crud linked into SBus kernels. + * Also, think for a moment about likes of floppy.c that + * include architecture specific parts. They may want to redefine ins/outs. + * + * We do not use horrible macros here because we want to + * advance pointer by sizeof(size). + */ +void outsb(unsigned long addr, const void *src, unsigned long count) +{ + while (count) { + count -= 1; + outb(*(const char *)src, addr); + src += 1; + /* addr += 1; */ + } +} +EXPORT_SYMBOL(outsb); + +void outsw(unsigned long addr, const void *src, unsigned long count) +{ + while (count) { + count -= 2; + outw(*(const short *)src, addr); + src += 2; + /* addr += 2; */ + } +} +EXPORT_SYMBOL(outsw); + +void outsl(unsigned long addr, const void *src, unsigned long count) +{ + while (count) { + count -= 4; + outl(*(const long *)src, addr); + src += 4; + /* addr += 4; */ + } +} +EXPORT_SYMBOL(outsl); + +void insb(unsigned long addr, void *dst, unsigned long count) +{ + while (count) { + count -= 1; + *(unsigned char *)dst = inb(addr); + dst += 1; + /* addr += 1; */ + } +} +EXPORT_SYMBOL(insb); + +void insw(unsigned long addr, void *dst, unsigned long count) +{ + while (count) { + count -= 2; + *(unsigned short *)dst = inw(addr); + dst += 2; + /* addr += 2; */ + } +} +EXPORT_SYMBOL(insw); + +void insl(unsigned long addr, void *dst, unsigned long count) +{ + while (count) { + count -= 4; + /* + * XXX I am sure we are in for an unaligned trap here. + */ + *(unsigned long *)dst = inl(addr); + dst += 4; + /* addr += 4; */ + } +} +EXPORT_SYMBOL(insl); diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index c85f744..094308e 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_X86_VISWS) += setup-irq.o obj-$(CONFIG_MN10300) += setup-bus.o obj-$(CONFIG_MICROBLAZE) += setup-bus.o obj-$(CONFIG_TILE) += setup-bus.o setup-irq.o +obj-$(CONFIG_SPARC_LEON) += setup-bus.o setup-irq.o # # ACPI Related PCI FW Functions