/*
 * 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-2012 Yuichi Watanabe
 */

#ifndef __IO_IO_H
#define __IO_IO_H

#include <core/types.h>

#define write8(addr, val) do { *(volatile u8*)(addr) = (u8)(val); } while(0)
#define write16(addr, val) do { *(volatile u16*)(addr) = (u16)(val); } while(0)
#define write32(addr, val) do { *(volatile u32*)(addr) = (u32)(val); } while(0)
#define write64(addr, val) do { *(volatile u64*)(addr) = (u64)(val); } while(0)

#define read8(addr) (*(volatile u8*)(addr))
#define read16(addr) (*(volatile u16*)(addr))
#define read32(addr) (*(volatile u32*)(addr))
#define read64(addr) (*(volatile u64*)(addr))

static inline void in8 (ioport_t port, u8 *data)
{ __asm__ volatile ("inb %%dx, %%al" : "=a" (*data) : "d" (port)); }
static inline void in16 (ioport_t port, u16 *data)
{ __asm__ volatile ("inw %%dx, %%ax" : "=a" (*data) : "d" (port)); }
static inline void in32 (ioport_t port, u32 *data)
{ __asm__ volatile ("inl %%dx, %%eax" : "=a" (*data) : "d" (port)); }
static inline void ins8 (ioport_t port, u8 *buf, u32 count)
{ __asm__ volatile ("cld; rep insb" : "=c" (count), "=D" (buf)
		: "c" (count), "d" (port), "D" (buf)); }
static inline void ins16 (ioport_t port, u16 *buf, u32 count)
{ __asm__ volatile ("cld; rep insw" : "=c" (count), "=D" (buf)
		: "c" (count), "d" (port), "D" (buf)); }
static inline void ins32 (ioport_t port, u32 *buf, u32 count)
{ __asm__ volatile ("cld; rep insl" : "=c" (count), "=D" (buf)
		: "c" (count), "d" (port), "D" (buf)); }
static inline void insn (ioport_t port, void *buf, int unit_size, u32 total_size)
{
	if (unit_size == 4)
		ins32 (port, buf, total_size/4);
	else if (unit_size == 2)
		ins16 (port, buf, total_size/2);
	else if (unit_size == 1)
		ins8 (port, buf, total_size);
}

static inline void out8 (ioport_t port, u8 data)
{ __asm__ volatile ("outb %%al, %%dx" : : "a" (data), "d" (port)); }
static inline void out16 (ioport_t port, u16 data)
{ __asm__ volatile ("outw %%ax, %%dx" : : "a" (data), "d" (port)); }
static inline void out32 (ioport_t port, u32 data)
{ __asm__ volatile ("outl %%eax, %%dx" : : "a" (data), "d" (port)); }
static inline void outs8 (ioport_t port, u8 *buf, u32 count)
{ __asm__ volatile ("cld; rep outsb" : "=c" (count), "=S" (buf)
		: "c" (count), "d" (port), "S" (buf)); }
static inline void outs16 (ioport_t port, u16 *buf, u32 count)
{ __asm__ volatile ("cld; rep outsw" : "=c" (count), "=S" (buf)
		: "c" (count), "d" (port), "S" (buf)); }
static inline void outs32 (ioport_t port, u32 *buf, u32 count)
{ __asm__ volatile ("cld; rep outsl" : "=c" (count), "=S" (buf)
		: "c" (count), "d" (port), "S" (buf)); }
static inline void outsn (ioport_t port, void *buf, int unit_size, u32 total_size)
{
	if (unit_size == 4)
		outs32 (port, buf, total_size/4);
	else if (unit_size == 2)
		outs16 (port, buf, total_size/2);
	else if (unit_size == 1)
		outs8 (port, buf, total_size);
}

#endif /* __IO_IO_H */
