1/*	$OpenBSD: mpfclock.c,v 1.1 2022/01/05 03:32:44 visa Exp $	*/
2
3/*
4 * Copyright (c) 2022 Visa Hankala
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19/*
20 * Driver for PolarFire SoC MSS clock controller.
21 */
22
23#include <sys/param.h>
24#include <sys/systm.h>
25#include <sys/device.h>
26#include <sys/mutex.h>
27
28#include <machine/bus.h>
29#include <machine/fdt.h>
30
31#include <dev/ofw/fdt.h>
32#include <dev/ofw/openfirm.h>
33#include <dev/ofw/ofw_clock.h>
34
35extern void (*cpuresetfn)(void);
36
37#define CLOCK_CONFIG_CR			0x0008
38#define  CLOCK_CONFIG_CR_AHB_DIV_SHIFT		4
39#define  CLOCK_CONFIG_CR_AXI_DIV_SHIFT		2
40#define  CLOCK_CONFIG_CR_CPU_DIV_SHIFT		0
41#define  CLOCK_CONFIG_CR_DIV_MASK		0x3
42#define MSS_RESET_CR			0x0018
43#define SUBBLK_CLOCK_CR			0x0084
44#define SUBBLK_RESET_CR			0x0088
45
46#define CLK_CPU				0
47#define CLK_AXI				1
48#define CLK_AHB				2
49#define CLK_ENVM			3
50#define CLK_MAC0			4
51#define CLK_MAC1			5
52#define CLK_MMC				6
53#define CLK_TIMER			7
54#define CLK_MMUART0			8
55#define CLK_MMUART1			9
56#define CLK_MMUART2			10
57#define CLK_MMUART3			11
58#define CLK_MMUART4			12
59#define CLK_SPI0			13
60#define CLK_SPI1			14
61#define CLK_I2C0			15
62#define CLK_I2C1			16
63#define CLK_CAN0			17
64#define CLK_CAN1			18
65#define CLK_USB				19
66#define CLK_RESERVED			20	/* FPGA in SUBBLK_RESET_CR */
67#define CLK_RTC				21
68#define CLK_QSPI			22
69#define CLK_GPIO0			23
70#define CLK_GPIO1			24
71#define CLK_GPIO2			25
72#define CLK_DDRC			26
73#define CLK_FIC0			27
74#define CLK_FIC1			28
75#define CLK_FIC2			29
76#define CLK_FIC3			30
77#define CLK_ATHENA			31
78#define CLK_CFM				32
79
80struct mpfclock_softc {
81	struct device		sc_dev;
82	bus_space_tag_t		sc_iot;
83	bus_space_handle_t	sc_ioh;
84	uint32_t		sc_clkcfg;
85	uint32_t		sc_refclk;
86
87	struct clock_device	sc_cd;
88};
89
90#define HREAD4(sc, reg) \
91	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
92#define HWRITE4(sc, reg, val) \
93	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
94
95int	mpfclock_match(struct device *, void *, void *);
96void	mpfclock_attach(struct device *, struct device *, void *);
97
98void	mpfclock_enable(void *, uint32_t *, int);
99uint32_t mpfclock_get_frequency(void *, uint32_t *);
100int	mpfclock_set_frequency(void *, uint32_t *, uint32_t);
101
102void	mpfclock_cpureset(void);
103
104const struct cfattach mpfclock_ca = {
105	sizeof(struct mpfclock_softc), mpfclock_match, mpfclock_attach
106};
107
108struct cfdriver mpfclock_cd = {
109	NULL, "mpfclock", DV_DULL
110};
111
112struct mutex		mpfclock_mtx = MUTEX_INITIALIZER(IPL_HIGH);
113struct mpfclock_softc	*mpfclock_sc;
114
115int
116mpfclock_match(struct device *parent, void *match, void *aux)
117{
118	struct fdt_attach_args *faa = aux;
119
120	if (faa->fa_nreg < 1)
121		return 0;
122	return OF_is_compatible(faa->fa_node, "microchip,mpfs-clkcfg");
123}
124
125void
126mpfclock_attach(struct device *parent, struct device *self, void *aux)
127{
128	struct fdt_attach_args *faa = aux;
129	struct mpfclock_softc *sc = (struct mpfclock_softc *)self;
130
131	sc->sc_refclk = clock_get_frequency_idx(faa->fa_node, 0);
132	if (sc->sc_refclk == 0) {
133		printf(": can't get refclk frequency\n");
134		return;
135	}
136
137	sc->sc_iot = faa->fa_iot;
138	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
139	    faa->fa_reg[0].size, 0, &sc->sc_ioh) != 0) {
140		printf(": can't map registers\n");
141		return;
142	}
143
144	sc->sc_clkcfg = HREAD4(sc, CLOCK_CONFIG_CR);
145
146	printf(": %u MHz ref clock\n", (sc->sc_refclk + 500000) / 1000000);
147
148	sc->sc_cd.cd_node = faa->fa_node;
149	sc->sc_cd.cd_cookie = sc;
150	sc->sc_cd.cd_enable = mpfclock_enable;
151	sc->sc_cd.cd_get_frequency = mpfclock_get_frequency;
152	sc->sc_cd.cd_set_frequency = mpfclock_set_frequency;
153	clock_register(&sc->sc_cd);
154
155	mpfclock_sc = sc;
156	cpuresetfn = mpfclock_cpureset;
157}
158
159uint32_t
160mpfclock_get_frequency(void *cookie, uint32_t *cells)
161{
162	struct mpfclock_softc *sc = cookie;
163	uint32_t div, shift;
164	uint32_t idx = cells[0];
165
166	if (idx == CLK_MMC)
167		return 200000000;
168
169	if (idx > CLK_AHB)
170		idx = CLK_AHB;
171
172	switch (idx) {
173	case CLK_CPU:
174		shift = CLOCK_CONFIG_CR_CPU_DIV_SHIFT;
175		break;
176	case CLK_AXI:
177		shift = CLOCK_CONFIG_CR_AXI_DIV_SHIFT;
178		break;
179	case CLK_AHB:
180		shift = CLOCK_CONFIG_CR_AHB_DIV_SHIFT;
181		break;
182	default:
183		panic("%s: invalid idx %u\n", __func__, idx);
184	}
185
186	div = 1U << ((sc->sc_clkcfg >> shift) & CLOCK_CONFIG_CR_DIV_MASK);
187
188	return sc->sc_refclk / div;
189}
190
191int
192mpfclock_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
193{
194	return -1;
195}
196
197void
198mpfclock_enable(void *cookie, uint32_t *cells, int on)
199{
200	struct mpfclock_softc *sc = cookie;
201	uint32_t idx = cells[0];
202	uint32_t bit, val;
203
204	if (idx < CLK_ENVM || idx - CLK_ENVM > 31)
205		return;
206	bit = 1U << (idx - CLK_ENVM);
207
208	mtx_enter(&mpfclock_mtx);
209	if (on) {
210		val = HREAD4(sc, SUBBLK_CLOCK_CR);
211		val |= bit;
212		HWRITE4(sc, SUBBLK_CLOCK_CR, val);
213
214		val = HREAD4(sc, SUBBLK_RESET_CR);
215		val &= ~bit;
216		HWRITE4(sc, SUBBLK_RESET_CR, val);
217	} else {
218		val = HREAD4(sc, SUBBLK_RESET_CR);
219		val |= bit;
220		HWRITE4(sc, SUBBLK_RESET_CR, val);
221
222		val = HREAD4(sc, SUBBLK_CLOCK_CR);
223		val &= ~bit;
224		HWRITE4(sc, SUBBLK_CLOCK_CR, val);
225	}
226	mtx_leave(&mpfclock_mtx);
227}
228
229void
230mpfclock_cpureset(void)
231{
232	struct mpfclock_softc *sc = mpfclock_sc;
233
234	HWRITE4(sc, MSS_RESET_CR, 0xdead);
235}
236