|  | /* | 
|  | * This file is subject to the terms and conditions of the GNU General Public | 
|  | * License.  See the file "COPYING" in the main directory of this archive | 
|  | * for more details. | 
|  | * | 
|  | * Copyright (C) 2013 Cavium, Inc. | 
|  | */ | 
|  |  | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/init.h> | 
|  | #include <linux/interrupt.h> | 
|  | #include <linux/pci.h> | 
|  |  | 
|  | #include <uapi/asm/bitfield.h> | 
|  | #include <asm/byteorder.h> | 
|  | #include <asm/io.h> | 
|  |  | 
|  | #define PCI_CONFIG_ADDRESS	0xcf8 | 
|  | #define PCI_CONFIG_DATA		0xcfc | 
|  |  | 
|  | union pci_config_address { | 
|  | struct { | 
|  | __BITFIELD_FIELD(unsigned enable_bit	  : 1,	/* 31       */ | 
|  | __BITFIELD_FIELD(unsigned reserved	  : 7,	/* 30 .. 24 */ | 
|  | __BITFIELD_FIELD(unsigned bus_number	  : 8,	/* 23 .. 16 */ | 
|  | __BITFIELD_FIELD(unsigned devfn_number	  : 8,	/* 15 .. 8  */ | 
|  | __BITFIELD_FIELD(unsigned register_number : 8,	/* 7  .. 0  */ | 
|  | ))))); | 
|  | }; | 
|  | u32 w; | 
|  | }; | 
|  |  | 
|  | int pcibios_plat_dev_init(struct pci_dev *dev) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) | 
|  | { | 
|  | return ((pin + slot) % 4)+ MIPS_IRQ_PCIA; | 
|  | } | 
|  |  | 
|  | static void pci_virtio_guest_write_config_addr(struct pci_bus *bus, | 
|  | unsigned int devfn, int reg) | 
|  | { | 
|  | union pci_config_address pca = { .w = 0 }; | 
|  |  | 
|  | pca.register_number = reg; | 
|  | pca.devfn_number = devfn; | 
|  | pca.bus_number = bus->number; | 
|  | pca.enable_bit = 1; | 
|  |  | 
|  | outl(pca.w, PCI_CONFIG_ADDRESS); | 
|  | } | 
|  |  | 
|  | static int pci_virtio_guest_write_config(struct pci_bus *bus, | 
|  | unsigned int devfn, int reg, int size, u32 val) | 
|  | { | 
|  | pci_virtio_guest_write_config_addr(bus, devfn, reg); | 
|  |  | 
|  | switch (size) { | 
|  | case 1: | 
|  | outb(val, PCI_CONFIG_DATA + (reg & 3)); | 
|  | break; | 
|  | case 2: | 
|  | outw(val, PCI_CONFIG_DATA + (reg & 2)); | 
|  | break; | 
|  | case 4: | 
|  | outl(val, PCI_CONFIG_DATA); | 
|  | break; | 
|  | } | 
|  |  | 
|  | return PCIBIOS_SUCCESSFUL; | 
|  | } | 
|  |  | 
|  | static int pci_virtio_guest_read_config(struct pci_bus *bus, unsigned int devfn, | 
|  | int reg, int size, u32 *val) | 
|  | { | 
|  | pci_virtio_guest_write_config_addr(bus, devfn, reg); | 
|  |  | 
|  | switch (size) { | 
|  | case 1: | 
|  | *val = inb(PCI_CONFIG_DATA + (reg & 3)); | 
|  | break; | 
|  | case 2: | 
|  | *val = inw(PCI_CONFIG_DATA + (reg & 2)); | 
|  | break; | 
|  | case 4: | 
|  | *val = inl(PCI_CONFIG_DATA); | 
|  | break; | 
|  | } | 
|  | return PCIBIOS_SUCCESSFUL; | 
|  | } | 
|  |  | 
|  | static struct pci_ops pci_virtio_guest_ops = { | 
|  | .read  = pci_virtio_guest_read_config, | 
|  | .write = pci_virtio_guest_write_config, | 
|  | }; | 
|  |  | 
|  | static struct resource pci_virtio_guest_mem_resource = { | 
|  | .name = "Virtio MEM", | 
|  | .flags = IORESOURCE_MEM, | 
|  | .start	= 0x10000000, | 
|  | .end	= 0x1dffffff | 
|  | }; | 
|  |  | 
|  | static struct resource pci_virtio_guest_io_resource = { | 
|  | .name = "Virtio IO", | 
|  | .flags = IORESOURCE_IO, | 
|  | .start	= 0, | 
|  | .end	= 0xffff | 
|  | }; | 
|  |  | 
|  | static struct pci_controller pci_virtio_guest_controller = { | 
|  | .pci_ops = &pci_virtio_guest_ops, | 
|  | .mem_resource = &pci_virtio_guest_mem_resource, | 
|  | .io_resource = &pci_virtio_guest_io_resource, | 
|  | }; | 
|  |  | 
|  | static int __init pci_virtio_guest_setup(void) | 
|  | { | 
|  | pr_err("pci_virtio_guest_setup\n"); | 
|  |  | 
|  | /* Virtio comes pre-assigned */ | 
|  | pci_set_flags(PCI_PROBE_ONLY); | 
|  |  | 
|  | pci_virtio_guest_controller.io_map_base = mips_io_port_base; | 
|  | register_pci_controller(&pci_virtio_guest_controller); | 
|  | return 0; | 
|  | } | 
|  | arch_initcall(pci_virtio_guest_setup); |