Skip to content

Commit

Permalink
[SPARC64]: More SUN4V PCI controller work.
Browse files Browse the repository at this point in the history
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: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Mar 20, 2006
1 parent 8f6a93a commit bade562
Show file tree
Hide file tree
Showing 5 changed files with 367 additions and 1 deletion.
3 changes: 2 additions & 1 deletion arch/sparc64/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
286 changes: 286 additions & 0 deletions arch/sparc64/kernel/pci_sun4v.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 changes: 20 additions & 0 deletions arch/sparc64/kernel/pci_sun4v.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* 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 changes: 56 additions & 0 deletions arch/sparc64/kernel/pci_sun4v_asm.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/* 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 changes: 3 additions & 0 deletions include/asm-sparc64/pbm.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit bade562

Please sign in to comment.