README.md
Rendering markdown...
// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2018,2019 IBM Corp.
#include "ahb.h"
#include "debug.h"
#include "devmem.h"
#include "ilpc.h"
#include "l2a.h"
#include "log.h"
#include "p2a.h"
#include "priv.h"
#include <assert.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
const char *ahb_interface_names[ahb_max_interfaces] = {
[ahb_ilpcb] = "iLPC2AHB",
[ahb_l2ab] = "LPC2AHB",
[ahb_p2ab] = "P2A",
[ahb_debug] = "Debug UART",
[ahb_devmem] = "devmem",
};
static void ahb_notify_bridge(struct ahb *ctx)
{
logi("Initialised %s AHB interface\n", ahb_interface_names[ctx->bridge]);
}
struct ahb *ahb_use(struct ahb *ctx, enum ahb_bridge type, void *bridge)
{
ctx->bridge = type;
if (type == ahb_ilpcb)
ctx->ilpcb = bridge;
else if (type == ahb_l2ab)
ctx->l2ab = bridge;
else if (type == ahb_p2ab)
ctx->p2ab = bridge;
else if (type == ahb_debug)
ctx->debug = bridge;
else if (type == ahb_devmem)
ctx->devmem = bridge;
else
assert(false);
return ctx;
}
int ahb_init(struct ahb *ctx, enum ahb_bridge type, ...)
{
int rc;
if (type == ahb_ilpcb) {
ctx->ilpcb = malloc(sizeof(*ctx->ilpcb));
rc = ilpcb_init(ctx->ilpcb);
if (rc < 0) {
free(ctx->ilpcb);
return rc;
}
} else if (type == ahb_l2ab) {
ctx->l2ab = malloc(sizeof(*ctx->l2ab));
rc = l2ab_init(ctx->l2ab);
if (rc < 0) {
free(ctx->l2ab);
return rc;
}
} else if (type == ahb_p2ab) {
ctx->p2ab = malloc(sizeof(*ctx->p2ab));
rc = p2ab_init(ctx->p2ab, AST_PCI_VID, AST_PCI_DID_VGA);
if (rc < 0) {
free(ctx->p2ab);
return rc;
}
} else if (type == ahb_debug) {
va_list args;
ctx->debug = malloc(sizeof(*ctx->debug));
va_start(args, type);
rc = debug_init_v(ctx->debug, args);
va_end(args);
if (rc < 0) {
free(ctx->debug);
return rc;
}
rc = debug_enter(ctx->debug);
if (rc < 0) {
debug_destroy(ctx->debug);
free(ctx->debug);
return rc;
}
} else if (type == ahb_devmem) {
ctx->devmem = malloc(sizeof(*ctx->devmem));
logi("Initialising devmem interface\n");
rc = devmem_init(ctx->devmem);
if (rc < 0) {
loge("devmem_init failed: %d\n", rc);
free(ctx->devmem);
return rc;
}
} else
return -EINVAL;
ctx->bridge = type;
ahb_notify_bridge(ctx);
return rc;
}
int ahb_cleanup(struct ahb *ctx)
{
if (ctx->bridge == ahb_ilpcb || ctx->bridge == ahb_l2ab ||
ctx->bridge == ahb_p2ab || ctx->bridge == ahb_devmem) {
ahb_destroy(ctx);
} else if (ctx->bridge == ahb_debug) {
debug_exit(ctx->debug);
debug_destroy(ctx->debug);
} else
assert(false);
return 0;
}
int ahb_destroy(struct ahb *ctx)
{
if (ctx->bridge == ahb_ilpcb) {
ilpcb_destroy(ctx->ilpcb);
free(ctx->ilpcb);
} else if (ctx->bridge == ahb_l2ab) {
l2ab_destroy(ctx->l2ab);
free(ctx->l2ab);
} else if (ctx->bridge == ahb_p2ab) {
p2ab_destroy(ctx->p2ab);
free(ctx->p2ab);
} else if (ctx->bridge == ahb_debug) {
debug_exit(ctx->debug);
debug_destroy(ctx->debug);
free(ctx->debug);
} else if (ctx->bridge == ahb_devmem) {
devmem_destroy(ctx->devmem);
free(ctx->devmem);
} else
assert(false);
return 0;
}
ssize_t ahb_read(struct ahb *ctx, uint32_t phys, void *buf, size_t len)
{
if (ctx->bridge == ahb_ilpcb)
return ilpcb_read(ctx->ilpcb, phys, buf, len);
else if (ctx->bridge == ahb_l2ab)
return l2ab_read(ctx->l2ab, phys, buf, len);
else if (ctx->bridge == ahb_p2ab)
return p2ab_read(ctx->p2ab, phys, buf, len);
else if (ctx->bridge == ahb_debug)
return debug_read(ctx->debug, phys, buf, len);
else if (ctx->bridge == ahb_devmem)
return devmem_read(ctx->devmem, phys, buf, len);
return -ENOTSUP;
}
ssize_t ahb_write(struct ahb *ctx, uint32_t phys, const void *buf, size_t len)
{
if (ctx->bridge == ahb_ilpcb)
return ilpcb_write(ctx->ilpcb, phys, buf, len);
else if (ctx->bridge == ahb_l2ab)
return l2ab_write(ctx->l2ab, phys, buf, len);
else if (ctx->bridge == ahb_p2ab)
return p2ab_write(ctx->p2ab, phys, buf, len);
else if (ctx->bridge == ahb_debug)
return debug_write(ctx->debug, phys, buf, len);
else if (ctx->bridge == ahb_devmem)
return devmem_write(ctx->devmem, phys, buf, len);
return -ENOTSUP;
}
int ahb_readl(struct ahb *ctx, uint32_t phys, uint32_t *val)
{
if (ctx->bridge == ahb_ilpcb)
return ilpcb_readl(ctx->ilpcb, phys, val);
else if (ctx->bridge == ahb_l2ab)
return l2ab_readl(ctx->l2ab, phys, val);
else if (ctx->bridge == ahb_p2ab)
return p2ab_readl(ctx->p2ab, phys, val);
else if (ctx->bridge == ahb_debug)
return debug_readl(ctx->debug, phys, val);
else if (ctx->bridge == ahb_devmem)
return devmem_readl(ctx->devmem, phys, val);
return -ENOTSUP;
}
int ahb_writel(struct ahb *ctx, uint32_t phys, uint32_t val)
{
if (ctx->bridge == ahb_ilpcb)
return ilpcb_writel(ctx->ilpcb, phys, val);
else if (ctx->bridge == ahb_l2ab)
return l2ab_writel(ctx->l2ab, phys, val);
else if (ctx->bridge == ahb_p2ab)
return p2ab_writel(ctx->p2ab, phys, val);
else if (ctx->bridge == ahb_debug)
return debug_writel(ctx->debug, phys, val);
else if (ctx->bridge == ahb_devmem)
return devmem_writel(ctx->devmem, phys, val);
return -ENOTSUP;
}
#define AHB_CHUNK (1 << 20)
ssize_t ahb_siphon_in(struct ahb *ctx, uint32_t phys, size_t len, int outfd)
{
ssize_t ingress, egress, remaining;
void *chunk, *cursor;
int rc = 0;
if (!len)
return 0;
chunk = malloc(AHB_CHUNK);
if (!chunk)
return -errno;
remaining = len;
do {
ingress = remaining > AHB_CHUNK ? AHB_CHUNK : remaining;
ingress = ahb_read(ctx, phys, chunk, ingress);
if (ingress < 0) { rc = ingress; goto done; }
phys += ingress;
remaining -= ingress;
cursor = chunk;
while (ingress) {
egress = write(outfd, cursor, ingress);
if (egress == -1) { rc = -errno; goto done; }
cursor += egress;
ingress -= egress;
}
fprintf(stderr, ".");
} while (remaining);
done:
fprintf(stderr, "\n");
free(chunk);
return rc;
}
ssize_t ahb_siphon_out(struct ahb *ctx, uint32_t phys, int infd)
{
ssize_t ingress, egress;
void *chunk;
int rc = 0;
chunk = malloc(AHB_CHUNK);
if (!chunk)
return -errno;
while ((ingress = read(infd, chunk, AHB_CHUNK))) {
if (ingress < 0) { rc = -errno; goto done; }
egress = ahb_write(ctx, phys, chunk, ingress);
if (egress < 0) { rc = egress; goto done; }
phys += ingress;
fprintf(stderr, ".");
}
done:
fprintf(stderr, "\n");
free(chunk);
return rc;
}