xref: /freebsd/sys/dev/bhnd/nvram/bhnd_nvram_iores.c (revision fdafd315)
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