1/*	$OpenBSD: mvortc.c,v 1.1 2023/03/02 09:57:43 jmatthew Exp $	*/
2/*
3 * Copyright (c) 2022 Jonathan Matthew <jmatthew@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/fdt.h>
28
29#include <dev/clock_subr.h>
30
31extern todr_chip_handle_t todr_handle;
32
33/* Registers. */
34#define RTC_STATUS		0x0000
35#define RTC_TIME		0x000c
36#define RTC_CONF_TEST		0x001c
37
38#define RTC_TIMING_CTL		0x0000
39#define  RTC_PERIOD_SHIFT	0
40#define  RTC_PERIOD_MASK	(0x3ff << RTC_PERIOD_SHIFT)
41#define  RTC_READ_DELAY_SHIFT 	26
42#define  RTC_READ_DELAY_MASK 	(0x1f << RTC_READ_DELAY_SHIFT)
43
44
45#define HREAD4(sc, reg)							\
46	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
47#define HWRITE4(sc, reg, val)						\
48	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
49
50struct mvortc_softc {
51	struct device		sc_dev;
52	bus_space_tag_t		sc_iot;
53	bus_space_handle_t	sc_ioh;
54	bus_space_handle_t	sc_soc_ioh;
55
56	struct todr_chip_handle sc_todr;
57};
58
59int mvortc_match(struct device *, void *, void *);
60void mvortc_attach(struct device *, struct device *, void *);
61
62const struct cfattach	mvortc_ca = {
63	sizeof (struct mvortc_softc), mvortc_match, mvortc_attach
64};
65
66struct cfdriver mvortc_cd = {
67	NULL, "mvortc", DV_DULL
68};
69
70int	mvortc_gettime(struct todr_chip_handle *, struct timeval *);
71int	mvortc_settime(struct todr_chip_handle *, struct timeval *);
72
73int
74mvortc_match(struct device *parent, void *match, void *aux)
75{
76	struct fdt_attach_args *faa = aux;
77
78	return OF_is_compatible(faa->fa_node, "marvell,armada-380-rtc");
79}
80
81void
82mvortc_attach(struct device *parent, struct device *self, void *aux)
83{
84	struct mvortc_softc *sc = (struct mvortc_softc *)self;
85	struct fdt_attach_args *faa = aux;
86	uint32_t reg;
87
88	if (faa->fa_nreg < 2) {
89		printf(": no registers\n");
90		return;
91	}
92
93	sc->sc_iot = faa->fa_iot;
94
95	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
96	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
97		printf(": can't map registers\n");
98		return;
99	}
100
101	if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr,
102	    faa->fa_reg[1].size, 0, &sc->sc_soc_ioh)) {
103		bus_space_unmap(sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size);
104		printf(": can't map soc registers\n");
105		return;
106	}
107
108	/* Magic to make bus access actually work. */
109	reg = bus_space_read_4(sc->sc_iot, sc->sc_soc_ioh, RTC_TIMING_CTL);
110	reg &= ~RTC_PERIOD_MASK;
111	reg |= (0x3ff << RTC_PERIOD_SHIFT);
112	reg &= ~RTC_READ_DELAY_MASK;
113	reg |= (0x1f << RTC_READ_DELAY_SHIFT);
114	bus_space_write_4(sc->sc_iot, sc->sc_soc_ioh, RTC_TIMING_CTL, reg);
115	printf("\n");
116
117	sc->sc_todr.cookie = sc;
118	sc->sc_todr.todr_gettime = mvortc_gettime;
119	sc->sc_todr.todr_settime = mvortc_settime;
120	todr_handle = &sc->sc_todr;
121}
122
123uint32_t
124mvortc_read(struct mvortc_softc *sc, int reg)
125{
126	uint32_t sample, mode;
127	uint32_t samples[100];
128	int counts[100];
129	int i, j, last;
130
131	memset(samples, 0, sizeof(samples));
132	memset(counts, 0, sizeof(counts));
133	last = 0;
134	for (i = 0; i < nitems(samples); i++) {
135		sample = HREAD4(sc, reg);
136
137		for (j = 0; j < last; j++) {
138			if (samples[j] == sample)
139				break;
140		}
141
142		if (j < last) {
143			counts[j]++;
144		} else {
145			samples[last] = sample;
146			counts[last] = 1;
147			last++;
148		}
149	}
150
151	j = 0;
152	mode = 0;
153	for (i = 0; i < last; i++) {
154		if (counts[i] > mode) {
155			mode = counts[i];
156			j = i;
157		}
158	}
159
160	return samples[j];
161}
162
163void
164mvortc_write(struct mvortc_softc *sc, int reg, uint32_t val)
165{
166	HWRITE4(sc, RTC_STATUS, 0);
167	HWRITE4(sc, RTC_STATUS, 0);
168	HWRITE4(sc, reg, val);
169	delay(5);
170}
171
172int
173mvortc_gettime(struct todr_chip_handle *handle, struct timeval *tv)
174{
175	struct mvortc_softc *sc = handle->cookie;
176
177	tv->tv_sec = mvortc_read(sc, RTC_TIME);
178	tv->tv_usec = 0;
179	return 0;
180}
181
182int
183mvortc_settime(struct todr_chip_handle *handle, struct timeval *tv)
184{
185	struct mvortc_softc *sc = handle->cookie;
186	uint32_t reg;
187
188	reg = mvortc_read(sc, RTC_CONF_TEST);
189	if (reg & 0xff) {
190		mvortc_write(sc, RTC_CONF_TEST, 0);
191		delay(500);
192		mvortc_write(sc, RTC_TIME, 0);
193		mvortc_write(sc, RTC_STATUS, 0x03);
194	}
195
196	mvortc_write(sc, RTC_TIME, tv->tv_sec);
197	return 0;
198}
199