#include <sys/param.h>
#include <sys/bus.h>
#include <sys/malloc.h>
#include <sys/rman.h>
#include <machine/bus.h>
#include <dev/bhnd/bhnd.h>
#include "bhnd_nvram_private.h"
#include "bhnd_nvram_io.h"
#include "bhnd_nvram_iovar.h"
struct bhnd_nvram_iores {
struct bhnd_nvram_io io;
struct bhnd_resource *res;
size_t offset;
size_t size;
u_int bus_width;
};
BHND_NVRAM_IOPS_DEFN(iores);
struct bhnd_nvram_io *
bhnd_nvram_iores_new(struct bhnd_resource *r, bus_size_t offset,
bus_size_t size, u_int bus_width)
{
struct bhnd_nvram_iores *iores;
rman_res_t r_start, r_size;
switch (bus_width) {
case 1:
case 2:
case 4:
break;
default:
BHND_NV_LOG("invalid bus width %u\n", bus_width);
return (NULL);
}
if (size > SIZE_MAX || offset > SIZE_MAX) {
BHND_NV_LOG("offset %#jx+%#jx exceeds SIZE_MAX\n",
(uintmax_t)offset, (uintmax_t)offset);
return (NULL);
}
if (size > BUS_SPACE_MAXSIZE || offset > BUS_SPACE_MAXSIZE)
{
BHND_NV_LOG("offset %#jx+%#jx exceeds BUS_SPACE_MAXSIZE\n",
(uintmax_t)offset, (uintmax_t)offset);
return (NULL);
}
r_size = rman_get_size(r->res);
r_start = rman_get_start(r->res);
if (r_size < offset || r_size < size || r_size - size < offset)
return (NULL);
if ((r_start + offset) % bus_width != 0) {
BHND_NV_LOG("base address %#jx+%#jx not aligned to bus width "
"%u\n", (uintmax_t)r_start, (uintmax_t)offset, bus_width);
return (NULL);
}
if (size % bus_width != 0) {
BHND_NV_LOG("size %#jx not aligned to bus width %u\n",
(uintmax_t)size, bus_width);
return (NULL);
}
iores = malloc(sizeof(*iores), M_BHND_NVRAM, M_WAITOK);
iores->io.iops = &bhnd_nvram_iores_ops;
iores->res = r;
iores->offset = offset;
iores->size = size;
iores->bus_width = bus_width;
return (&iores->io);
}
static void
bhnd_nvram_iores_free(struct bhnd_nvram_io *io)
{
free(io, M_BHND_NVRAM);
}
static size_t
bhnd_nvram_iores_getsize(struct bhnd_nvram_io *io)
{
struct bhnd_nvram_iores *iores = (struct bhnd_nvram_iores *)io;
return (iores->size);
}
static int
bhnd_nvram_iores_setsize(struct bhnd_nvram_io *io, size_t size)
{
return (ENODEV);
}
static int
bhnd_nvram_iores_read_ptr(struct bhnd_nvram_io *io, size_t offset,
const void **ptr, size_t nbytes, size_t *navail)
{
return (ENODEV);
}
static int
bhnd_nvram_iores_write_ptr(struct bhnd_nvram_io *io, size_t offset,
void **ptr, size_t nbytes, size_t *navail)
{
return (ENODEV);
}
static int
bhnd_nvram_iores_validate_req(struct bhnd_nvram_iores *iores, size_t offset,
size_t *nbytes)
{
if (offset > iores->size)
return (ENXIO);
if (offset == iores->size) {
*nbytes = 0;
return (0);
}
if (offset % iores->bus_width != 0)
return (EFAULT);
*nbytes = ummin(*nbytes, iores->size - offset);
if (*nbytes < iores->bus_width && *nbytes % iores->bus_width != 0)
return (EFAULT);
return (0);
}
static int
bhnd_nvram_iores_read(struct bhnd_nvram_io *io, size_t offset, void *buffer,
size_t nbytes)
{
struct bhnd_nvram_iores *iores;
bus_size_t r_offset;
size_t navail;
int error;
iores = (struct bhnd_nvram_iores *)io;
navail = nbytes;
if ((error = bhnd_nvram_iores_validate_req(iores, offset, &navail)))
return (error);
if (navail < nbytes)
return (ENXIO);
if (nbytes == 0)
return (0);
r_offset = iores->offset + offset;
switch (iores->bus_width) {
case 1:
bhnd_bus_read_region_stream_1(iores->res, r_offset, buffer,
nbytes);
break;
case 2:
bhnd_bus_read_region_stream_2(iores->res, r_offset, buffer,
nbytes / 2);
break;
case 4:
bhnd_bus_read_region_stream_4(iores->res, r_offset, buffer,
nbytes / 4);
break;
default:
panic("unreachable!");
}
return (0);
}
static int
bhnd_nvram_iores_write(struct bhnd_nvram_io *io, size_t offset,
void *buffer, size_t nbytes)
{
struct bhnd_nvram_iores *iores;
size_t navail;
bus_size_t r_offset;
int error;
iores = (struct bhnd_nvram_iores *)io;
navail = nbytes;
if ((error = bhnd_nvram_iores_validate_req(iores, offset, &navail)))
return (error);
if (navail < nbytes)
return (ENXIO);
r_offset = iores->offset + offset;
switch (iores->bus_width) {
case 1:
bhnd_bus_write_region_stream_1(iores->res, r_offset, buffer,
nbytes);
break;
case 2:
bhnd_bus_write_region_stream_2(iores->res, r_offset, buffer,
nbytes / 2);
break;
case 4:
bhnd_bus_write_region_stream_4(iores->res, r_offset, buffer,
nbytes / 4);
break;
default:
panic("unreachable!");
}
return (0);
}