xref: /f-stack/freebsd/mips/ingenic/jz4780_nemc.c (revision 22ce4aff)
1 /*-
2  * Copyright 2015 Alexander Kabaev <[email protected]>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 /*
28  * Ingenic JZ4780 NAND and External Memory Controller (NEMC) driver.
29  *
30  */
31 
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/conf.h>
38 #include <sys/bus.h>
39 #include <sys/kernel.h>
40 #include <sys/module.h>
41 #include <sys/lock.h>
42 #include <sys/mutex.h>
43 #include <sys/resource.h>
44 #include <sys/rman.h>
45 
46 #include <machine/bus.h>
47 
48 #include <dev/extres/clk/clk.h>
49 
50 #include <dev/fdt/fdt_common.h>
51 #include <dev/fdt/simplebus.h>
52 #include <dev/ofw/ofw_bus.h>
53 #include <dev/ofw/ofw_bus_subr.h>
54 
55 #include <mips/ingenic/jz4780_regs.h>
56 
57 struct jz4780_nemc_devinfo {
58 	struct simplebus_devinfo sinfo;
59 	uint32_t                 bank;
60 };
61 
62 struct jz4780_nemc_softc {
63 	struct simplebus_softc	simplebus_sc;
64 	device_t		dev;
65 	struct resource		*res[1];
66 	uint32_t		banks;
67 	uint32_t		clock_tick_psecs;
68 	clk_t			clk;
69 };
70 
71 static struct resource_spec jz4780_nemc_spec[] = {
72 	{ SYS_RES_MEMORY, 0, RF_ACTIVE },
73 	{ -1, 0 }
74 };
75 
76 #define	CSR_WRITE_4(sc, reg, val)	bus_write_4((sc)->res[0], reg, (val))
77 #define	CSR_READ_4(sc, reg)		bus_read_4((sc)->res[0], reg)
78 
79 static int jz4780_nemc_probe(device_t dev);
80 static int jz4780_nemc_attach(device_t dev);
81 static int jz4780_nemc_detach(device_t dev);
82 
83 static int
jz4780_nemc_probe(device_t dev)84 jz4780_nemc_probe(device_t dev)
85 {
86 
87 	if (!ofw_bus_status_okay(dev))
88 		return (ENXIO);
89 
90 	if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-nemc"))
91 		return (ENXIO);
92 
93 	device_set_desc(dev, "Ingenic JZ4780 NEMC");
94 
95 	return (BUS_PROBE_DEFAULT);
96 }
97 
98 #define JZ4780_NEMC_NS_TO_TICKS(sc, val) howmany((val) * 1000,  (sc)->clock_tick_psecs)
99 
100 /* Use table from JZ4780 programmers manual to convert ticks to tBP/tAW register values */
101 static const uint8_t ticks_to_tBP_tAW[32] = {
102 	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,  /* 1:1 mapping */
103 	11, 11,                            /* 12 cycles */
104 	12, 12, 12,                        /* 15 cycles */
105 	13, 13, 13, 13, 13,                /* 20 cycles */
106 	14, 14, 14, 14, 14,                /* 25 cycles */
107 	15, 15, 15, 15, 15, 15             /* 31 cycles */
108 };
109 
110 static int
jz4780_nemc_configure_bank(struct jz4780_nemc_softc * sc,device_t dev,u_int bank)111 jz4780_nemc_configure_bank(struct jz4780_nemc_softc *sc,
112         device_t dev, u_int bank)
113 {
114 	uint32_t smcr, cycles;
115 	phandle_t node;
116 	pcell_t   val;
117 
118 	/* Check if bank is configured already */
119 	if (sc->banks & (1 << bank))
120 		return 0;
121 
122 	smcr = CSR_READ_4(sc, JZ_NEMC_SMCR(bank));
123 
124 	smcr &= ~JZ_NEMC_SMCR_SMT_MASK;
125 	smcr |= JZ_NEMC_SMCR_SMT_NORMAL << JZ_NEMC_SMCR_SMT_SHIFT;
126 
127 	node = ofw_bus_get_node(dev);
128 	if (OF_getencprop(node, "ingenic,nemc-tAS", &val, sizeof(val)) > 0) {
129 		cycles = JZ4780_NEMC_NS_TO_TICKS(sc, val);
130 		if (cycles > 15) {
131 			device_printf(sc->dev,
132 			    "invalid value of %s %u (%u cycles), maximum %u cycles supported\n",
133 			    "ingenic,nemc-tAS", val, cycles, 15);
134 			return -1;
135 		}
136 		smcr &= ~JZ_NEMC_SMCR_TAS_MASK;
137 		smcr |= cycles << JZ_NEMC_SMCR_TAS_SHIFT;
138 	}
139 
140 	if (OF_getencprop(node, "ingenic,nemc-tAH", &val, sizeof(val)) > 0) {
141 		cycles = JZ4780_NEMC_NS_TO_TICKS(sc, val);
142 		if (cycles > 15) {
143 			device_printf(sc->dev,
144 			    "invalid value of %s %u (%u cycles), maximum %u cycles supported\n",
145 			    "ingenic,nemc-tAH", val, cycles, 15);
146 			return -1;
147 		}
148 		smcr &= ~JZ_NEMC_SMCR_TAH_MASK;
149 		smcr |= cycles << JZ_NEMC_SMCR_TAH_SHIFT;
150 	}
151 
152 	if (OF_getencprop(node, "ingenic,nemc-tBP", &val, sizeof(val)) > 0) {
153 		cycles = JZ4780_NEMC_NS_TO_TICKS(sc, val);
154 		if (cycles > 31) {
155 			device_printf(sc->dev,
156 			    "invalid value of %s %u (%u cycles), maximum %u cycles supported\n",
157 			    "ingenic,nemc-tBP", val, cycles, 15);
158 			return -1;
159 		}
160 		smcr &= ~JZ_NEMC_SMCR_TBP_MASK;
161 		smcr |= ticks_to_tBP_tAW[cycles] << JZ_NEMC_SMCR_TBP_SHIFT;
162 	}
163 
164 	if (OF_getencprop(node, "ingenic,nemc-tAW", &val, sizeof(val)) > 0) {
165 		cycles = JZ4780_NEMC_NS_TO_TICKS(sc, val);
166 		if (cycles > 31) {
167 			device_printf(sc->dev,
168 			    "invalid value of %s %u (%u cycles), maximum %u cycles supported\n",
169 			    "ingenic,nemc-tAW", val, cycles, 15);
170 			return -1;
171 		}
172 		smcr &= ~JZ_NEMC_SMCR_TAW_MASK;
173 		smcr |= ticks_to_tBP_tAW[cycles] << JZ_NEMC_SMCR_TAW_SHIFT;
174 	}
175 
176 	if (OF_getencprop(node, "ingenic,nemc-tSTRV", &val, sizeof(val)) > 0) {
177 		cycles = JZ4780_NEMC_NS_TO_TICKS(sc, val);
178 		if (cycles > 63) {
179 			device_printf(sc->dev,
180 			    "invalid value of %s %u (%u cycles), maximum %u cycles supported\n",
181 			    "ingenic,nemc-tSTRV", val, cycles, 15);
182 			return -1;
183 		}
184 		smcr &= ~JZ_NEMC_SMCR_STRV_MASK;
185 		smcr |= cycles << JZ_NEMC_SMCR_STRV_SHIFT;
186 	}
187 	CSR_WRITE_4(sc, JZ_NEMC_SMCR(bank), smcr);
188 	sc->banks |= (1 << bank);
189 	return 0;
190 }
191 
192 /* Wholesale copy of simplebus routine */
193 static int
jz4780_nemc_fill_ranges(phandle_t node,struct simplebus_softc * sc)194 jz4780_nemc_fill_ranges(phandle_t node, struct simplebus_softc *sc)
195 {
196 	int host_address_cells;
197 	cell_t *base_ranges;
198 	ssize_t nbase_ranges;
199 	int err;
200 	int i, j, k;
201 
202 	err = OF_searchencprop(OF_parent(node), "#address-cells",
203 	    &host_address_cells, sizeof(host_address_cells));
204 	if (err <= 0)
205 		return (-1);
206 
207 	nbase_ranges = OF_getproplen(node, "ranges");
208 	if (nbase_ranges < 0)
209 		return (-1);
210 	sc->nranges = nbase_ranges / sizeof(cell_t) /
211 	    (sc->acells + host_address_cells + sc->scells);
212 	if (sc->nranges == 0)
213 		return (0);
214 
215 	sc->ranges = malloc(sc->nranges * sizeof(sc->ranges[0]),
216 	    M_DEVBUF, M_WAITOK);
217 	base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK);
218 	OF_getencprop(node, "ranges", base_ranges, nbase_ranges);
219 
220 	for (i = 0, j = 0; i < sc->nranges; i++) {
221 		sc->ranges[i].bus = 0;
222 		for (k = 0; k < sc->acells; k++) {
223 			sc->ranges[i].bus <<= 32;
224 			sc->ranges[i].bus |= base_ranges[j++];
225 		}
226 		sc->ranges[i].host = 0;
227 		for (k = 0; k < host_address_cells; k++) {
228 			sc->ranges[i].host <<= 32;
229 			sc->ranges[i].host |= base_ranges[j++];
230 		}
231 		sc->ranges[i].size = 0;
232 		for (k = 0; k < sc->scells; k++) {
233 			sc->ranges[i].size <<= 32;
234 			sc->ranges[i].size |= base_ranges[j++];
235 		}
236 	}
237 
238 	free(base_ranges, M_DEVBUF);
239 	return (sc->nranges);
240 }
241 
242 static int
jz4780_nemc_attach(device_t dev)243 jz4780_nemc_attach(device_t dev)
244 {
245 	struct jz4780_nemc_softc *sc = device_get_softc(dev);
246 	phandle_t node;
247 	uint64_t freq;
248 
249 	sc->dev = dev;
250 
251 	if (bus_alloc_resources(dev, jz4780_nemc_spec, sc->res)) {
252 		device_printf(dev, "could not allocate resources for device\n");
253 		return (ENXIO);
254 	}
255 
256 	node = ofw_bus_get_node(dev);
257 
258 	/* Initialize simplebus and enumerate resources */
259 	simplebus_init(dev, node);
260 
261 	if (jz4780_nemc_fill_ranges(node, &sc->simplebus_sc) < 0)
262 		goto error;
263 
264 	/* Figure our underlying clock rate. */
265 	if (clk_get_by_ofw_index(dev, 0, 0, &sc->clk) != 0) {
266 		device_printf(dev, "could not lookup device clock\n");
267 		goto error;
268 	}
269 	if (clk_enable(sc->clk) != 0) {
270 		device_printf(dev, "could not enable device clock\n");
271 		goto error;
272 	}
273 	if (clk_get_freq(sc->clk, &freq) != 0) {
274 		device_printf(dev, "could not determine clock speed\n");
275 		goto error;
276 	}
277 
278 	/* Convert clock frequency to picoseconds-per-tick value. */
279 	sc->clock_tick_psecs = (uint32_t)(1000000000000ULL / freq);
280 
281 	/*
282 	 * Allow devices to identify.
283 	 */
284 	bus_generic_probe(dev);
285 
286 	/*
287 	 * Now walk the tree and attach top level devices
288 	 */
289 	for (node = OF_child(node); node > 0; node = OF_peer(node))
290 		simplebus_add_device(dev, node, 0, NULL, -1, NULL);
291 
292 	return (bus_generic_attach(dev));
293 error:
294 	jz4780_nemc_detach(dev);
295 	return (ENXIO);
296 }
297 
298 static int
jz4780_nemc_detach(device_t dev)299 jz4780_nemc_detach(device_t dev)
300 {
301 	struct jz4780_nemc_softc *sc = device_get_softc(dev);
302 
303 	bus_generic_detach(dev);
304 	if (sc->clk != NULL)
305 		clk_release(sc->clk);
306 	bus_release_resources(dev, jz4780_nemc_spec, sc->res);
307 	return (0);
308 }
309 
310 static int
jz4780_nemc_decode_bank(struct simplebus_softc * sc,struct resource * r,u_int * bank)311 jz4780_nemc_decode_bank(struct simplebus_softc *sc, struct resource *r,
312     u_int *bank)
313 {
314 	rman_res_t start, end;
315 	int i;
316 
317 	start = rman_get_start(r);
318 	end = rman_get_end(r);
319 
320 	/* Remap through ranges property */
321 	for (i = 0; i < sc->nranges; i++) {
322 		if (start >= sc->ranges[i].host && end <
323 		    sc->ranges[i].host + sc->ranges[i].size) {
324 			*bank = (sc->ranges[i].bus >> 32);
325 			return (0);
326 		}
327 	}
328 	return (1);
329 }
330 
331 static int
jz4780_nemc_activate_resource(device_t bus,device_t child,int type,int rid,struct resource * r)332 jz4780_nemc_activate_resource(device_t bus, device_t child, int type, int rid,
333     struct resource *r)
334 {
335 	struct jz4780_nemc_softc *sc;
336 	u_int bank;
337 	int err;
338 
339 	if (type == SYS_RES_MEMORY) {
340 		sc = device_get_softc(bus);
341 
342 		/* Figure out on what bank device is residing */
343 		err = jz4780_nemc_decode_bank(&sc->simplebus_sc, r, &bank);
344 		if (err == 0) {
345 			/* Attempt to configure the bank if not done already */
346 			err = jz4780_nemc_configure_bank(sc, child, bank);
347 			if (err != 0)
348 				return (err);
349 		}
350 	}
351 
352 	/* Call default implementation to finish the work */
353 	return (bus_generic_activate_resource(bus, child,
354 		type, rid, r));
355 }
356 
357 static device_method_t jz4780_nemc_methods[] = {
358 	/* Device interface */
359 	DEVMETHOD(device_probe,		jz4780_nemc_probe),
360 	DEVMETHOD(device_attach,	jz4780_nemc_attach),
361 	DEVMETHOD(device_detach,	jz4780_nemc_detach),
362 
363 	/* Overrides to configure bank on resource activation */
364 	DEVMETHOD(bus_activate_resource, jz4780_nemc_activate_resource),
365 
366 	DEVMETHOD_END
367 };
368 
369 static devclass_t jz4780_nemc_devclass;
370 DEFINE_CLASS_1(nemc, jz4780_nemc_driver, jz4780_nemc_methods,
371         sizeof(struct jz4780_nemc_softc), simplebus_driver);
372 DRIVER_MODULE(jz4780_nemc, simplebus, jz4780_nemc_driver,
373     jz4780_nemc_devclass, 0, 0);
374