1*6e54367aSthorpej /* $NetBSD: tegra_rtc.c,v 1.8 2021/01/27 03:10:19 thorpej Exp $ */
28827e789Sjmcneill
38827e789Sjmcneill /*-
48827e789Sjmcneill * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca>
58827e789Sjmcneill * All rights reserved.
68827e789Sjmcneill *
78827e789Sjmcneill * Redistribution and use in source and binary forms, with or without
88827e789Sjmcneill * modification, are permitted provided that the following conditions
98827e789Sjmcneill * are met:
108827e789Sjmcneill * 1. Redistributions of source code must retain the above copyright
118827e789Sjmcneill * notice, this list of conditions and the following disclaimer.
128827e789Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright
138827e789Sjmcneill * notice, this list of conditions and the following disclaimer in the
148827e789Sjmcneill * documentation and/or other materials provided with the distribution.
158827e789Sjmcneill *
168827e789Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
178827e789Sjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
188827e789Sjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
198827e789Sjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
208827e789Sjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
218827e789Sjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
228827e789Sjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
238827e789Sjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
248827e789Sjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
258827e789Sjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
268827e789Sjmcneill * SUCH DAMAGE.
278827e789Sjmcneill */
288827e789Sjmcneill
298827e789Sjmcneill #include <sys/cdefs.h>
30*6e54367aSthorpej __KERNEL_RCSID(0, "$NetBSD: tegra_rtc.c,v 1.8 2021/01/27 03:10:19 thorpej Exp $");
318827e789Sjmcneill
328827e789Sjmcneill #include <sys/param.h>
338827e789Sjmcneill #include <sys/bus.h>
348827e789Sjmcneill #include <sys/device.h>
358827e789Sjmcneill #include <sys/intr.h>
368827e789Sjmcneill #include <sys/systm.h>
378827e789Sjmcneill #include <sys/kernel.h>
388827e789Sjmcneill #include <sys/kmem.h>
398827e789Sjmcneill
408827e789Sjmcneill #include <dev/clock_subr.h>
418827e789Sjmcneill
428827e789Sjmcneill #include <arm/nvidia/tegra_reg.h>
438827e789Sjmcneill #include <arm/nvidia/tegra_rtcreg.h>
448827e789Sjmcneill #include <arm/nvidia/tegra_var.h>
458827e789Sjmcneill
46d59db8d0Sjmcneill #include <dev/fdt/fdtvar.h>
47d59db8d0Sjmcneill
488827e789Sjmcneill static int tegra_rtc_match(device_t, cfdata_t, void *);
498827e789Sjmcneill static void tegra_rtc_attach(device_t, device_t, void *);
508827e789Sjmcneill
518827e789Sjmcneill struct tegra_rtc_softc {
528827e789Sjmcneill device_t sc_dev;
538827e789Sjmcneill bus_space_tag_t sc_bst;
548827e789Sjmcneill bus_space_handle_t sc_bsh;
558827e789Sjmcneill
568827e789Sjmcneill struct todr_chip_handle sc_todr;
578827e789Sjmcneill };
588827e789Sjmcneill
598827e789Sjmcneill static int tegra_rtc_gettime(todr_chip_handle_t, struct timeval *);
608827e789Sjmcneill static int tegra_rtc_settime(todr_chip_handle_t, struct timeval *);
618827e789Sjmcneill
628827e789Sjmcneill CFATTACH_DECL_NEW(tegra_rtc, sizeof(struct tegra_rtc_softc),
638827e789Sjmcneill tegra_rtc_match, tegra_rtc_attach, NULL, NULL);
648827e789Sjmcneill
658827e789Sjmcneill #define RTC_READ(sc, reg) \
668827e789Sjmcneill bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
678827e789Sjmcneill #define RTC_WRITE(sc, reg, val) \
688827e789Sjmcneill bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
698827e789Sjmcneill
70*6e54367aSthorpej static const struct device_compatible_entry compat_data[] = {
71*6e54367aSthorpej { .compat = "nvidia,tegra210-rtc" },
72*6e54367aSthorpej { .compat = "nvidia,tegra124-rtc" },
73*6e54367aSthorpej { .compat = "nvidia,tegra20-rtc" },
74*6e54367aSthorpej DEVICE_COMPAT_EOL
75*6e54367aSthorpej };
76*6e54367aSthorpej
778827e789Sjmcneill static int
tegra_rtc_match(device_t parent,cfdata_t cf,void * aux)788827e789Sjmcneill tegra_rtc_match(device_t parent, cfdata_t cf, void *aux)
798827e789Sjmcneill {
80d59db8d0Sjmcneill struct fdt_attach_args * const faa = aux;
81d59db8d0Sjmcneill
82*6e54367aSthorpej return of_compatible_match(faa->faa_phandle, compat_data);
838827e789Sjmcneill }
848827e789Sjmcneill
858827e789Sjmcneill static void
tegra_rtc_attach(device_t parent,device_t self,void * aux)868827e789Sjmcneill tegra_rtc_attach(device_t parent, device_t self, void *aux)
878827e789Sjmcneill {
888827e789Sjmcneill struct tegra_rtc_softc * const sc = device_private(self);
89d59db8d0Sjmcneill struct fdt_attach_args * const faa = aux;
90d59db8d0Sjmcneill bus_addr_t addr;
91d59db8d0Sjmcneill bus_size_t size;
92d59db8d0Sjmcneill int error;
93d59db8d0Sjmcneill
94d59db8d0Sjmcneill if (fdtbus_get_reg(faa->faa_phandle, 0, &addr, &size) != 0) {
95d59db8d0Sjmcneill aprint_error(": couldn't get registers\n");
96d59db8d0Sjmcneill return;
97d59db8d0Sjmcneill }
988827e789Sjmcneill
998827e789Sjmcneill sc->sc_dev = self;
100d59db8d0Sjmcneill sc->sc_bst = faa->faa_bst;
101d59db8d0Sjmcneill error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh);
102d59db8d0Sjmcneill if (error) {
1032e65b46dSskrll aprint_error(": couldn't map %#" PRIxBUSADDR ": %d", addr, error);
104d59db8d0Sjmcneill return;
105d59db8d0Sjmcneill }
1068827e789Sjmcneill
1078827e789Sjmcneill aprint_naive("\n");
1088827e789Sjmcneill aprint_normal(": RTC\n");
1098827e789Sjmcneill
1108827e789Sjmcneill sc->sc_todr.todr_gettime = tegra_rtc_gettime;
1118827e789Sjmcneill sc->sc_todr.todr_settime = tegra_rtc_settime;
1128827e789Sjmcneill sc->sc_todr.cookie = sc;
113b06fec0aSjmcneill fdtbus_todr_attach(self, faa->faa_phandle, &sc->sc_todr);
1148827e789Sjmcneill }
1158827e789Sjmcneill
1168827e789Sjmcneill static int
tegra_rtc_gettime(todr_chip_handle_t tch,struct timeval * tv)1178827e789Sjmcneill tegra_rtc_gettime(todr_chip_handle_t tch, struct timeval *tv)
1188827e789Sjmcneill {
1198827e789Sjmcneill struct tegra_rtc_softc * const sc = tch->cookie;
1208827e789Sjmcneill
1218827e789Sjmcneill tv->tv_sec = RTC_READ(sc, RTC_SECONDS_REG);
1228827e789Sjmcneill tv->tv_usec = 0;
1238827e789Sjmcneill
1248827e789Sjmcneill return 0;
1258827e789Sjmcneill }
1268827e789Sjmcneill
1278827e789Sjmcneill static int
tegra_rtc_settime(todr_chip_handle_t tch,struct timeval * tv)1288827e789Sjmcneill tegra_rtc_settime(todr_chip_handle_t tch, struct timeval *tv)
1298827e789Sjmcneill {
1308827e789Sjmcneill struct tegra_rtc_softc * const sc = tch->cookie;
1318827e789Sjmcneill int retry = 500;
1328827e789Sjmcneill
1338827e789Sjmcneill while (--retry > 0) {
1348827e789Sjmcneill if ((RTC_READ(sc, RTC_BUSY_REG) & RTC_BUSY_STATUS) == 0)
1358827e789Sjmcneill break;
1368827e789Sjmcneill delay(1);
1378827e789Sjmcneill }
1388827e789Sjmcneill if (retry == 0) {
1398827e789Sjmcneill device_printf(sc->sc_dev, "RTC write failed (BUSY)\n");
1408827e789Sjmcneill return ETIMEDOUT;
1418827e789Sjmcneill }
1428827e789Sjmcneill
1438827e789Sjmcneill RTC_WRITE(sc, RTC_SECONDS_REG, tv->tv_sec);
1448827e789Sjmcneill
1458827e789Sjmcneill return 0;
1468827e789Sjmcneill }
147