/******************************************************************************
 * Copyright (c) 2004, 2008 IBM Corporation
 * All rights reserved.
 * This program and the accompanying materials
 * are made available under the terms of the BSD License
 * which accompanies this distribution, and is available at
 * http://www.opensource.org/licenses/bsd-license.php
 *
 * Contributors:
 *     IBM Corporation - initial implementation
 *****************************************************************************/

#ifndef DEVICE_LIB_H
#define DEVICE_LIB_H

#include <stdint.h>
#include <cpu.h>
#include "of.h"
#include <stdio.h>

// a Expansion Header Struct as defined in Plug and Play BIOS Spec 1.0a Chapter 3.2
typedef struct {
	char signature[4];	// signature
	uint8_t structure_revision;
	uint8_t length;		// in 16 byte blocks
	uint16_t next_header_offset;	// offset to next Expansion Header as 16bit little-endian value, as offset from the start of the Expansion ROM
	uint8_t reserved;
	uint8_t checksum;	// the sum of all bytes of the Expansion Header must be 0
	uint32_t device_id;	// PnP Device ID as 32bit little-endian value
	uint16_t p_manufacturer_string;	//16bit little-endian offset from start of Expansion ROM
	uint16_t p_product_string;	//16bit little-endian offset from start of Expansion ROM
	uint8_t device_base_type;
	uint8_t device_sub_type;
	uint8_t device_if_type;
	uint8_t device_indicators;
	// the following vectors are all 16bit little-endian offsets from start of Expansion ROM
	uint16_t bcv;		// Boot Connection Vector
	uint16_t dv;		// Disconnect Vector
	uint16_t bev;		// Bootstrap Entry Vector
	uint16_t reserved_2;
	uint16_t sriv;		// Static Resource Information Vector
} __attribute__ ((__packed__)) exp_header_struct_t;

// a PCI Data Struct as defined in PCI 2.3 Spec Chapter 6.3.1.2
typedef struct {
	uint8_t signature[4];	// signature, the String "PCIR"
	uint16_t vendor_id;
	uint16_t device_id;
	uint16_t reserved;
	uint16_t pci_ds_length;	// PCI Data Structure Length, 16bit little-endian value
	uint8_t pci_ds_revision;
	uint8_t class_code[3];
	uint16_t img_length;	// length of the Exp.ROM Image, 16bit little-endian value in 512 bytes
	uint16_t img_revision;
	uint8_t code_type;
	uint8_t indicator;
	uint16_t reserved_2;
} __attribute__ ((__packed__)) pci_data_struct_t;

typedef struct {
	uint8_t bus;
	uint8_t devfn;
	uint64_t puid;
	phandle_t phandle;
	ihandle_t ihandle;
	// store the address of the BAR that is used to simulate
	// legacy VGA memory accesses
	uint64_t vmem_addr;
	uint64_t vmem_size;
	// used to buffer I/O Accesses, that do not access the I/O Range of the device...
	// 64k might be overkill, but we can buffer all I/O accesses...
	uint8_t io_buffer[64 * 1024];
	uint16_t pci_vendor_id;
	uint16_t pci_device_id;
	// translated address of the "PC-Compatible" Expansion ROM Image for this device
	uint64_t img_addr;
	uint32_t img_size;	// size of the Expansion ROM Image (read from the PCI Data Structure)
} device_t;

typedef struct {
	uint8_t info;
	uint8_t bus;
	uint8_t devfn;
	uint8_t cfg_space_offset;
	uint64_t address;
	uint64_t address_offset;
	uint64_t size;
} __attribute__ ((__packed__)) translate_address_t;

// array to store address translations for this
// device. Needed for faster address translation, so
// not every I/O or Memory Access needs to call translate_address_dev
// and access the device tree
// 6 BARs, 1 Exp. ROM, 1 Cfg.Space, and 3 Legacy
// translations are supported... this should be enough for
// most devices... for VGA it is enough anyways...
translate_address_t translate_address_array[11];

// index of last translate_address_array entry
// set by get_dev_addr_info function
uint8_t taa_last_entry;

device_t bios_device;

uint8_t dev_init(char *device_name);
// NOTE: for dev_check_exprom to work, dev_init MUST be called first!
uint8_t dev_check_exprom();

uint8_t dev_translate_address(uint64_t * addr);

/* endianness swap functions for 16 and 32 bit words
 * copied from axon_pciconfig.c
 */
static inline void
out32le(void *addr, uint32_t val)
{
	asm volatile ("stwbrx  %0, 0, %1"::"r" (val), "r"(addr));
}

static inline uint32_t
in32le(void *addr)
{
	uint32_t val;
	const uint32_t *zaddr = addr;
	asm volatile ("lwbrx %0, %y1" : "=r"(val) : "Z"(*zaddr));
	return val;
}

static inline void
out16le(void *addr, uint16_t val)
{
	asm volatile ("sthbrx  %0, 0, %1"::"r" (val), "r"(addr));
}

static inline uint16_t
in16le(void *addr)
{
	uint16_t val;
	const uint16_t *zaddr = addr;
	asm volatile ("lhbrx %0, %y1" : "=r"(val) : "Z"(*zaddr));
	return val;
}

/* debug function, dumps HID1 and HID4 to detect wether caches are on/off */
static inline void
dumpHID()
{
	uint64_t hid;
	//HID1 = 1009
	__asm__ __volatile__("mfspr %0, 1009":"=r"(hid));
	printf("HID1: %016llx\n", hid);
	//HID4 = 1012
	__asm__ __volatile__("mfspr %0, 1012":"=r"(hid));
	printf("HID4: %016llx\n", hid);
}

#endif
