/*
 * This file is part of the coreboot project.
 *
 * Copyright 2013 Google Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef REG_SCRIPT_H
#define REG_SCRIPT_H

#include <stdint.h>
#include <arch/io.h>
#include <device/device.h>
#include <device/resource.h>

/*
 * The reg script library is a way to provide data-driven I/O accesses for
 * initializing devices. It currently supports PCI, legacy I/O,
 * memory-mapped I/O, and IOSF accesses.
 *
 * In order to simplify things for the developer the following features
 * are employed:
 * - Chaining of tables that allow runtime tables to chain to compile-time
 *   tables.
 * - Notion of current device (device_t) being worked on. This allows for
 *   PCI config, io, and mmio on a particular device's resources.
 *
 * Note that when using REG_SCRIPT_COMMAND_NEXT there is an implicit push
 * and pop of the context. A chained reg_script inherits the previous
 * context (such as current device), but it does not impact the previous
 * context in any way.
 */

enum {
	REG_SCRIPT_COMMAND_READ,
	REG_SCRIPT_COMMAND_WRITE,
	REG_SCRIPT_COMMAND_RMW,
	REG_SCRIPT_COMMAND_POLL,
	REG_SCRIPT_COMMAND_SET_DEV,
	REG_SCRIPT_COMMAND_NEXT,
	REG_SCRIPT_COMMAND_END,
};

enum {
	REG_SCRIPT_TYPE_PCI,
	REG_SCRIPT_TYPE_IO,
	REG_SCRIPT_TYPE_MMIO,
	REG_SCRIPT_TYPE_RES,
	REG_SCRIPT_TYPE_IOSF,
	REG_SCRIPT_TYPE_MSR,
};

enum {
	REG_SCRIPT_SIZE_8,
	REG_SCRIPT_SIZE_16,
	REG_SCRIPT_SIZE_32,
	REG_SCRIPT_SIZE_64,
};

struct reg_script {
	uint32_t command;
	uint32_t type;
	uint32_t size;
	uint32_t reg;
	uint64_t mask;
	uint64_t value;
	uint32_t timeout;
	union {
		uint32_t id;
		const struct reg_script *next;
		device_t dev;
		unsigned int res_index;
	};
};

/* Internal helper Macros. */

#define _REG_SCRIPT_ENCODE_RAW(cmd_, type_, size_, reg_, \
			  mask_, value_, timeout_, id_)  \
	{ .command = cmd_,     \
	  .type = type_,       \
	  .size = size_,       \
	  .reg = reg_,         \
	  .mask = mask_,       \
	  .value = value_,     \
	  .timeout = timeout_, \
	  .id = id_,           \
	}

#define _REG_SCRIPT_ENCODE_RES(cmd_, type_, res_index_, size_, reg_, \
			       mask_, value_, timeout_)  \
	{ .command = cmd_,         \
	  .type = type_,           \
	  .size = size_,           \
	  .reg = reg_,             \
	  .mask = mask_,           \
	  .value = value_,         \
	  .timeout = timeout_,     \
	  .res_index = res_index_, \
	}

/*
 * PCI
 */

#define REG_SCRIPT_PCI(cmd_, bits_, reg_, mask_, value_, timeout_) \
	_REG_SCRIPT_ENCODE_RAW(REG_SCRIPT_COMMAND_##cmd_,          \
			       REG_SCRIPT_TYPE_PCI,                \
			       REG_SCRIPT_SIZE_##bits_,            \
			       reg_, mask_, value_, timeout_, 0)
#define REG_PCI_READ8(reg_) \
	REG_SCRIPT_PCI(READ, 8, reg_, 0, 0, 0)
#define REG_PCI_READ16(reg_) \
	REG_SCRIPT_PCI(READ, 16, reg_, 0, 0, 0)
#define REG_PCI_READ32(reg_) \
	REG_SCRIPT_PCI(READ, 32, reg_, 0, 0, 0)
#define REG_PCI_WRITE8(reg_, value_) \
	REG_SCRIPT_PCI(WRITE, 8, reg_, 0, value_, 0)
#define REG_PCI_WRITE16(reg_, value_) \
	REG_SCRIPT_PCI(WRITE, 16, reg_, 0, value_, 0)
#define REG_PCI_WRITE32(reg_, value_) \
	REG_SCRIPT_PCI(WRITE, 32, reg_, 0, value_, 0)
#define REG_PCI_RMW8(reg_, mask_, value_) \
	REG_SCRIPT_PCI(RMW, 8, reg_, mask_, value_, 0)
#define REG_PCI_RMW16(reg_, mask_, value_) \
	REG_SCRIPT_PCI(RMW, 16, reg_, mask_, value_, 0)
#define REG_PCI_RMW32(reg_, mask_, value_) \
	REG_SCRIPT_PCI(RMW, 32, reg_, mask_, value_, 0)
#define REG_PCI_OR8(reg_, value_) \
	REG_SCRIPT_PCI(RMW, 8, reg_, 0xff, value_, 0)
#define REG_PCI_OR16(reg_, value_) \
	REG_SCRIPT_PCI(RMW, 16, reg_, 0xffff, value_, 0)
#define REG_PCI_OR32(reg_, value_) \
	REG_SCRIPT_PCI(RMW, 32, reg_, 0xffffffff, value_, 0)
#define REG_PCI_POLL8(reg_, mask_, value_, timeout_) \
	REG_SCRIPT_PCI(POLL, 8, reg_, mask_, value_, timeout_)
#define REG_PCI_POLL16(reg_, mask_, value_, timeout_) \
	REG_SCRIPT_PCI(POLL, 16, reg_, mask_, value_, timeout_)
#define REG_PCI_POLL32(reg_, mask_, value_, timeout_) \
	REG_SCRIPT_PCI(POLL, 32, reg_, mask_, value_, timeout_)

/*
 * Legacy IO
 */

#define REG_SCRIPT_IO(cmd_, bits_, reg_, mask_, value_, timeout_) \
	_REG_SCRIPT_ENCODE_RAW(REG_SCRIPT_COMMAND_##cmd_,         \
			       REG_SCRIPT_TYPE_IO,                \
			       REG_SCRIPT_SIZE_##bits_,           \
			       reg_, mask_, value_, timeout_, 0)
#define REG_IO_READ8(reg_) \
	REG_SCRIPT_IO(READ, 8, reg_, 0, 0, 0)
#define REG_IO_READ16(reg_) \
	REG_SCRIPT_IO(READ, 16, reg_, 0, 0, 0)
#define REG_IO_READ32(reg_) \
	REG_SCRIPT_IO(READ, 32, reg_, 0, 0, 0)
#define REG_IO_WRITE8(reg_, value_) \
	REG_SCRIPT_IO(WRITE, 8, reg_, 0, value_, 0)
#define REG_IO_WRITE16(reg_, value_) \
	REG_SCRIPT_IO(WRITE, 16, reg_, 0, value_, 0)
#define REG_IO_WRITE32(reg_, value_) \
	REG_SCRIPT_IO(WRITE, 32, reg_, 0, value_, 0)
#define REG_IO_RMW8(reg_, mask_, value_) \
	REG_SCRIPT_IO(RMW, 8, reg_, mask_, value_, 0)
#define REG_IO_RMW16(reg_, mask_, value_) \
	REG_SCRIPT_IO(RMW, 16, reg_, mask_, value_, 0)
#define REG_IO_RMW32(reg_, mask_, value_) \
	REG_SCRIPT_IO(RMW, 32, reg_, mask_, value_, 0)
#define REG_IO_OR8(reg_, value_) \
	REG_IO_RMW8(reg_, 0xff, value_)
#define REG_IO_OR16(reg_, value_) \
	REG_IO_RMW16(reg_, 0xffff, value_)
#define REG_IO_OR32(reg_, value_) \
	REG_IO_RMW32(reg_, 0xffffffff, value_)
#define REG_IO_POLL8(reg_, mask_, value_, timeout_) \
	REG_SCRIPT_IO(POLL, 8, reg_, mask_, value_, timeout_)
#define REG_IO_POLL16(reg_, mask_, value_, timeout_) \
	REG_SCRIPT_IO(POLL, 16, reg_, mask_, value_, timeout_)
#define REG_IO_POLL32(reg_, mask_, value_, timeout_) \
	REG_SCRIPT_IO(POLL, 32, reg_, mask_, value_, timeout_)

/*
 * Memory Mapped IO
 */

#define REG_SCRIPT_MMIO(cmd_, bits_, reg_, mask_, value_, timeout_) \
	_REG_SCRIPT_ENCODE_RAW(REG_SCRIPT_COMMAND_##cmd_,           \
			       REG_SCRIPT_TYPE_MMIO,                \
			       REG_SCRIPT_SIZE_##bits_,             \
			       reg_, mask_, value_, timeout_, 0)
#define REG_MMIO_READ8(reg_) \
	REG_SCRIPT_MMIO(READ, 8, reg_, 0, 0, 0)
#define REG_MMIO_READ16(reg_) \
	REG_SCRIPT_MMIO(READ, 16, reg_, 0, 0, 0)
#define REG_MMIO_READ32(reg_) \
	REG_SCRIPT_MMIO(READ, 32, reg_, 0, 0, 0)
#define REG_MMIO_WRITE8(reg_, value_) \
	REG_SCRIPT_MMIO(WRITE, 8, reg_, 0, value_, 0)
#define REG_MMIO_WRITE16(reg_, value_) \
	REG_SCRIPT_MMIO(WRITE, 16, reg_, 0, value_, 0)
#define REG_MMIO_WRITE32(reg_, value_) \
	REG_SCRIPT_MMIO(WRITE, 32, reg_, 0, value_, 0)
#define REG_MMIO_RMW8(reg_, mask_, value_) \
	REG_SCRIPT_MMIO(RMW, 8, reg_, mask_, value_, 0)
#define REG_MMIO_RMW16(reg_, mask_, value_) \
	REG_SCRIPT_MMIO(RMW, 16, reg_, mask_, value_, 0)
#define REG_MMIO_RMW32(reg_, mask_, value_) \
	REG_SCRIPT_MMIO(RMW, 32, reg_, mask_, value_, 0)
#define REG_MMIO_OR8(reg_, value_) \
	REG_MMIO_RMW8(reg_, 0xff, value_)
#define REG_MMIO_OR16(reg_, value_) \
	REG_MMIO_RMW16(reg_, 0xffff, value_)
#define REG_MMIO_OR32(reg_, value_) \
	REG_MMIO_RMW32(reg_, 0xffffffff, value_)
#define REG_MMIO_POLL8(reg_, mask_, value_, timeout_) \
	REG_SCRIPT_MMIO(POLL, 8, reg_, mask_, value_, timeout_)
#define REG_MMIO_POLL16(reg_, mask_, value_, timeout_) \
	REG_SCRIPT_MMIO(POLL, 16, reg_, mask_, value_, timeout_)
#define REG_MMIO_POLL32(reg_, mask_, value_, timeout_) \
	REG_SCRIPT_MMIO(POLL, 32, reg_, mask_, value_, timeout_)

/*
 * Access through a device's resource such as a Base Address Register (BAR)
 */

#define REG_SCRIPT_RES(cmd_, bits_, bar_, reg_, mask_, value_, timeout_) \
	_REG_SCRIPT_ENCODE_RES(REG_SCRIPT_COMMAND_##cmd_,                \
			       REG_SCRIPT_TYPE_RES, bar_,                \
			       REG_SCRIPT_SIZE_##bits_,                  \
			       reg_, mask_, value_, timeout_)
#define REG_RES_READ8(bar_, reg_) \
	REG_SCRIPT_RES(READ, 8, bar_, reg_, 0, 0, 0)
#define REG_RES_READ16(bar_, reg_) \
	REG_SCRIPT_RES(READ, 16, bar_, reg_, 0, 0, 0)
#define REG_RES_READ32(bar_, reg_) \
	REG_SCRIPT_RES(READ, 32, bar_, reg_, 0, 0, 0)
#define REG_RES_WRITE8(bar_, reg_, value_) \
	REG_SCRIPT_RES(WRITE, 8, bar_, reg_, 0, value_, 0)
#define REG_RES_WRITE16(bar_, reg_, value_) \
	REG_SCRIPT_RES(WRITE, 16, bar_, reg_, 0, value_, 0)
#define REG_RES_WRITE32(bar_, reg_, value_) \
	REG_SCRIPT_RES(WRITE, 32, bar_, reg_, 0, value_, 0)
#define REG_RES_RMW8(bar_, reg_, mask_, value_) \
	REG_SCRIPT_RES(RMW, 8, bar_, reg_, mask_, value_, 0)
#define REG_RES_RMW16(bar_, reg_, mask_, value_) \
	REG_SCRIPT_RES(RMW, 16, bar_, reg_, mask_, value_, 0)
#define REG_RES_RMW32(bar_, reg_, mask_, value_) \
	REG_SCRIPT_RES(RMW, 32, bar_, reg_, mask_, value_, 0)
#define REG_RES_OR8(bar_, reg_, value_) \
	REG_RES_RMW8(bar_, reg_, 0xff, value_)
#define REG_RES_OR16(bar_, reg_, value_) \
	REG_RES_RMW16(bar_, reg_, 0xffff, value_)
#define REG_RES_OR32(bar_, reg_, value_) \
	REG_RES_RMW32(bar_, reg_, 0xffffffff, value_)
#define REG_RES_POLL8(bar_, reg_, mask_, value_, timeout_) \
	REG_SCRIPT_RES(POLL, 8, bar_, reg_, mask_, value_, timeout_)
#define REG_RES_POLL16(bar_, reg_, mask_, value_, timeout_) \
	REG_SCRIPT_RES(POLL, 16, bar_, reg_, mask_, value_, timeout_)
#define REG_RES_POLL32(bar_, reg_, mask_, value_, timeout_) \
	REG_SCRIPT_RES(POLL, 32, bar_, reg_, mask_, value_, timeout_)

/*
 * IO Sideband Function
 */

#define REG_SCRIPT_IOSF(cmd_, unit_, reg_, mask_, value_, timeout_) \
	_REG_SCRIPT_ENCODE_RAW(REG_SCRIPT_COMMAND_##cmd_,      \
			       REG_SCRIPT_TYPE_IOSF,           \
			       REG_SCRIPT_SIZE_32,             \
			       reg_, mask_, value_, timeout_, unit_)
#define REG_IOSF_READ(unit_, reg_) \
	REG_SCRIPT_IOSF(READ, unit_, reg_, 0, 0, 0)
#define REG_IOSF_WRITE(unit_, reg_, value_) \
	REG_SCRIPT_IOSF(WRITE, unit_, reg_, 0, value_, 0)
#define REG_IOSF_RMW(unit_, reg_, mask_, value_) \
	REG_SCRIPT_IOSF(RMW, unit_, reg_, mask_, value_, 0)
#define REG_IOSF_OR(unit_, reg_, value_) \
	REG_IOSF_RMW(unit_, reg_, 0xffffffff, value_)
#define REG_IOSF_POLL(unit_, reg_, mask_, value_, timeout_) \
	REG_SCRIPT_IOSF(POLL, unit_, reg_, mask_, value_, timeout_)

/*
 * CPU Model Specific Register
 */

#define REG_SCRIPT_MSR(cmd_, reg_, mask_, value_, timeout_) \
	_REG_SCRIPT_ENCODE_RAW(REG_SCRIPT_COMMAND_##cmd_,   \
			       REG_SCRIPT_TYPE_MSR,         \
			       REG_SCRIPT_SIZE_64,          \
			       reg_, mask_, value_, timeout_, 0)
#define REG_MSR_READ(reg_) \
	REG_SCRIPT_MSR(READ, reg_, 0, 0, 0)
#define REG_MSR_WRITE(reg_, value_) \
	REG_SCRIPT_MSR(WRITE, reg_, 0, value_, 0)
#define REG_MSR_RMW(reg_, mask_, value_) \
	REG_SCRIPT_MSR(RMW, reg_, mask_, value_, 0)
#define REG_MSR_OR(reg_, value_) \
	REG_MSR_RMW(reg_, -1ULL, value_)
#define REG_MSR_POLL(reg_, mask_, value_, timeout_) \
	REG_SCRIPT_MSR(POLL, reg_, mask_, value_, timeout_)

/*
 * Chain to another table.
 */
#define REG_SCRIPT_NEXT(next_)                \
	{ .command = REG_SCRIPT_COMMAND_NEXT, \
	  .next = next_,                      \
	}

/*
 * Set current device
 */
#define REG_SCRIPT_SET_DEV(dev_)                 \
	{ .command = REG_SCRIPT_COMMAND_SET_DEV, \
	  .dev = dev_,                           \
	}

/*
 * Last script entry. All tables need to end with REG_SCRIPT_END.
 */
#define REG_SCRIPT_END \
	_REG_SCRIPT_ENCODE_RAW(REG_SCRIPT_COMMAND_END, 0, 0, 0, 0, 0, 0, 0)

void reg_script_run(const struct reg_script *script);
void reg_script_run_on_dev(device_t dev, const struct reg_script *step);

#endif /* REG_SCRIPT_H */
