177cb4d3eSLandon J. Fuller /*-
277cb4d3eSLandon J. Fuller * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
377cb4d3eSLandon J. Fuller * All rights reserved.
477cb4d3eSLandon J. Fuller *
577cb4d3eSLandon J. Fuller * Redistribution and use in source and binary forms, with or without
677cb4d3eSLandon J. Fuller * modification, are permitted provided that the following conditions
777cb4d3eSLandon J. Fuller * are met:
877cb4d3eSLandon J. Fuller * 1. Redistributions of source code must retain the above copyright
977cb4d3eSLandon J. Fuller * notice, this list of conditions and the following disclaimer,
1077cb4d3eSLandon J. Fuller * without modification.
1177cb4d3eSLandon J. Fuller * 2. Redistributions in binary form must reproduce at minimum a disclaimer
1277cb4d3eSLandon J. Fuller * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
1377cb4d3eSLandon J. Fuller * redistribution must be conditioned upon including a substantially
1477cb4d3eSLandon J. Fuller * similar Disclaimer requirement for further binary redistribution.
1577cb4d3eSLandon J. Fuller *
1677cb4d3eSLandon J. Fuller * NO WARRANTY
1777cb4d3eSLandon J. Fuller * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1877cb4d3eSLandon J. Fuller * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1977cb4d3eSLandon J. Fuller * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
2077cb4d3eSLandon J. Fuller * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
2177cb4d3eSLandon J. Fuller * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
2277cb4d3eSLandon J. Fuller * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2377cb4d3eSLandon J. Fuller * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2477cb4d3eSLandon J. Fuller * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
2577cb4d3eSLandon J. Fuller * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2677cb4d3eSLandon J. Fuller * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
2777cb4d3eSLandon J. Fuller * THE POSSIBILITY OF SUCH DAMAGES.
2877cb4d3eSLandon J. Fuller */
2977cb4d3eSLandon J. Fuller
3077cb4d3eSLandon J. Fuller #include <sys/param.h>
3177cb4d3eSLandon J. Fuller #include <sys/bus.h>
3277cb4d3eSLandon J. Fuller #include <sys/malloc.h>
3377cb4d3eSLandon J. Fuller #include <sys/rman.h>
3477cb4d3eSLandon J. Fuller
3577cb4d3eSLandon J. Fuller #include <machine/bus.h>
3677cb4d3eSLandon J. Fuller
3777cb4d3eSLandon J. Fuller #include <dev/bhnd/bhnd.h>
3877cb4d3eSLandon J. Fuller
3977cb4d3eSLandon J. Fuller #include "bhnd_nvram_private.h"
4077cb4d3eSLandon J. Fuller
4177cb4d3eSLandon J. Fuller #include "bhnd_nvram_io.h"
4277cb4d3eSLandon J. Fuller #include "bhnd_nvram_iovar.h"
4377cb4d3eSLandon J. Fuller
4477cb4d3eSLandon J. Fuller /**
4577cb4d3eSLandon J. Fuller * BHND resource-backed NVRAM I/O context.
4677cb4d3eSLandon J. Fuller */
4777cb4d3eSLandon J. Fuller struct bhnd_nvram_iores {
4877cb4d3eSLandon J. Fuller struct bhnd_nvram_io io; /**< common I/O instance state */
4977cb4d3eSLandon J. Fuller struct bhnd_resource *res; /**< backing resource (borrowed ref) */
5077cb4d3eSLandon J. Fuller size_t offset; /**< offset within res */
5177cb4d3eSLandon J. Fuller size_t size; /**< size relative to the base offset */
5277cb4d3eSLandon J. Fuller u_int bus_width; /**< data type byte width to be used
5377cb4d3eSLandon J. Fuller when performing bus operations
5477cb4d3eSLandon J. Fuller on res. (1, 2, or 4 bytes) */
5577cb4d3eSLandon J. Fuller };
5677cb4d3eSLandon J. Fuller
5777cb4d3eSLandon J. Fuller BHND_NVRAM_IOPS_DEFN(iores);
5877cb4d3eSLandon J. Fuller
5977cb4d3eSLandon J. Fuller /**
6077cb4d3eSLandon J. Fuller * Allocate and return a new I/O context backed by a borrowed reference to @p r.
6177cb4d3eSLandon J. Fuller *
6277cb4d3eSLandon J. Fuller * The caller is responsible for deallocating the returned I/O context via
6377cb4d3eSLandon J. Fuller * bhnd_nvram_io_free().
6477cb4d3eSLandon J. Fuller *
6577cb4d3eSLandon J. Fuller * @param r The resource to be mapped by the returned I/O
6677cb4d3eSLandon J. Fuller * context.
6777cb4d3eSLandon J. Fuller * @param offset Offset
6877cb4d3eSLandon J. Fuller * @param bus_width The required I/O width (1, 2, or 4 bytes) to be
6977cb4d3eSLandon J. Fuller * used when reading from @p r.
7077cb4d3eSLandon J. Fuller *
7177cb4d3eSLandon J. Fuller * @retval bhnd_nvram_io success.
7277cb4d3eSLandon J. Fuller * @retval NULL if allocation fails, or an invalid argument
7377cb4d3eSLandon J. Fuller * is supplied.
7477cb4d3eSLandon J. Fuller */
7577cb4d3eSLandon J. Fuller struct bhnd_nvram_io *
bhnd_nvram_iores_new(struct bhnd_resource * r,bus_size_t offset,bus_size_t size,u_int bus_width)7677cb4d3eSLandon J. Fuller bhnd_nvram_iores_new(struct bhnd_resource *r, bus_size_t offset,
7777cb4d3eSLandon J. Fuller bus_size_t size, u_int bus_width)
7877cb4d3eSLandon J. Fuller {
7977cb4d3eSLandon J. Fuller struct bhnd_nvram_iores *iores;
8077cb4d3eSLandon J. Fuller rman_res_t r_start, r_size;
8177cb4d3eSLandon J. Fuller
8277cb4d3eSLandon J. Fuller /* Verify the bus width */
8377cb4d3eSLandon J. Fuller switch (bus_width) {
8477cb4d3eSLandon J. Fuller case 1:
8577cb4d3eSLandon J. Fuller case 2:
8677cb4d3eSLandon J. Fuller case 4:
8777cb4d3eSLandon J. Fuller /* valid */
8877cb4d3eSLandon J. Fuller break;
8977cb4d3eSLandon J. Fuller default:
9077cb4d3eSLandon J. Fuller BHND_NV_LOG("invalid bus width %u\n", bus_width);
9177cb4d3eSLandon J. Fuller return (NULL);
9277cb4d3eSLandon J. Fuller }
9377cb4d3eSLandon J. Fuller
9477cb4d3eSLandon J. Fuller /* offset/size must not exceed our internal size_t representation,
9577cb4d3eSLandon J. Fuller * or our bus_size_t usage (note that BUS_SPACE_MAXSIZE may be less
9677cb4d3eSLandon J. Fuller * than 2^(sizeof(bus_size_t) * 32). */
9777cb4d3eSLandon J. Fuller if (size > SIZE_MAX || offset > SIZE_MAX) {
9877cb4d3eSLandon J. Fuller BHND_NV_LOG("offset %#jx+%#jx exceeds SIZE_MAX\n",
9977cb4d3eSLandon J. Fuller (uintmax_t)offset, (uintmax_t)offset);
10077cb4d3eSLandon J. Fuller return (NULL);
10177cb4d3eSLandon J. Fuller }
10277cb4d3eSLandon J. Fuller
10377cb4d3eSLandon J. Fuller if (size > BUS_SPACE_MAXSIZE || offset > BUS_SPACE_MAXSIZE)
10477cb4d3eSLandon J. Fuller {
10577cb4d3eSLandon J. Fuller BHND_NV_LOG("offset %#jx+%#jx exceeds BUS_SPACE_MAXSIZE\n",
10677cb4d3eSLandon J. Fuller (uintmax_t)offset, (uintmax_t)offset);
10777cb4d3eSLandon J. Fuller return (NULL);
10877cb4d3eSLandon J. Fuller }
10977cb4d3eSLandon J. Fuller
11077cb4d3eSLandon J. Fuller /* offset/size fall within the resource's mapped range */
11177cb4d3eSLandon J. Fuller r_size = rman_get_size(r->res);
11277cb4d3eSLandon J. Fuller r_start = rman_get_start(r->res);
11377cb4d3eSLandon J. Fuller if (r_size < offset || r_size < size || r_size - size < offset)
11477cb4d3eSLandon J. Fuller return (NULL);
11577cb4d3eSLandon J. Fuller
11677cb4d3eSLandon J. Fuller /* offset/size must be bus_width aligned */
11777cb4d3eSLandon J. Fuller if ((r_start + offset) % bus_width != 0) {
11877cb4d3eSLandon J. Fuller BHND_NV_LOG("base address %#jx+%#jx not aligned to bus width "
11977cb4d3eSLandon J. Fuller "%u\n", (uintmax_t)r_start, (uintmax_t)offset, bus_width);
12077cb4d3eSLandon J. Fuller return (NULL);
12177cb4d3eSLandon J. Fuller }
12277cb4d3eSLandon J. Fuller
12377cb4d3eSLandon J. Fuller if (size % bus_width != 0) {
12477cb4d3eSLandon J. Fuller BHND_NV_LOG("size %#jx not aligned to bus width %u\n",
12577cb4d3eSLandon J. Fuller (uintmax_t)size, bus_width);
12677cb4d3eSLandon J. Fuller return (NULL);
12777cb4d3eSLandon J. Fuller }
12877cb4d3eSLandon J. Fuller
12977cb4d3eSLandon J. Fuller /* Allocate and return the I/O context */
13077cb4d3eSLandon J. Fuller iores = malloc(sizeof(*iores), M_BHND_NVRAM, M_WAITOK);
13177cb4d3eSLandon J. Fuller iores->io.iops = &bhnd_nvram_iores_ops;
13277cb4d3eSLandon J. Fuller iores->res = r;
13377cb4d3eSLandon J. Fuller iores->offset = offset;
13477cb4d3eSLandon J. Fuller iores->size = size;
13577cb4d3eSLandon J. Fuller iores->bus_width = bus_width;
13677cb4d3eSLandon J. Fuller
13777cb4d3eSLandon J. Fuller return (&iores->io);
13877cb4d3eSLandon J. Fuller }
13977cb4d3eSLandon J. Fuller
14077cb4d3eSLandon J. Fuller static void
bhnd_nvram_iores_free(struct bhnd_nvram_io * io)14177cb4d3eSLandon J. Fuller bhnd_nvram_iores_free(struct bhnd_nvram_io *io)
14277cb4d3eSLandon J. Fuller {
14377cb4d3eSLandon J. Fuller free(io, M_BHND_NVRAM);
14477cb4d3eSLandon J. Fuller }
14577cb4d3eSLandon J. Fuller
14677cb4d3eSLandon J. Fuller static size_t
bhnd_nvram_iores_getsize(struct bhnd_nvram_io * io)14777cb4d3eSLandon J. Fuller bhnd_nvram_iores_getsize(struct bhnd_nvram_io *io)
14877cb4d3eSLandon J. Fuller {
14977cb4d3eSLandon J. Fuller struct bhnd_nvram_iores *iores = (struct bhnd_nvram_iores *)io;
15077cb4d3eSLandon J. Fuller return (iores->size);
15177cb4d3eSLandon J. Fuller }
15277cb4d3eSLandon J. Fuller
15377cb4d3eSLandon J. Fuller static int
bhnd_nvram_iores_setsize(struct bhnd_nvram_io * io,size_t size)15477cb4d3eSLandon J. Fuller bhnd_nvram_iores_setsize(struct bhnd_nvram_io *io, size_t size)
15577cb4d3eSLandon J. Fuller {
15677cb4d3eSLandon J. Fuller /* unsupported */
15777cb4d3eSLandon J. Fuller return (ENODEV);
15877cb4d3eSLandon J. Fuller }
15977cb4d3eSLandon J. Fuller
16077cb4d3eSLandon J. Fuller static int
bhnd_nvram_iores_read_ptr(struct bhnd_nvram_io * io,size_t offset,const void ** ptr,size_t nbytes,size_t * navail)16177cb4d3eSLandon J. Fuller bhnd_nvram_iores_read_ptr(struct bhnd_nvram_io *io, size_t offset,
16277cb4d3eSLandon J. Fuller const void **ptr, size_t nbytes, size_t *navail)
16377cb4d3eSLandon J. Fuller {
16477cb4d3eSLandon J. Fuller /* unsupported */
16577cb4d3eSLandon J. Fuller return (ENODEV);
16677cb4d3eSLandon J. Fuller }
16777cb4d3eSLandon J. Fuller
16877cb4d3eSLandon J. Fuller static int
bhnd_nvram_iores_write_ptr(struct bhnd_nvram_io * io,size_t offset,void ** ptr,size_t nbytes,size_t * navail)16977cb4d3eSLandon J. Fuller bhnd_nvram_iores_write_ptr(struct bhnd_nvram_io *io, size_t offset,
17077cb4d3eSLandon J. Fuller void **ptr, size_t nbytes, size_t *navail)
17177cb4d3eSLandon J. Fuller {
17277cb4d3eSLandon J. Fuller /* unsupported */
17377cb4d3eSLandon J. Fuller return (ENODEV);
17477cb4d3eSLandon J. Fuller }
17577cb4d3eSLandon J. Fuller
17677cb4d3eSLandon J. Fuller /**
17777cb4d3eSLandon J. Fuller * Validate @p offset and @p nbytes:
17877cb4d3eSLandon J. Fuller *
17977cb4d3eSLandon J. Fuller * - Verify that @p offset is mapped by the backing resource.
18077cb4d3eSLandon J. Fuller * - If less than @p nbytes are available at @p offset, write the actual number
18177cb4d3eSLandon J. Fuller * of bytes available to @p nbytes.
18277cb4d3eSLandon J. Fuller * - Verify that @p offset + @p nbytes are correctly aligned.
18377cb4d3eSLandon J. Fuller */
18477cb4d3eSLandon J. Fuller static int
bhnd_nvram_iores_validate_req(struct bhnd_nvram_iores * iores,size_t offset,size_t * nbytes)18577cb4d3eSLandon J. Fuller bhnd_nvram_iores_validate_req(struct bhnd_nvram_iores *iores, size_t offset,
18677cb4d3eSLandon J. Fuller size_t *nbytes)
18777cb4d3eSLandon J. Fuller {
18877cb4d3eSLandon J. Fuller /* Verify offset falls within the resource range */
18977cb4d3eSLandon J. Fuller if (offset > iores->size)
19077cb4d3eSLandon J. Fuller return (ENXIO);
19177cb4d3eSLandon J. Fuller
19277cb4d3eSLandon J. Fuller /* Check for eof */
19377cb4d3eSLandon J. Fuller if (offset == iores->size) {
19477cb4d3eSLandon J. Fuller *nbytes = 0;
19577cb4d3eSLandon J. Fuller return (0);
19677cb4d3eSLandon J. Fuller }
19777cb4d3eSLandon J. Fuller
19877cb4d3eSLandon J. Fuller /* Verify offset alignment */
19977cb4d3eSLandon J. Fuller if (offset % iores->bus_width != 0)
20077cb4d3eSLandon J. Fuller return (EFAULT);
20177cb4d3eSLandon J. Fuller
20277cb4d3eSLandon J. Fuller /* Limit nbytes to available range and verify size alignment */
20377cb4d3eSLandon J. Fuller *nbytes = ummin(*nbytes, iores->size - offset);
20477cb4d3eSLandon J. Fuller if (*nbytes < iores->bus_width && *nbytes % iores->bus_width != 0)
20577cb4d3eSLandon J. Fuller return (EFAULT);
20677cb4d3eSLandon J. Fuller
20777cb4d3eSLandon J. Fuller return (0);
20877cb4d3eSLandon J. Fuller }
20977cb4d3eSLandon J. Fuller
21077cb4d3eSLandon J. Fuller static int
bhnd_nvram_iores_read(struct bhnd_nvram_io * io,size_t offset,void * buffer,size_t nbytes)21177cb4d3eSLandon J. Fuller bhnd_nvram_iores_read(struct bhnd_nvram_io *io, size_t offset, void *buffer,
21277cb4d3eSLandon J. Fuller size_t nbytes)
21377cb4d3eSLandon J. Fuller {
21477cb4d3eSLandon J. Fuller struct bhnd_nvram_iores *iores;
21577cb4d3eSLandon J. Fuller bus_size_t r_offset;
21677cb4d3eSLandon J. Fuller size_t navail;
21777cb4d3eSLandon J. Fuller int error;
21877cb4d3eSLandon J. Fuller
21977cb4d3eSLandon J. Fuller iores = (struct bhnd_nvram_iores *)io;
22077cb4d3eSLandon J. Fuller
22177cb4d3eSLandon J. Fuller /* Validate the request and determine the actual number of readable
22277cb4d3eSLandon J. Fuller * bytes */
22377cb4d3eSLandon J. Fuller navail = nbytes;
22477cb4d3eSLandon J. Fuller if ((error = bhnd_nvram_iores_validate_req(iores, offset, &navail)))
22577cb4d3eSLandon J. Fuller return (error);
22677cb4d3eSLandon J. Fuller
22777cb4d3eSLandon J. Fuller /* At least nbytes must be readable */
22877cb4d3eSLandon J. Fuller if (navail < nbytes)
22977cb4d3eSLandon J. Fuller return (ENXIO);
23077cb4d3eSLandon J. Fuller
23177cb4d3eSLandon J. Fuller /* Handle zero length read */
23277cb4d3eSLandon J. Fuller if (nbytes == 0)
23377cb4d3eSLandon J. Fuller return (0);
23477cb4d3eSLandon J. Fuller
23577cb4d3eSLandon J. Fuller /* Determine actual resource offset and perform the read */
23677cb4d3eSLandon J. Fuller r_offset = iores->offset + offset;
23777cb4d3eSLandon J. Fuller switch (iores->bus_width) {
23877cb4d3eSLandon J. Fuller case 1:
23977cb4d3eSLandon J. Fuller bhnd_bus_read_region_stream_1(iores->res, r_offset, buffer,
24077cb4d3eSLandon J. Fuller nbytes);
24177cb4d3eSLandon J. Fuller break;
24277cb4d3eSLandon J. Fuller case 2:
24377cb4d3eSLandon J. Fuller bhnd_bus_read_region_stream_2(iores->res, r_offset, buffer,
24477cb4d3eSLandon J. Fuller nbytes / 2);
24577cb4d3eSLandon J. Fuller break;
24677cb4d3eSLandon J. Fuller case 4:
24777cb4d3eSLandon J. Fuller bhnd_bus_read_region_stream_4(iores->res, r_offset, buffer,
24877cb4d3eSLandon J. Fuller nbytes / 4);
24977cb4d3eSLandon J. Fuller break;
25077cb4d3eSLandon J. Fuller default:
25177cb4d3eSLandon J. Fuller panic("unreachable!");
25277cb4d3eSLandon J. Fuller }
25377cb4d3eSLandon J. Fuller
25477cb4d3eSLandon J. Fuller return (0);
25577cb4d3eSLandon J. Fuller }
25677cb4d3eSLandon J. Fuller
25777cb4d3eSLandon J. Fuller static int
bhnd_nvram_iores_write(struct bhnd_nvram_io * io,size_t offset,void * buffer,size_t nbytes)25877cb4d3eSLandon J. Fuller bhnd_nvram_iores_write(struct bhnd_nvram_io *io, size_t offset,
25977cb4d3eSLandon J. Fuller void *buffer, size_t nbytes)
26077cb4d3eSLandon J. Fuller {
26177cb4d3eSLandon J. Fuller struct bhnd_nvram_iores *iores;
26277cb4d3eSLandon J. Fuller size_t navail;
26377cb4d3eSLandon J. Fuller bus_size_t r_offset;
26477cb4d3eSLandon J. Fuller int error;
26577cb4d3eSLandon J. Fuller
26677cb4d3eSLandon J. Fuller iores = (struct bhnd_nvram_iores *)io;
26777cb4d3eSLandon J. Fuller
26877cb4d3eSLandon J. Fuller /* Validate the request and determine the actual number of writable
26977cb4d3eSLandon J. Fuller * bytes */
27077cb4d3eSLandon J. Fuller navail = nbytes;
27177cb4d3eSLandon J. Fuller if ((error = bhnd_nvram_iores_validate_req(iores, offset, &navail)))
27277cb4d3eSLandon J. Fuller return (error);
27377cb4d3eSLandon J. Fuller
27477cb4d3eSLandon J. Fuller /* At least nbytes must be writable */
27577cb4d3eSLandon J. Fuller if (navail < nbytes)
27677cb4d3eSLandon J. Fuller return (ENXIO);
27777cb4d3eSLandon J. Fuller
27877cb4d3eSLandon J. Fuller /* Determine actual resource offset and perform the write */
27977cb4d3eSLandon J. Fuller r_offset = iores->offset + offset;
28077cb4d3eSLandon J. Fuller switch (iores->bus_width) {
28177cb4d3eSLandon J. Fuller case 1:
28277cb4d3eSLandon J. Fuller bhnd_bus_write_region_stream_1(iores->res, r_offset, buffer,
28377cb4d3eSLandon J. Fuller nbytes);
28477cb4d3eSLandon J. Fuller break;
28577cb4d3eSLandon J. Fuller case 2:
28677cb4d3eSLandon J. Fuller bhnd_bus_write_region_stream_2(iores->res, r_offset, buffer,
28777cb4d3eSLandon J. Fuller nbytes / 2);
28877cb4d3eSLandon J. Fuller break;
28977cb4d3eSLandon J. Fuller case 4:
29077cb4d3eSLandon J. Fuller bhnd_bus_write_region_stream_4(iores->res, r_offset, buffer,
29177cb4d3eSLandon J. Fuller nbytes / 4);
29277cb4d3eSLandon J. Fuller break;
29377cb4d3eSLandon J. Fuller default:
29477cb4d3eSLandon J. Fuller panic("unreachable!");
29577cb4d3eSLandon J. Fuller }
29677cb4d3eSLandon J. Fuller
29777cb4d3eSLandon J. Fuller return (0);
29877cb4d3eSLandon J. Fuller }
299