Helpers for memory mapped IO.

This logic can be shared between architecture and drivers. The addresses
for memory mapped IO have an opaque type to avoid accidental misuse and
arrays have an associated size so overflows can be detected. Accessor
functions make the use if IO clear and optionally add memory barriers.

Change-Id: I59d82bd2ee7d41e0ab61438ca0ccf10b46f23c54
diff --git a/inc/hf/io.h b/inc/hf/io.h
new file mode 100644
index 0000000..12bc440
--- /dev/null
+++ b/inc/hf/io.h
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2019 The Hafnium Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "hf/arch/barriers.h"
+
+#include "hf/assert.h"
+
+/* Opaque types for different sized fields of memory mapped IO. */
+
+typedef struct {
+	volatile uint8_t *ptr;
+} io8_t;
+
+typedef struct {
+	volatile uint16_t *ptr;
+} io16_t;
+
+typedef struct {
+	volatile uint32_t *ptr;
+} io32_t;
+
+typedef struct {
+	volatile uint64_t *ptr;
+} io64_t;
+
+typedef struct {
+	volatile uint8_t *base;
+	size_t count;
+} io8_array_t;
+
+typedef struct {
+	volatile uint16_t *base;
+	size_t count;
+} io16_array_t;
+
+typedef struct {
+	volatile uint32_t *base;
+	size_t count;
+} io32_array_t;
+
+typedef struct {
+	volatile uint64_t *base;
+	size_t count;
+} io64_array_t;
+
+/* Contructors for literals. */
+
+#define IO8_C(addr) ((io8_t){.ptr = (volatile uint8_t *)(addr)})
+#define IO16_C(addr) ((io16_t){.ptr = (volatile uint16_t *)(addr)})
+#define IO32_C(addr) ((io32_t){.ptr = (volatile uint32_t *)(addr)})
+#define IO64_C(addr) ((io64_t){.ptr = (volatile uint64_t *)(addr)})
+
+#define IO8_ARRAY_C(addr, cnt) \
+	((io8_array_t){.base = (volatile uint8_t *)(addr), .count = (cnt)})
+#define IO16_ARRAY_C(addr, cnt) \
+	((io16_array_t){.base = (volatile uint16_t *)(addr), .count = (cnt)})
+#define IO32_ARRAY_C(addr, cnt) \
+	((io32_array_t){.base = (volatile uint32_t *)(addr), .count = (cnt)})
+#define IO64_ARRAY_C(addr, cnt) \
+	((io64_array_t){.base = (volatile uint64_t *)(addr), .count = (cnt)})
+
+/** Read from memory-mapped IO. */
+
+static inline uint8_t io_read8(io8_t io)
+{
+	return *io.ptr;
+}
+
+static inline uint16_t io_read16(io16_t io)
+{
+	return *io.ptr;
+}
+
+static inline uint32_t io_read32(io32_t io)
+{
+	return *io.ptr;
+}
+
+static inline uint64_t io_read64(io64_t io)
+{
+	return *io.ptr;
+}
+
+static inline uint8_t io_read8_array(io8_array_t io, size_t n)
+{
+	assert(n < io.count);
+	return io.base[n];
+}
+
+static inline uint16_t io_read16_array(io16_array_t io, size_t n)
+{
+	assert(n < io.count);
+	return io.base[n];
+}
+
+static inline uint32_t io_read32_array(io32_array_t io, size_t n)
+{
+	assert(n < io.count);
+	return io.base[n];
+}
+
+static inline uint64_t io_read64_array(io64_array_t io, size_t n)
+{
+	assert(n < io.count);
+	return io.base[n];
+}
+
+/**
+ * Read from memory-mapped IO with memory barrier.
+ *
+ * The read is ordered before subsequent memory accesses.
+ */
+
+static inline uint8_t io_read8_mb(io8_t io)
+{
+	uint8_t v = io_read8(io);
+
+	dsb();
+	return v;
+}
+
+static inline uint16_t io_read16_mb(io16_t io)
+{
+	uint16_t v = io_read16(io);
+
+	dsb();
+	return v;
+}
+
+static inline uint32_t io_read32_mb(io32_t io)
+{
+	uint32_t v = io_read32(io);
+
+	dsb();
+	return v;
+}
+
+static inline uint64_t io_read64_mb(io64_t io)
+{
+	uint64_t v = io_read64(io);
+
+	dsb();
+	return v;
+}
+
+static inline uint8_t io_read8_array_mb(io8_array_t io, size_t n)
+{
+	uint8_t v = io_read8_array(io, n);
+
+	dsb();
+	return v;
+}
+
+static inline uint16_t io_read16_array_mb(io16_array_t io, size_t n)
+{
+	uint16_t v = io_read16_array(io, n);
+
+	dsb();
+	return v;
+}
+
+static inline uint32_t io_read32_array_mb(io32_array_t io, size_t n)
+{
+	uint32_t v = io_read32_array(io, n);
+
+	dsb();
+	return v;
+}
+
+static inline uint64_t io_read64_array_mb(io64_array_t io, size_t n)
+{
+	uint64_t v = io_read64_array(io, n);
+
+	dsb();
+	return v;
+}
+
+/* Write to memory-mapped IO. */
+
+static inline void io_write8(io8_t io, uint8_t v)
+{
+	*io.ptr = v;
+}
+
+static inline void io_write16(io16_t io, uint16_t v)
+{
+	*io.ptr = v;
+}
+
+static inline void io_write32(io32_t io, uint32_t v)
+{
+	*io.ptr = v;
+}
+
+static inline void io_write64(io64_t io, uint64_t v)
+{
+	*io.ptr = v;
+}
+
+static inline void io_write8_array(io8_array_t io, size_t n, uint8_t v)
+{
+	assert(n < io.count);
+	io.base[n] = v;
+}
+
+static inline void io_write16_array(io16_array_t io, size_t n, uint16_t v)
+{
+	assert(n < io.count);
+	io.base[n] = v;
+}
+
+static inline void io_write32_array(io32_array_t io, size_t n, uint32_t v)
+{
+	assert(n < io.count);
+	io.base[n] = v;
+}
+
+static inline void io_write64_array(io64_array_t io, size_t n, uint64_t v)
+{
+	assert(n < io.count);
+	io.base[n] = v;
+}
+
+/*
+ * Write to memory-mapped IO with memory barrier.
+ *
+ * The write is ordered after previous memory accesses.
+ */
+
+static inline void io_write8_mb(io8_t io, uint8_t v)
+{
+	dsb();
+	io_write8(io, v);
+}
+
+static inline void io_write16_mb(io16_t io, uint16_t v)
+{
+	dsb();
+	io_write16(io, v);
+}
+
+static inline void io_write32_mb(io32_t io, uint32_t v)
+{
+	dsb();
+	io_write32(io, v);
+}
+
+static inline void io_write64_mb(io64_t io, uint64_t v)
+{
+	dsb();
+	io_write64(io, v);
+}
+
+static inline void io_write8_array_mb(io8_array_t io, size_t n, uint8_t v)
+{
+	dsb();
+	io_write8_array(io, n, v);
+}
+
+static inline void io_write16_array_mb(io16_array_t io, size_t n, uint16_t v)
+{
+	dsb();
+	io_write16_array(io, n, v);
+}
+
+static inline void io_write32_array_mb(io32_array_t io, size_t n, uint32_t v)
+{
+	dsb();
+	io_write32_array(io, n, v);
+}
+
+static inline void io_write64_array_mb(io64_array_t io, size_t n, uint64_t v)
+{
+	dsb();
+	io_write64_array(io, n, v);
+}