1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2012 Semihalf.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD$");
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/kernel.h>
35#include <sys/module.h>
36#include <sys/slicer.h>
37
38#include <dev/fdt/fdt_common.h>
39#include <dev/ofw/ofw_bus.h>
40#include <dev/ofw/openfirm.h>
41
42#ifdef DEBUG
43#define debugf(fmt, args...) do { printf("%s(): ", __func__);	\
44    printf(fmt,##args); } while (0)
45#else
46#define debugf(fmt, args...)
47#endif
48
49static int fill_slices(device_t dev, const char *provider,
50    struct flash_slice *slices, int *slices_num);
51static void fdt_slicer_init(void);
52
53static int
54fill_slices_from_node(phandle_t node, struct flash_slice *slices, int *count)
55{
56	char *label;
57	phandle_t child;
58	u_long base, size;
59	int flags, i;
60	ssize_t nmlen;
61
62	i = 0;
63	for (child = OF_child(node); child != 0; child = OF_peer(child)) {
64		flags = FLASH_SLICES_FLAG_NONE;
65
66		/* Nodes with a compatible property are not slices. */
67		if (OF_hasprop(child, "compatible"))
68			continue;
69
70		if (i == FLASH_SLICES_MAX_NUM) {
71			debugf("not enough buffer for slice i=%d\n", i);
72			break;
73		}
74
75		/* Retrieve start and size of the slice. */
76		if (fdt_regsize(child, &base, &size) != 0) {
77			debugf("error during processing reg property, i=%d\n",
78			    i);
79			continue;
80		}
81
82		if (size == 0) {
83			debugf("slice i=%d with no size\n", i);
84			continue;
85		}
86
87		/* Retrieve label. */
88		nmlen = OF_getprop_alloc(child, "label", (void **)&label);
89		if (nmlen <= 0) {
90			/* Use node name if no label defined */
91			nmlen = OF_getprop_alloc(child, "name", (void **)&label);
92			if (nmlen <= 0) {
93				debugf("slice i=%d with no name\n", i);
94				label = NULL;
95			}
96		}
97
98		if (OF_hasprop(child, "read-only"))
99			flags |= FLASH_SLICES_FLAG_RO;
100
101		/* Fill slice entry data. */
102		slices[i].base = base;
103		slices[i].size = size;
104		slices[i].label = label;
105		slices[i].flags = flags;
106		i++;
107	}
108
109	*count = i;
110	return (0);
111}
112
113static int
114fill_slices(device_t dev, const char *provider __unused,
115    struct flash_slice *slices, int *slices_num)
116{
117	phandle_t child, node;
118
119	/*
120	 * We assume the caller provides buffer for FLASH_SLICES_MAX_NUM
121	 * flash_slice structures.
122	 */
123	if (slices == NULL) {
124		*slices_num = 0;
125		return (ENOMEM);
126	}
127
128	node = ofw_bus_get_node(dev);
129
130	/*
131	 * If there is a child node whose compatible is "fixed-partitions" then
132	 * we have new-style data where all partitions are the children of that
133	 * node.  Otherwise we have old-style data where all the children of the
134	 * device node are the partitions.
135	 */
136	child = fdt_find_compatible(node, "fixed-partitions", false);
137	if (child == 0)
138		return fill_slices_from_node(node, slices, slices_num);
139	else
140		return fill_slices_from_node(child, slices, slices_num);
141}
142
143static void
144fdt_slicer_init(void)
145{
146
147	flash_register_slicer(fill_slices, FLASH_SLICES_TYPE_NAND, false);
148	flash_register_slicer(fill_slices, FLASH_SLICES_TYPE_CFI, false);
149	flash_register_slicer(fill_slices, FLASH_SLICES_TYPE_SPI, false);
150}
151
152static void
153fdt_slicer_cleanup(void)
154{
155
156	flash_register_slicer(NULL, FLASH_SLICES_TYPE_NAND, true);
157	flash_register_slicer(NULL, FLASH_SLICES_TYPE_CFI, true);
158	flash_register_slicer(NULL, FLASH_SLICES_TYPE_SPI, true);
159}
160
161/*
162 * Must be initialized after GEOM classes (SI_SUB_DRIVERS/SI_ORDER_SECOND),
163 * i. e. after g_init() is called, due to the use of the GEOM topology_lock
164 * in flash_register_slicer().  However, must be before SI_SUB_CONFIGURE.
165 */
166SYSINIT(fdt_slicer, SI_SUB_DRIVERS, SI_ORDER_THIRD, fdt_slicer_init, NULL);
167SYSUNINIT(fdt_slicer, SI_SUB_DRIVERS, SI_ORDER_THIRD, fdt_slicer_cleanup, NULL);
168
169static int
170mod_handler(module_t mod, int type, void *data)
171{
172
173	/*
174	 * Nothing to do here: the SYSINIT/SYSUNINIT defined above run
175	 * automatically at module load/unload time.
176	 */
177	return (0);
178}
179
180static moduledata_t fdt_slicer_mod = {
181	"fdt_slicer", mod_handler, NULL
182};
183
184DECLARE_MODULE(fdt_slicer, fdt_slicer_mod, SI_SUB_DRIVERS, SI_ORDER_THIRD);
185MODULE_VERSION(fdt_slicer, 1);
186