Commit bade5622 authored by David S. Miller's avatar David S. Miller
Browse files

[SPARC64]: More SUN4V PCI controller work.



Add assembler file for PCI hypervisor calls.
Setup basic skeleton of SUN4V PCI controller driver.

Add 32-bit devhandle to PBM struct, as this is needed for
hypervisor calls.

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 8f6a93a1
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -14,7 +14,8 @@ obj-y := process.o setup.o cpu.o idprom.o \
		   power.o sbus.o iommu_common.o sparc64_ksyms.o chmc.o

obj-$(CONFIG_PCI)	 += ebus.o isa.o pci_common.o pci_iommu.o \
			    pci_psycho.o pci_sabre.o pci_schizo.o pci_sun4v.o
			    pci_psycho.o pci_sabre.o pci_schizo.o \
			    pci_sun4v.o pci_sun4v_asm.o
obj-$(CONFIG_SMP)	 += smp.o trampoline.o
obj-$(CONFIG_SPARC32_COMPAT) += sys32.o sys_sparc32.o signal32.o
obj-$(CONFIG_BINFMT_ELF32) += binfmt_elf32.o
+286 −0
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@
#include "pci_impl.h"
#include "iommu_common.h"

#include "pci_sun4v.h"

static void *pci_4v_alloc_consistent(struct pci_dev *pdev, size_t size, dma_addr_t *dma_addrp)
{
	return NULL;
@@ -67,8 +69,292 @@ struct pci_iommu_ops pci_sun4v_iommu_ops = {
	.dma_sync_sg_for_cpu		= pci_4v_dma_sync_sg_for_cpu,
};

/* SUN4V PCI configuration space accessors. */

static int pci_sun4v_read_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn,
				  int where, int size, u32 *value)
{
	/* XXX Implement me! XXX */
	return 0;
}

static int pci_sun4v_write_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn,
				   int where, int size, u32 value)
{
	/* XXX Implement me! XXX */
	return 0;
}

static struct pci_ops pci_sun4v_ops = {
	.read =		pci_sun4v_read_pci_cfg,
	.write =	pci_sun4v_write_pci_cfg,
};


static void pci_sun4v_scan_bus(struct pci_controller_info *p)
{
	/* XXX Implement me! XXX */
}

static unsigned int pci_sun4v_irq_build(struct pci_pbm_info *pbm,
					struct pci_dev *pdev,
					unsigned int ino)
{
	/* XXX Implement me! XXX */
	return 0;
}

/* XXX correct? XXX */
static void pci_sun4v_base_address_update(struct pci_dev *pdev, int resource)
{
	struct pcidev_cookie *pcp = pdev->sysdata;
	struct pci_pbm_info *pbm = pcp->pbm;
	struct resource *res, *root;
	u32 reg;
	int where, size, is_64bit;

	res = &pdev->resource[resource];
	if (resource < 6) {
		where = PCI_BASE_ADDRESS_0 + (resource * 4);
	} else if (resource == PCI_ROM_RESOURCE) {
		where = pdev->rom_base_reg;
	} else {
		/* Somebody might have asked allocation of a non-standard resource */
		return;
	}

	is_64bit = 0;
	if (res->flags & IORESOURCE_IO)
		root = &pbm->io_space;
	else {
		root = &pbm->mem_space;
		if ((res->flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK)
		    == PCI_BASE_ADDRESS_MEM_TYPE_64)
			is_64bit = 1;
	}

	size = res->end - res->start;
	pci_read_config_dword(pdev, where, &reg);
	reg = ((reg & size) |
	       (((u32)(res->start - root->start)) & ~size));
	if (resource == PCI_ROM_RESOURCE) {
		reg |= PCI_ROM_ADDRESS_ENABLE;
		res->flags |= IORESOURCE_ROM_ENABLE;
	}
	pci_write_config_dword(pdev, where, reg);

	/* This knows that the upper 32-bits of the address
	 * must be zero.  Our PCI common layer enforces this.
	 */
	if (is_64bit)
		pci_write_config_dword(pdev, where + 4, 0);
}

/* XXX correct? XXX */
static void pci_sun4v_resource_adjust(struct pci_dev *pdev,
				      struct resource *res,
				      struct resource *root)
{
	res->start += root->start;
	res->end += root->start;
}

/* Use ranges property to determine where PCI MEM, I/O, and Config
 * space are for this PCI bus module.
 */
static void pci_sun4v_determine_mem_io_space(struct pci_pbm_info *pbm)
{
	int i, saw_cfg, saw_mem, saw_io;

	saw_cfg = saw_mem = saw_io = 0;
	for (i = 0; i < pbm->num_pbm_ranges; i++) {
		struct linux_prom_pci_ranges *pr = &pbm->pbm_ranges[i];
		unsigned long a;
		int type;

		type = (pr->child_phys_hi >> 24) & 0x3;
		a = (((unsigned long)pr->parent_phys_hi << 32UL) |
		     ((unsigned long)pr->parent_phys_lo  <<  0UL));

		switch (type) {
		case 0:
			/* PCI config space, 16MB */
			pbm->config_space = a;
			saw_cfg = 1;
			break;

		case 1:
			/* 16-bit IO space, 16MB */
			pbm->io_space.start = a;
			pbm->io_space.end = a + ((16UL*1024UL*1024UL) - 1UL);
			pbm->io_space.flags = IORESOURCE_IO;
			saw_io = 1;
			break;

		case 2:
			/* 32-bit MEM space, 2GB */
			pbm->mem_space.start = a;
			pbm->mem_space.end = a + (0x80000000UL - 1UL);
			pbm->mem_space.flags = IORESOURCE_MEM;
			saw_mem = 1;
			break;

		default:
			break;
		};
	}

	if (!saw_cfg || !saw_io || !saw_mem) {
		prom_printf("%s: Fatal error, missing %s PBM range.\n",
			    pbm->name,
			    ((!saw_cfg ?
			      "CFG" :
			      (!saw_io ?
			       "IO" : "MEM"))));
		prom_halt();
	}

	printk("%s: PCI CFG[%lx] IO[%lx] MEM[%lx]\n",
	       pbm->name,
	       pbm->config_space,
	       pbm->io_space.start,
	       pbm->mem_space.start);
}

static void pbm_register_toplevel_resources(struct pci_controller_info *p,
					    struct pci_pbm_info *pbm)
{
	pbm->io_space.name = pbm->mem_space.name = pbm->name;

	request_resource(&ioport_resource, &pbm->io_space);
	request_resource(&iomem_resource, &pbm->mem_space);
	pci_register_legacy_regions(&pbm->io_space,
				    &pbm->mem_space);
}

static void pci_sun4v_iommu_init(struct pci_pbm_info *pbm)
{
	/* XXX Implement me! XXX */
}

static void pci_sun4v_pbm_init(struct pci_controller_info *p, int prom_node)
{
	struct pci_pbm_info *pbm;
	struct linux_prom64_registers regs;
	unsigned int busrange[2];
	int err;

	/* XXX */
	pbm = &p->pbm_A;

	pbm->parent = p;
	pbm->prom_node = prom_node;
	pbm->pci_first_slot = 1;

	prom_getproperty(prom_node, "reg", (char *)&regs, sizeof(regs));
	pbm->devhandle = (regs.phys_addr >> 32UL) & 0x0fffffff;

	sprintf(pbm->name, "SUN4V-PCI%d PBM%c",
		p->index, (pbm == &p->pbm_A ? 'A' : 'B'));

	printk("%s: devhandle[%x]\n", pbm->name, pbm->devhandle);

	prom_getstring(prom_node, "name",
		       pbm->prom_name, sizeof(pbm->prom_name));

	err = prom_getproperty(prom_node, "ranges",
			       (char *) pbm->pbm_ranges,
			       sizeof(pbm->pbm_ranges));
	if (err == 0 || err == -1) {
		prom_printf("%s: Fatal error, no ranges property.\n",
			    pbm->name);
		prom_halt();
	}

	pbm->num_pbm_ranges =
		(err / sizeof(struct linux_prom_pci_ranges));

	pci_sun4v_determine_mem_io_space(pbm);
	pbm_register_toplevel_resources(p, pbm);

	err = prom_getproperty(prom_node, "interrupt-map",
			       (char *)pbm->pbm_intmap,
			       sizeof(pbm->pbm_intmap));
	if (err != -1) {
		pbm->num_pbm_intmap = (err / sizeof(struct linux_prom_pci_intmap));
		err = prom_getproperty(prom_node, "interrupt-map-mask",
				       (char *)&pbm->pbm_intmask,
				       sizeof(pbm->pbm_intmask));
		if (err == -1) {
			prom_printf("%s: Fatal error, no "
				    "interrupt-map-mask.\n", pbm->name);
			prom_halt();
		}
	} else {
		pbm->num_pbm_intmap = 0;
		memset(&pbm->pbm_intmask, 0, sizeof(pbm->pbm_intmask));
	}

	err = prom_getproperty(prom_node, "bus-range",
			       (char *)&busrange[0],
			       sizeof(busrange));
	if (err == 0 || err == -1) {
		prom_printf("%s: Fatal error, no bus-range.\n", pbm->name);
		prom_halt();
	}
	pbm->pci_first_busno = busrange[0];
	pbm->pci_last_busno = busrange[1];

	pci_sun4v_iommu_init(pbm);
}

void sun4v_pci_init(int node, char *model_name)
{
	struct pci_controller_info *p;
	struct pci_iommu *iommu;

	p = kmalloc(sizeof(struct pci_controller_info), GFP_ATOMIC);
	if (!p) {
		prom_printf("SUN4V_PCI: Fatal memory allocation error.\n");
		prom_halt();
	}
	memset(p, 0, sizeof(*p));

	iommu = kmalloc(sizeof(struct pci_iommu), GFP_ATOMIC);
	if (!iommu) {
		prom_printf("SCHIZO: Fatal memory allocation error.\n");
		prom_halt();
	}
	memset(iommu, 0, sizeof(*iommu));
	p->pbm_A.iommu = iommu;

	iommu = kmalloc(sizeof(struct pci_iommu), GFP_ATOMIC);
	if (!iommu) {
		prom_printf("SCHIZO: Fatal memory allocation error.\n");
		prom_halt();
	}
	memset(iommu, 0, sizeof(*iommu));
	p->pbm_B.iommu = iommu;

	p->next = pci_controller_root;
	pci_controller_root = p;

	p->index = pci_num_controllers++;
	p->pbms_same_domain = 0;

	p->scan_bus = pci_sun4v_scan_bus;
	p->irq_build = pci_sun4v_irq_build;
	p->base_address_update = pci_sun4v_base_address_update;
	p->resource_adjust = pci_sun4v_resource_adjust;
	p->pci_ops = &pci_sun4v_ops;

	/* Like PSYCHO and SCHIZO we have a 2GB aligned area
	 * for memory space.
	 */
	pci_memspace_mask = 0x7fffffffUL;

	pci_sun4v_pbm_init(p, node);

	prom_printf("sun4v_pci_init: Implement me.\n");
	prom_halt();
}
+20 −0
Original line number Diff line number Diff line
/* pci_sun4v.h: SUN4V specific PCI controller support.
 *
 * Copyright (C) 2006 David S. Miller (davem@davemloft.net)
 */

#ifndef _PCI_SUN4V_H
#define _PCI_SUN4V_H

extern unsigned long pci_sun4v_devino_to_sysino(unsigned long devhandle,
						unsigned long deino);
extern unsigned long pci_sun4v_iommu_map(unsigned long devhandle,
					 unsigned long tsbid,
					 unsigned long num_ttes,
					 unsigned long io_attributes,
					 unsigned long io_page_list_pa);
extern unsigned long pci_sun4v_iommu_demap(unsigned long devhandle,
					   unsigned long tsbid,
					   unsigned long num_ttes);

#endif /* !(_PCI_SUN4V_H) */
+56 −0
Original line number Diff line number Diff line
/* pci_sun4v_asm: Hypervisor calls for PCI support.
 *
 * Copyright (C) 2006 David S. Miller <davem@davemloft.net>
 */

#include <asm/hypervisor.h>

	/* %o0: devhandle
	 * %o1:	devino
	 *
	 * returns %o0: sysino
	 */
	.globl	pci_sun4v_devino_to_sysino
pci_sun4v_devino_to_sysino:
	mov	%o1, %o2
	mov	%o0, %o1
	mov	HV_FAST_INTR_DEVINO2SYSINO, %o0
	ta	HV_FAST_TRAP
	retl
	 mov	%o1, %o0

	/* %o0: devhandle
	 * %o1:	tsbid
	 * %o2:	num ttes
	 * %o3:	io_attributes
	 * %o4:	io_page_list phys address
	 *
	 * returns %o0:	num ttes mapped
	 */
	.globl	pci_sun4v_iommu_map
pci_sun4v_iommu_map:
	mov	%o4, %o5
	mov	%o3, %o4
	mov	%o2, %o3
	mov	%o1, %o2
	mov	%o0, %o1
	mov	HV_FAST_PCI_IOMMU_MAP, %o0
	ta	HV_FAST_TRAP
	retl
	 mov	%o1, %o0

	/* %o0: devhandle
	 * %o1:	tsbid
	 * %o2:	num ttes
	 *
	 * returns %o0:	num ttes demapped
	 */
	.globl	pci_sun4v_iommu_demap
pci_sun4v_iommu_demap:
	mov	%o2, %o3
	mov	%o1, %o2
	mov	%o0, %o1
	mov	HV_FAST_PCI_IOMMU_DEMAP, %o0
	ta	HV_FAST_TRAP
	retl
	 mov	%o1, %o0
+3 −0
Original line number Diff line number Diff line
@@ -139,6 +139,9 @@ struct pci_pbm_info {
	/* Opaque 32-bit system bus Port ID. */
	u32				portid;

	/* Opaque 32-bit handle used for hypervisor calls.  */
	u32				devhandle;

	/* Chipset version information. */
	int				chip_type;
#define PBM_CHIP_TYPE_SABRE		1