xref: /openbsd-src/sys/dev/fdt/mvclock.c (revision 9f11ffb7133c203312a01e4b986886bc88c7d74b)
1 /*	$OpenBSD: mvclock.c,v 1.2 2018/07/24 21:52:38 kettenis Exp $	*/
2 /*
3  * Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/device.h>
21 
22 #include <machine/intr.h>
23 #include <machine/bus.h>
24 #include <machine/fdt.h>
25 
26 #include <dev/ofw/openfirm.h>
27 #include <dev/ofw/ofw_clock.h>
28 #include <dev/ofw/ofw_misc.h>
29 #include <dev/ofw/fdt.h>
30 
31 struct mvclock_softc {
32 	struct device		sc_dev;
33 
34 	struct clock_device	sc_cd;
35 };
36 
37 int mvclock_match(struct device *, void *, void *);
38 void mvclock_attach(struct device *, struct device *, void *);
39 
40 struct cfattach	mvclock_ca = {
41 	sizeof (struct mvclock_softc), mvclock_match, mvclock_attach
42 };
43 
44 struct cfdriver mvclock_cd = {
45 	NULL, "mvclock", DV_DULL
46 };
47 
48 uint32_t ap806_get_frequency(void *, uint32_t *);
49 uint32_t cp110_get_frequency(void *, uint32_t *);
50 void	cp110_enable(void *, uint32_t *, int);
51 
52 int
53 mvclock_match(struct device *parent, void *match, void *aux)
54 {
55 	struct fdt_attach_args *faa = aux;
56 
57 	return (OF_is_compatible(faa->fa_node, "marvell,ap806-clock") ||
58 	    OF_is_compatible(faa->fa_node, "marvell,cp110-clock"));
59 }
60 
61 void
62 mvclock_attach(struct device *parent, struct device *self, void *aux)
63 {
64 	struct mvclock_softc *sc = (struct mvclock_softc *)self;
65 	struct fdt_attach_args *faa = aux;
66 
67 	printf("\n");
68 
69 	sc->sc_cd.cd_node = faa->fa_node;
70 	sc->sc_cd.cd_cookie = sc;
71 	if (OF_is_compatible(faa->fa_node, "marvell,ap806-clock")) {
72 		sc->sc_cd.cd_get_frequency = ap806_get_frequency;
73 	} else {
74 		sc->sc_cd.cd_get_frequency = cp110_get_frequency;
75 		sc->sc_cd.cd_enable = cp110_enable;
76 	}
77 	clock_register(&sc->sc_cd);
78 }
79 
80 /* AP806 block */
81 
82 #define AP806_CORE_FIXED	2
83 #define AP806_CORE_MSS		3
84 #define AP806_CORE_SDIO		4
85 
86 uint32_t
87 ap806_get_frequency(void *cookie, uint32_t *cells)
88 {
89 	uint32_t idx = cells[0];
90 
91 	switch (idx) {
92 	case AP806_CORE_FIXED:
93 		/* fixed PLL at 1200MHz */
94 		return 1200000000;
95 	case AP806_CORE_MSS:
96 		/* MSS clock is fixed clock divided by 6 */
97 		return 200000000;
98 	case AP806_CORE_SDIO:
99 		/* SDIO/eMMC clock is fixed clock divided by 3 */
100 		return 400000000;
101 	default:
102 		break;
103 	}
104 
105 	printf("%s: 0x%08x\n", __func__, idx);
106 	return 0;
107 }
108 
109 /* CP110 block */
110 
111 #define CP110_PM_CLOCK_GATING_CTRL	0x220
112 
113 #define CP110_CORE_APLL		0
114 #define CP110_CORE_PPV2		1
115 #define CP110_CORE_X2CORE	2
116 #define CP110_CORE_CORE		3
117 #define CP110_CORE_SDIO		5
118 
119 #define CP110_GATE_SDIO		4
120 #define CP110_GATE_SLOW_IO	21
121 
122 uint32_t
123 cp110_get_frequency(void *cookie, uint32_t *cells)
124 {
125 	struct mvclock_softc *sc = cookie;
126 	uint32_t mod = cells[0];
127 	uint32_t idx = cells[1];
128 	uint32_t parent[2] = { 0, 0 };
129 
130 	/* Core clocks */
131 	if (mod == 0) {
132 		switch (idx) {
133 		case CP110_CORE_APLL:
134 			/* fixed PLL at 1GHz */
135 			return 1000000000;
136 		case CP110_CORE_PPV2:
137 			/* PPv2 clock is APLL/3 */
138 			return 333333333;
139 		case CP110_CORE_X2CORE:
140 			/* X2CORE clock is APLL/2 */
141 			return 500000000;
142 		case CP110_CORE_CORE:
143 			/* Core clock is X2CORE/2 */
144 			return 250000000;
145 		case CP110_CORE_SDIO:
146 			/* SDIO clock is APLL/2.5 */
147 			return 400000000;
148 		default:
149 			break;
150 		}
151 	}
152 
153 	/* Gatable clocks */
154 	if (mod == 1) {
155 		switch (idx) {
156 		case CP110_GATE_SDIO:
157 			parent[1] = CP110_CORE_SDIO;
158 			break;
159 		case CP110_GATE_SLOW_IO:
160 			parent[1] = CP110_CORE_X2CORE;
161 			break;
162 		default:
163 			break;
164 		}
165 
166 		if (parent[1] != 0)
167 			return cp110_get_frequency(sc, parent);
168 	}
169 
170 	printf("%s: 0x%08x 0x%08x\n", __func__, mod, idx);
171 	return 0;
172 }
173 
174 void
175 cp110_enable(void *cookie, uint32_t *cells, int on)
176 {
177 	struct mvclock_softc *sc = cookie;
178 	uint32_t mod = cells[0];
179 	uint32_t idx = cells[1];
180 
181 	/* Gatable clocks */
182 	if (mod == 1 && idx < 32) {
183 		struct regmap *rm;
184 		uint32_t reg;
185 
186 		rm = regmap_bynode(OF_parent(sc->sc_cd.cd_node));
187 		if (rm == NULL) {
188 			printf("%s: can't enable clock 0x%08x 0x%08x\n",
189 			    sc->sc_dev.dv_xname, mod, idx);
190 			return;
191 		}
192 		reg = regmap_read_4(rm, CP110_PM_CLOCK_GATING_CTRL);
193 		if (on)
194 			reg |= (1U << idx);
195 		else
196 			reg &= ~(1U << idx);
197 		regmap_write_4(rm, CP110_PM_CLOCK_GATING_CTRL, reg);
198 		return;
199 	}
200 
201 	printf("%s: 0x%08x 0x%08x\n", __func__, mod, idx);
202 }
203