/*
 * Copyright (c) 2007, 2008 University of Tsukuba
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of the University of Tsukuba nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
/*
 * Copyright (c) 2010-2013 Yuichi Watanabe
 */

#ifndef __IO_PCI_H
#define __IO_PCI_H

#include <common/common.h>
#include <common/types.h>
#include <common/list.h>
#include <core/rm.h>

struct idmask { u32 id, mask; };
#define idmask_match(a, b) ((a & b.mask) == b.id)

#define PCI_ID_ANY	0
#define PCI_ID_ANY_MASK	0

#define PCI_ID(vendor_id, device_id) \
	((((u32)device_id & 0xFFFF) << 16) | ((u32)vendor_id & 0xFFFF))

#define PCI_DEVFN(dn, fn) \
	((dn << 3) | fn)

#define PCI_DEV_NO(devfn) \
	((devfn & 0xF8) >> 3)

#define PCI_FUNC_NO(devfn) \
	(devfn & 0x7)

#define PCI_LOCATION_STR_SIZE	8
#define PCI_LOCATION_FORMAT	"%02x:%02x.%01x"
#define PCI_LOCATION_VALUE(dev)	dev->bus_no, PCI_DEV_NO(dev->devfn), PCI_FUNC_NO(dev->devfn)

#define PCI_MMCONFIG_SIZE		4096
#define PCI_LEGACY_CONFIG_SIZE		256

#define PCI_CONFIG_VENDOR_ID		0x00
#define PCI_CONFIG_DEVICE_ID		0x02
#define PCI_CONFIG_COMMAND		0x04
#define  PCI_CONFIG_COMMAND_INTERRUPT_DISABLE 0x0400
#define PCI_CONFIG_STATUS		0x06
#define PCI_CONFIG_REVISION_ID		0x08
#define PCI_CONFIG_CLASS_CODE		0x09
#define PCI_CONFIG_PROGRAMMING_IF	0x09
#define PCI_CONFIG_SUB_CLASS		0x0a
#define PCI_CONFIG_BASE_CLASS		0x0b
#define PCI_CONFIG_CACHE_LINE_SIZE	0x0c
#define PCI_CONFIG_LATENCY_TIMER	0x0d
#define PCI_CONFIG_HEADER_TYPE		0x0e
#define PCI_CONFIG_BIST			0x0f
#define PCI_CONFIG_BASE_ADDRESS0	0x10
#define PCI_CONFIG_BASE_ADDRESS1	0x14
#define PCI_CONFIG_BASE_ADDRESS2	0x18
#define PCI_CONFIG_BASE_ADDRESS3	0x1c
#define PCI_CONFIG_BASE_ADDRESS4	0x20
#define PCI_CONFIG_BASE_ADDRESS5	0x24
#define PCI_CONFIG_CARDBUS_CIS_POINTER	0x28
#define PCI_CONFIG_SUBSYSTEM_VENDOR_ID	0x2c
#define PCI_CONFIG_SUBSYSTEM_ID		0x2e
#define PCI_CONFIG_EXPANSION_ROM	0x30
#define PCI_CONFIG_CAPABILITY_POINTER	0x34
#define PCI_CONFIG_INTERRUPT_LINE	0x3c
#define PCI_CONFIG_INTERRUPT_PIN	0x3d
#define PCI_CONFIG_MIN_GRANT		0x3e
#define PCI_CONFIG_MAX_LATENCY		0x3f

#define PCI_CONFIG_PRIMARY_BUS_NUMBER		0x18
#define PCI_CONFIG_SECONDARY_BUS_NUMBER		0x19
#define PCI_CONFIG_SUBORDINATE_BUS_NUMBER	0x1a
#define PCI_CONFIG_SECONDARY_LATENCY_TIMER	0x1b
#define PCI_CONFIG_IO_BASE			0x1c
#define PCI_CONFIG_IO_LIMIT			0x1d
#define PCI_CONFIG_SECONDARY_STATUS		0x1e
#define PCI_CONFIG_MEMORY_BASE			0x20
#define PCI_CONFIG_MEMORY_LIMIT			0x22
#define PCI_CONFIG_PREFETCHABLE_BASE		0x24
#define PCI_CONFIG_PREFETCHABLE_LIMIT		0x26
#define PCI_CONFIG_PREFETCHABLE_BASE_UPPER	0x28
#define PCI_CONFIG_PREFETCHABLE_LIMIT_UPPER	0x2c
#define PCI_CONFIG_IO_BASE_UPPER		0x30
#define PCI_CONFIG_IO_LIMIT_UPPER		0x32
#define PCI_CONFIG_BRIDGE_CONTROL		0x3e
#define  PCI_CONFIG_SECONDARY_BUS_RESET		0x40

#define PCI_CONFIG_HEADER_TYPE_MASK		0x7f
#define PCI_CONFIG_HEADER_TYPE_0		0x00
#define PCI_CONFIG_HEADER_TYPE_1		0x01
#define PCI_CONFIG_HEADER_TYPE_MULTI_FUNCTION	0x80

#define PCI_CONFIG_BAR_NUM			6
#define PCI_CONFIG_HEADER1_BAR_NUM		2
#define PCI_CONFIG_BAR_SPACEMASK		0x00000001
#define PCI_CONFIG_BAR_MEMSPACE			0
#define PCI_CONFIG_BAR_IOSPACE			1
#define PCI_CONFIG_BAR_IOMASK			0x0000FFFC
#define PCI_CONFIG_BAR_MEMMASK			0xFFFFFFF0
#define PCI_CONFIG_BAR_MEMTYPE_MASK		0x00000006
#define PCI_CONFIG_BAR_MEMTYPE_32		0x00000000
#define PCI_CONFIG_BAR_MEMTYPE_64		0x00000004
#define PCI_CONFIG_BAR_PREFETCHABLE		0x00000008
#define PCI_CONFIG_ROM_MEMMASK			0xFFFFFFFE

#define PCI_CONFIG_IO_BASE_ADDRESS_MASK			0xf0
#define PCI_CONFIG_IO_BASE_CAP_MASK			0x0f
#define  PCI_CONFIG_IO_BASE_32_BIT			0x01
#define PCI_CONFIG_IO_LIMIT_ADDRESS_MASK		0xf0
#define PCI_CONFIG_IO_LIMIT_CAP_MASK			0x0f
#define  PCI_CONFIG_IO_LIMIT_32_BIT			0x01
#define PCI_CONFIG_MEMORY_BASE_ADDRESS_MASK		0xfff0
#define PCI_CONFIG_MEMORY_BASE_RESERVED_MASK		0x000f
#define PCI_CONFIG_MEMORY_LIMIT_ADDRESS_MASK		0xfff0
#define PCI_CONFIG_MEMORY_LIMIT_RESERVED_MASK		0x000f
#define PCI_CONFIG_PREFETCHABLE_BASE_ADDRESS_MASK	0xfff0
#define PCI_CONFIG_PREFETCHABLE_BASE_CAP_MASK		0x000f
#define  PCI_CONFIG_PREFETCHABLE_BASE_64_BIT		0x0001
#define PCI_CONFIG_PREFETCHABLE_LIMIT_ADDRESS_MASK	0xfff0
#define PCI_CONFIG_PREFETCHABLE_LIMIT_CAP_MASK		0x000f
#define  PCI_CONFIG_PREFETCHABLE_LIMIT_64_BIT		0x0001

#define PCI_CONFIG_VGA_ENABLE				0x0008

#define PCI_RESOURCE_NUM			9
#define PCI_RESOURCE_IO_WINDOW_INDEX		2
#define PCI_RESOURCE_MEMORY_WINDOW_INDEX	3
#define PCI_RESOURCE_PREFETCHABLE_WINDOW_INDEX	4

#define PCI_RESOURCE_VGA_MEM			5
#define PCI_RESOURCE_VGA_IO1			6
#define PCI_RESOURCE_VGA_IO2			7

#define PCI_RESOURCE_EXPANTION_ROM_INDEX	8

#define PCI_RESOURCE_PREFETCHABLE		0x01
#define PCI_RESOURCE_WINDOW			0x10

#define PCI_ASSIGNED_DEVICE_IRQ			0xff

typedef u16 pci_off_t;

struct pci_device {
	LIST_DEFINE(pci_device_list);
	LIST2_DEFINE(struct pci_device, vm_pci_device_list);

	struct pci_device *parent;
	LIST2_DEFINE(struct pci_device, sibling);

	struct pci_driver *driver;
	bool assigned;

	char name[PCI_LOCATION_STR_SIZE];

	u16 seg;
	u8 bus_no;
	u8 devfn;
	union {
		struct {
			u16 vendor_id;
			u16 device_id;
		} __attribute__ ((packed));
		u32 id;
	};
	union {
		struct {
			u32 class_code;
		};
		struct {
			u8 prog_if;
			u8 sub_class;
			u8 base_class;
		} __attribute__ ((packed)) ;
	};
	union {
		u8  header_type;
		struct {
			unsigned int type:		7;
			unsigned int multi_function:	1;
		} __attribute__ ((packed));
	};
	struct resource resource[PCI_RESOURCE_NUM];
	struct resource vmresource[PCI_RESOURCE_NUM];

	/*
	 * The followings are valid for PCI-PCI bridge.
	 */
	LIST2_DEFINE_HEAD(struct pci_device, children);
	u8 sec_bus;
	u8 sub_bus;

	u32 saved_config[PCI_MMCONFIG_SIZE / sizeof(u32)];
};

struct pci_driver {
	LIST_DEFINE(pci_driver_list);
	struct idmask id;
	struct idmask class;
	void (*new)(struct pci_device *dev);
	const char *name, *longname;
};

void pci_register_driver (struct pci_driver *driver);
struct pci_device *pci_next_assgined_pci_device(struct pci_device *cur);
void pci_dump_resources(void);

u8 pci_read_config_8(/* u16 seg, */ u8 bus, u8 devfn, pci_off_t offset);
u16 pci_read_config_16(/* u16 seg, */ u8 bus, u8 devfn, pci_off_t offset);
u32 pci_read_config_32(/* u16 seg, */ u8 bus, u8 devfn, pci_off_t offset);
u64 pci_read_config_64(/* u16 seg, */ u8 bus, u8 devfn, pci_off_t offset);
void pci_write_config_8(/* u16 seg, */ u8 bus, u8 devfn, pci_off_t offset, u8 data);
void pci_write_config_16(/* u16 seg, */ u8 bus, u8 devfn, pci_off_t offset, u16 data);
void pci_write_config_32(/* u16 seg, */ u8 bus, u8 devfn, pci_off_t offset, u32 data);
void pci_write_config_64(/* u16 seg, */ u8 bus, u8 devfn, pci_off_t offset, u64 data);

u8 pci_read_config8(struct pci_device *dev, pci_off_t offset);
u16 pci_read_config16(struct pci_device *dev, pci_off_t offset);
u32 pci_read_config32(struct pci_device *dev, pci_off_t offset);
u64 pci_read_config64(struct pci_device *dev, pci_off_t offset);
void pci_write_config8(struct pci_device *dev, pci_off_t offset, u8 data);
void pci_write_config16(struct pci_device *dev, pci_off_t offset, u16 data);
void pci_write_config32(struct pci_device *dev, pci_off_t offset, u32 data);
void pci_write_config64(struct pci_device *dev, pci_off_t offset, u64 data);

size_t pci_config_size(void);

#ifdef CONFIG_IO_HOOK
int pci_assigned_mmio_addr(phys_t addr, size_t *len);
int pci_assigned_ioport(ioport_t port, ioport_t *len);
void pci_dump_assignment(void);
#else
static inline int pci_assigned_mmio_addr(phys_t addr, size_t *len) { return 0; };
static inline int pci_assigned_ioport(ioport_t port, ioport_t *len) { return 0; };
static inline void pci_dump_assignment(void) {};
#endif

#endif /* __IO_PCI_H */
