1296936Smmel/*-
2296936Smmel * Copyright (c) 2015 Michal Meloun
3296936Smmel * All rights reserved.
4296936Smmel *
5296936Smmel * Redistribution and use in source and binary forms, with or without
6296936Smmel * modification, are permitted provided that the following conditions
7296936Smmel * are met:
8296936Smmel * 1. Redistributions of source code must retain the above copyright
9296936Smmel *    notice, this list of conditions and the following disclaimer.
10296936Smmel * 2. Redistributions in binary form must reproduce the above copyright
11296936Smmel *    notice, this list of conditions and the following disclaimer in the
12296936Smmel *    documentation and/or other materials provided with the distribution.
13296936Smmel *
14296936Smmel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15296936Smmel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16296936Smmel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17296936Smmel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18296936Smmel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19296936Smmel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20296936Smmel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21296936Smmel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22296936Smmel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23296936Smmel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24296936Smmel * SUCH DAMAGE.
25296936Smmel */
26296936Smmel
27296936Smmel#include <sys/cdefs.h>
28296936Smmel__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_efuse.c 314506 2017-03-01 19:55:04Z ian $");
29296936Smmel
30296936Smmel#include <sys/param.h>
31296936Smmel#include <sys/systm.h>
32296936Smmel#include <sys/bus.h>
33296936Smmel#include <sys/clock.h>
34296936Smmel#include <sys/kernel.h>
35296936Smmel#include <sys/limits.h>
36296936Smmel#include <sys/lock.h>
37296936Smmel#include <sys/mutex.h>
38296936Smmel#include <sys/module.h>
39296936Smmel#include <sys/resource.h>
40296936Smmel#include <sys/rman.h>
41296936Smmel
42296936Smmel#include <machine/bus.h>
43296936Smmel#include <machine/resource.h>
44296936Smmel
45296936Smmel#include <dev/extres/clk/clk.h>
46296936Smmel#include <dev/extres/hwreset/hwreset.h>
47296936Smmel#include <dev/fdt/fdt_common.h>
48296936Smmel#include <dev/ofw/ofw_bus.h>
49296936Smmel#include <dev/ofw/ofw_bus_subr.h>
50296936Smmel
51296936Smmel#include <arm/nvidia/tegra_efuse.h>
52296936Smmel
53296936Smmel
54296936Smmel#define	RD4(_sc, _r)	bus_read_4((_sc)->mem_res, (_sc)->fuse_begin + (_r))
55296936Smmel
56296936Smmelstatic struct ofw_compat_data compat_data[] = {
57296936Smmel	{"nvidia,tegra124-efuse",	1},
58296936Smmel	{NULL,			0}
59296936Smmel};
60296936Smmel
61296936Smmelstruct tegra_efuse_softc {
62296936Smmel	device_t		dev;
63296936Smmel	struct resource		*mem_res;
64296936Smmel
65296936Smmel	int			fuse_begin;
66296936Smmel	clk_t			clk;
67296936Smmel	hwreset_t			reset;
68296936Smmel};
69296936Smmelstruct tegra_efuse_softc *dev_sc;
70296936Smmel
71296936Smmelstruct tegra_sku_info tegra_sku_info;
72296936Smmelstatic char *tegra_rev_name[] = {
73296936Smmel	[TEGRA_REVISION_UNKNOWN] = "unknown",
74296936Smmel	[TEGRA_REVISION_A01]     = "A01",
75296936Smmel	[TEGRA_REVISION_A02]     = "A02",
76296936Smmel	[TEGRA_REVISION_A03]     = "A03",
77296936Smmel	[TEGRA_REVISION_A03p]    = "A03 prime",
78296936Smmel	[TEGRA_REVISION_A04]     = "A04",
79296936Smmel};
80296936Smmel
81296936Smmel/* Tegra30 and later */
82296936Smmel#define	FUSE_VENDOR_CODE	0x100
83296936Smmel#define	FUSE_FAB_CODE		0x104
84296936Smmel#define	FUSE_LOT_CODE_0		0x108
85296936Smmel#define	FUSE_LOT_CODE_1		0x10c
86296936Smmel#define	FUSE_WAFER_ID		0x110
87296936Smmel#define	FUSE_X_COORDINATE	0x114
88296936Smmel#define	FUSE_Y_COORDINATE	0x118
89296936Smmel
90296936Smmel/* ---------------------- Tegra 124 specific code & data --------------- */
91296936Smmel#define	TEGRA124_FUSE_BEGIN		0x100
92296936Smmel
93296936Smmel#define	TEGRA124_CPU_PROCESS_CORNERS	2
94296936Smmel#define	TEGRA124_GPU_PROCESS_CORNERS	2
95296936Smmel#define	TEGRA124_SOC_PROCESS_CORNERS	2
96296936Smmel
97296936Smmel#define	TEGRA124_FUSE_SKU_INFO		0x10
98296936Smmel#define	TEGRA124_FUSE_CPU_SPEEDO_0	0x14
99296936Smmel#define	TEGRA124_FUSE_CPU_IDDQ		0x18
100296936Smmel#define	TEGRA124_FUSE_FT_REV		0x28
101296936Smmel#define	TEGRA124_FUSE_CPU_SPEEDO_1	0x2c
102296936Smmel#define	TEGRA124_FUSE_CPU_SPEEDO_2	0x30
103296936Smmel#define	TEGRA124_FUSE_SOC_SPEEDO_0	0x34
104296936Smmel#define	TEGRA124_FUSE_SOC_SPEEDO_1	0x38
105296936Smmel#define	TEGRA124_FUSE_SOC_SPEEDO_2	0x3c
106296936Smmel#define	TEGRA124_FUSE_SOC_IDDQ		0x40
107296936Smmel#define	TEGRA124_FUSE_GPU_IDDQ		0x128
108296936Smmel
109296936Smmelenum {
110296936Smmel	TEGRA124_THRESHOLD_INDEX_0,
111296936Smmel	TEGRA124_THRESHOLD_INDEX_1,
112296936Smmel	TEGRA124_THRESHOLD_INDEX_COUNT,
113296936Smmel};
114296936Smmel
115296936Smmelstatic uint32_t tegra124_cpu_process_speedos[][TEGRA124_CPU_PROCESS_CORNERS] =
116296936Smmel{
117296936Smmel	{2190,	UINT_MAX},
118296936Smmel	{0,	UINT_MAX},
119296936Smmel};
120296936Smmel
121296936Smmelstatic uint32_t tegra124_gpu_process_speedos[][TEGRA124_GPU_PROCESS_CORNERS] =
122296936Smmel{
123296936Smmel	{1965,	UINT_MAX},
124296936Smmel	{0,	UINT_MAX},
125296936Smmel};
126296936Smmel
127296936Smmelstatic uint32_t tegra124_soc_process_speedos[][TEGRA124_SOC_PROCESS_CORNERS] =
128296936Smmel{
129296936Smmel	{2101,	UINT_MAX},
130296936Smmel	{0,	UINT_MAX},
131296936Smmel};
132296936Smmel
133296936Smmelstatic void
134296936Smmeltegra124_rev_sku_to_speedo_ids(struct tegra_efuse_softc *sc,
135296936Smmel    struct tegra_sku_info *sku, int *threshold)
136296936Smmel{
137296936Smmel
138296936Smmel	/* Assign to default */
139296936Smmel	sku->cpu_speedo_id = 0;
140296936Smmel	sku->soc_speedo_id = 0;
141296936Smmel	sku->gpu_speedo_id = 0;
142296936Smmel	*threshold = TEGRA124_THRESHOLD_INDEX_0;
143296936Smmel
144296936Smmel	switch (sku->sku_id) {
145296936Smmel	case 0x00: /* Eng sku */
146296936Smmel	case 0x0F:
147296936Smmel	case 0x23:
148296936Smmel		/* Using the default */
149296936Smmel		break;
150296936Smmel	case 0x83:
151296936Smmel		sku->cpu_speedo_id = 2;
152296936Smmel		break;
153296936Smmel
154296936Smmel	case 0x1F:
155296936Smmel	case 0x87:
156296936Smmel	case 0x27:
157296936Smmel		sku->cpu_speedo_id = 2;
158296936Smmel		sku->soc_speedo_id = 0;
159296936Smmel		sku->gpu_speedo_id = 1;
160296936Smmel		*threshold = TEGRA124_THRESHOLD_INDEX_0;
161296936Smmel		break;
162296936Smmel	case 0x81:
163296936Smmel	case 0x21:
164296936Smmel	case 0x07:
165296936Smmel		sku->cpu_speedo_id = 1;
166296936Smmel		sku->soc_speedo_id = 1;
167296936Smmel		sku->gpu_speedo_id = 1;
168296936Smmel		*threshold = TEGRA124_THRESHOLD_INDEX_1;
169296936Smmel		break;
170296936Smmel	case 0x49:
171296936Smmel	case 0x4A:
172296936Smmel	case 0x48:
173296936Smmel		sku->cpu_speedo_id = 4;
174296936Smmel		sku->soc_speedo_id = 2;
175296936Smmel		sku->gpu_speedo_id = 3;
176296936Smmel		*threshold = TEGRA124_THRESHOLD_INDEX_1;
177296936Smmel		break;
178296936Smmel	default:
179296936Smmel		device_printf(sc->dev, " Unknown SKU ID %d\n", sku->sku_id);
180296936Smmel		break;
181296936Smmel	}
182296936Smmel}
183296936Smmel
184296936Smmel
185296936Smmelstatic void
186296936Smmeltegra124_init_speedo(struct tegra_efuse_softc *sc, struct tegra_sku_info *sku)
187296936Smmel{
188296936Smmel	int i, threshold;
189296936Smmel
190296936Smmel	sku->sku_id = RD4(sc, TEGRA124_FUSE_SKU_INFO);
191296936Smmel	sku->soc_iddq_value = RD4(sc, TEGRA124_FUSE_SOC_IDDQ);
192296936Smmel	sku->cpu_iddq_value = RD4(sc, TEGRA124_FUSE_CPU_IDDQ);
193296936Smmel	sku->gpu_iddq_value = RD4(sc, TEGRA124_FUSE_GPU_IDDQ);
194296936Smmel	sku->soc_speedo_value = RD4(sc, TEGRA124_FUSE_SOC_SPEEDO_0);
195296936Smmel	sku->cpu_speedo_value = RD4(sc, TEGRA124_FUSE_CPU_SPEEDO_0);
196296936Smmel	sku->gpu_speedo_value = RD4(sc, TEGRA124_FUSE_CPU_SPEEDO_2);
197296936Smmel
198296936Smmel	if (sku->cpu_speedo_value == 0) {
199296936Smmel		device_printf(sc->dev, "CPU Speedo value is not fused.\n");
200296936Smmel		return;
201296936Smmel	}
202296936Smmel
203296936Smmel	tegra124_rev_sku_to_speedo_ids(sc, sku, &threshold);
204296936Smmel
205296936Smmel	for (i = 0; i < TEGRA124_SOC_PROCESS_CORNERS; i++) {
206296936Smmel		if (sku->soc_speedo_value <
207296936Smmel			tegra124_soc_process_speedos[threshold][i])
208296936Smmel			break;
209296936Smmel	}
210296936Smmel	sku->soc_process_id = i;
211296936Smmel
212296936Smmel	for (i = 0; i < TEGRA124_CPU_PROCESS_CORNERS; i++) {
213296936Smmel		if (sku->cpu_speedo_value <
214296936Smmel			tegra124_cpu_process_speedos[threshold][i])
215296936Smmel				break;
216296936Smmel	}
217296936Smmel	sku->cpu_process_id = i;
218296936Smmel
219296936Smmel	for (i = 0; i < TEGRA124_GPU_PROCESS_CORNERS; i++) {
220296936Smmel		if (sku->gpu_speedo_value <
221296936Smmel			tegra124_gpu_process_speedos[threshold][i])
222296936Smmel			break;
223296936Smmel	}
224296936Smmel	sku->gpu_process_id = i;
225296936Smmel
226296936Smmel}
227296936Smmel
228296936Smmel/* ----------------- End of Tegra 124 specific code & data --------------- */
229296936Smmel
230296936Smmeluint32_t
231296936Smmeltegra_fuse_read_4(int addr) {
232296936Smmel
233296936Smmel	if (dev_sc == NULL)
234296936Smmel		panic("tegra_fuse_read_4 called too early");
235296936Smmel	return (RD4(dev_sc, addr));
236296936Smmel}
237296936Smmel
238296936Smmel
239296936Smmelstatic void
240314506Siantegra_efuse_dump_sku(void)
241296936Smmel{
242296936Smmel	printf(" TEGRA SKU Info:\n");
243296936Smmel	printf("  chip_id: %u\n", tegra_sku_info.chip_id);
244296936Smmel	printf("  sku_id: %u\n", tegra_sku_info.sku_id);
245296936Smmel	printf("  cpu_process_id: %u\n", tegra_sku_info.cpu_process_id);
246296936Smmel	printf("  cpu_speedo_id: %u\n", tegra_sku_info.cpu_speedo_id);
247296936Smmel	printf("  cpu_speedo_value: %u\n", tegra_sku_info.cpu_speedo_value);
248296936Smmel	printf("  cpu_iddq_value: %u\n", tegra_sku_info.cpu_iddq_value);
249296936Smmel	printf("  soc_process_id: %u\n", tegra_sku_info.soc_process_id);
250296936Smmel	printf("  soc_speedo_id: %u\n", tegra_sku_info.soc_speedo_id);
251296936Smmel	printf("  soc_speedo_value: %u\n", tegra_sku_info.soc_speedo_value);
252296936Smmel	printf("  soc_iddq_value: %u\n", tegra_sku_info.soc_iddq_value);
253296936Smmel	printf("  gpu_process_id: %u\n", tegra_sku_info.gpu_process_id);
254296936Smmel	printf("  gpu_speedo_id: %u\n", tegra_sku_info.gpu_speedo_id);
255296936Smmel	printf("  gpu_speedo_value: %u\n", tegra_sku_info.gpu_speedo_value);
256296936Smmel	printf("  gpu_iddq_value: %u\n", tegra_sku_info.gpu_iddq_value);
257296936Smmel	printf("  revision: %s\n", tegra_rev_name[tegra_sku_info.revision]);
258296936Smmel}
259296936Smmel
260296936Smmelstatic int
261296936Smmeltegra_efuse_probe(device_t dev)
262296936Smmel{
263296936Smmel	if (!ofw_bus_status_okay(dev))
264296936Smmel		return (ENXIO);
265296936Smmel
266296936Smmel	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
267296936Smmel		return (ENXIO);
268296936Smmel
269296936Smmel	return (BUS_PROBE_DEFAULT);
270296936Smmel}
271296936Smmel
272296936Smmelstatic int
273296936Smmeltegra_efuse_attach(device_t dev)
274296936Smmel{
275296936Smmel	int rv, rid;
276296936Smmel	phandle_t node;
277296936Smmel	struct tegra_efuse_softc *sc;
278296936Smmel
279296936Smmel	sc = device_get_softc(dev);
280296936Smmel	sc->dev = dev;
281296936Smmel	node = ofw_bus_get_node(dev);
282296936Smmel
283296936Smmel	/* Get the memory resource for the register mapping. */
284296936Smmel	rid = 0;
285296936Smmel	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
286296936Smmel	    RF_ACTIVE);
287296936Smmel	if (sc->mem_res == NULL) {
288296936Smmel		device_printf(dev, "Cannot map registers.\n");
289296936Smmel		rv = ENXIO;
290296936Smmel		goto fail;
291296936Smmel	}
292296936Smmel
293296936Smmel	/* OFW resources. */
294308324Smmel	rv = clk_get_by_ofw_name(dev, 0, "fuse", &sc->clk);
295296936Smmel	if (rv != 0) {
296296936Smmel		device_printf(dev, "Cannot get fuse clock: %d\n", rv);
297296936Smmel		goto fail;
298296936Smmel	}
299296936Smmel	rv = clk_enable(sc->clk);
300296936Smmel	if (rv != 0) {
301296936Smmel		device_printf(dev, "Cannot enable clock: %d\n", rv);
302296936Smmel		goto fail;
303296936Smmel	}
304308324Smmel	rv = hwreset_get_by_ofw_name(sc->dev, 0, "fuse", &sc->reset);
305296936Smmel	if (rv != 0) {
306296936Smmel		device_printf(dev, "Cannot get fuse reset\n");
307296936Smmel		goto fail;
308296936Smmel	}
309296936Smmel	rv = hwreset_deassert(sc->reset);
310296936Smmel	if (rv != 0) {
311296936Smmel		device_printf(sc->dev, "Cannot clear reset\n");
312296936Smmel		goto fail;
313296936Smmel	}
314296936Smmel
315296936Smmel	/* Tegra124 specific init. */
316296936Smmel	sc->fuse_begin = TEGRA124_FUSE_BEGIN;
317296936Smmel	tegra124_init_speedo(sc, &tegra_sku_info);
318296936Smmel
319296936Smmel	dev_sc = sc;
320296936Smmel
321296936Smmel	if (bootverbose)
322296936Smmel		tegra_efuse_dump_sku();
323296936Smmel	return (bus_generic_attach(dev));
324296936Smmel
325296936Smmelfail:
326296936Smmel	dev_sc = NULL;
327296936Smmel	if (sc->clk != NULL)
328296936Smmel		clk_release(sc->clk);
329296936Smmel	if (sc->reset != NULL)
330296936Smmel		hwreset_release(sc->reset);
331296936Smmel	if (sc->mem_res != NULL)
332296936Smmel		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
333296936Smmel
334296936Smmel	return (rv);
335296936Smmel}
336296936Smmel
337296936Smmelstatic int
338296936Smmeltegra_efuse_detach(device_t dev)
339296936Smmel{
340296936Smmel	struct tegra_efuse_softc *sc;
341296936Smmel
342296936Smmel	sc = device_get_softc(dev);
343296936Smmel	dev_sc = NULL;
344296936Smmel	if (sc->clk != NULL)
345296936Smmel		clk_release(sc->clk);
346296936Smmel	if (sc->reset != NULL)
347296936Smmel		hwreset_release(sc->reset);
348296936Smmel	if (sc->mem_res != NULL)
349296936Smmel		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
350296936Smmel
351296936Smmel	return (bus_generic_detach(dev));
352296936Smmel}
353296936Smmel
354296936Smmelstatic device_method_t tegra_efuse_methods[] = {
355296936Smmel	/* Device interface */
356296936Smmel	DEVMETHOD(device_probe,		tegra_efuse_probe),
357296936Smmel	DEVMETHOD(device_attach,	tegra_efuse_attach),
358296936Smmel	DEVMETHOD(device_detach,	tegra_efuse_detach),
359296936Smmel
360296936Smmel	DEVMETHOD_END
361296936Smmel};
362296936Smmel
363308335Smmelstatic devclass_t tegra_efuse_devclass;
364308335Smmelstatic DEFINE_CLASS_0(efuse, tegra_efuse_driver, tegra_efuse_methods,
365296936Smmel    sizeof(struct tegra_efuse_softc));
366296936SmmelEARLY_DRIVER_MODULE(tegra_efuse, simplebus, tegra_efuse_driver,
367308335Smmel    tegra_efuse_devclass, NULL, NULL, BUS_PASS_TIMER);
368