1/*-
2 * Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>
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
57struct jz4780_nemc_devinfo {
58	struct simplebus_devinfo sinfo;
59	uint32_t                 bank;
60};
61
62struct 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
71static 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
79static int jz4780_nemc_probe(device_t dev);
80static int jz4780_nemc_attach(device_t dev);
81static int jz4780_nemc_detach(device_t dev);
82
83static int
84jz4780_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 */
101static 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
110static int
111jz4780_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 */
193static int
194jz4780_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
242static int
243jz4780_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));
293error:
294	jz4780_nemc_detach(dev);
295	return (ENXIO);
296}
297
298static int
299jz4780_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
310static int
311jz4780_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
331static int
332jz4780_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
357static 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
369static devclass_t jz4780_nemc_devclass;
370DEFINE_CLASS_1(nemc, jz4780_nemc_driver, jz4780_nemc_methods,
371        sizeof(struct jz4780_nemc_softc), simplebus_driver);
372DRIVER_MODULE(jz4780_nemc, simplebus, jz4780_nemc_driver,
373    jz4780_nemc_devclass, 0, 0);
374