1*386e06c2Sandvar /* $NetBSD: pxa2x0_rtc.c,v 1.8 2024/02/24 12:04:16 andvar Exp $ */
28d4fbc45Snonaka
38d4fbc45Snonaka /*
48d4fbc45Snonaka * Copyright (c) 2007 NONAKA Kimihiro <nonaka@netbsd.org>
58d4fbc45Snonaka *
68d4fbc45Snonaka * Redistribution and use in source and binary forms, with or without
78d4fbc45Snonaka * modification, are permitted provided that the following conditions
88d4fbc45Snonaka * are met:
98d4fbc45Snonaka * 1. Redistributions of source code must retain the above copyright
108d4fbc45Snonaka * notice, this list of conditions and the following disclaimer.
118d4fbc45Snonaka * 2. Redistributions in binary form must reproduce the above copyright
128d4fbc45Snonaka * notice, this list of conditions and the following disclaimer in the
138d4fbc45Snonaka * documentation and/or other materials provided with the distribution.
148d4fbc45Snonaka *
158d4fbc45Snonaka * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
168d4fbc45Snonaka * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
178d4fbc45Snonaka * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
188d4fbc45Snonaka * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
198d4fbc45Snonaka * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
208d4fbc45Snonaka * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
218d4fbc45Snonaka * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
228d4fbc45Snonaka */
238d4fbc45Snonaka
248d4fbc45Snonaka #include <sys/cdefs.h>
25*386e06c2Sandvar __KERNEL_RCSID(0, "$NetBSD: pxa2x0_rtc.c,v 1.8 2024/02/24 12:04:16 andvar Exp $");
268d4fbc45Snonaka
278d4fbc45Snonaka #include <sys/param.h>
288d4fbc45Snonaka #include <sys/systm.h>
298d4fbc45Snonaka #include <sys/device.h>
308d4fbc45Snonaka #include <sys/kernel.h>
318d4fbc45Snonaka
328d4fbc45Snonaka #include <dev/clock_subr.h>
338d4fbc45Snonaka
34ed9977b1Sdyoung #include <sys/bus.h>
358d4fbc45Snonaka
368d4fbc45Snonaka #include <arm/xscale/pxa2x0cpu.h>
378d4fbc45Snonaka #include <arm/xscale/pxa2x0reg.h>
388d4fbc45Snonaka #include <arm/xscale/pxa2x0var.h>
398d4fbc45Snonaka
408d4fbc45Snonaka #ifdef PXARTC_DEBUG
418d4fbc45Snonaka #define DPRINTF(s) printf s
428d4fbc45Snonaka #else
438d4fbc45Snonaka #define DPRINTF(s)
448d4fbc45Snonaka #endif
458d4fbc45Snonaka
468d4fbc45Snonaka struct pxartc_softc {
4764488ec1Snonaka device_t sc_dev;
488d4fbc45Snonaka bus_space_tag_t sc_iot;
498d4fbc45Snonaka bus_space_handle_t sc_ioh;
508d4fbc45Snonaka
518d4fbc45Snonaka struct todr_chip_handle sc_todr;
528d4fbc45Snonaka
538d4fbc45Snonaka int sc_flags;
548d4fbc45Snonaka #define FLAG_WRISTWATCH (1 << 0)
558d4fbc45Snonaka };
568d4fbc45Snonaka
57cbab9cadSchs static int pxartc_match(device_t, cfdata_t, void *);
58cbab9cadSchs static void pxartc_attach(device_t, device_t, void *);
598d4fbc45Snonaka
6064488ec1Snonaka CFATTACH_DECL_NEW(pxartc, sizeof(struct pxartc_softc),
618d4fbc45Snonaka pxartc_match, pxartc_attach, NULL, NULL);
628d4fbc45Snonaka
638d4fbc45Snonaka /* todr(9) interface */
64471e528bStsutsui static int pxartc_todr_gettime(todr_chip_handle_t, struct timeval *);
65471e528bStsutsui static int pxartc_todr_settime(todr_chip_handle_t, struct timeval *);
6616726ef9Sthorpej static int pxartc_wristwatch_gettime(todr_chip_handle_t, struct clock_ymdhms *);
6716726ef9Sthorpej static int pxartc_wristwatch_settime(todr_chip_handle_t, struct clock_ymdhms *);
688d4fbc45Snonaka
698d4fbc45Snonaka static int
pxartc_match(device_t parent,cfdata_t cf,void * aux)70cbab9cadSchs pxartc_match(device_t parent, cfdata_t cf, void *aux)
718d4fbc45Snonaka {
728d4fbc45Snonaka struct pxaip_attach_args *pxa = aux;
738d4fbc45Snonaka
74eb34e284Skiyohara if (strcmp(pxa->pxa_name, cf->cf_name) != 0)
758d4fbc45Snonaka return 0;
768d4fbc45Snonaka
7764488ec1Snonaka if (pxa->pxa_size == 0) {
7864488ec1Snonaka pxa->pxa_size =
7964488ec1Snonaka CPU_IS_PXA270 ? PXA270_RTC_SIZE : PXA250_RTC_SIZE;
8064488ec1Snonaka }
818d4fbc45Snonaka return 1;
828d4fbc45Snonaka }
838d4fbc45Snonaka
848d4fbc45Snonaka static void
pxartc_attach(device_t parent,device_t self,void * aux)85cbab9cadSchs pxartc_attach(device_t parent, device_t self, void *aux)
868d4fbc45Snonaka {
8764488ec1Snonaka struct pxartc_softc *sc = device_private(self);
888d4fbc45Snonaka struct pxaip_attach_args *pxa = aux;
898d4fbc45Snonaka
9064488ec1Snonaka sc->sc_dev = self;
918d4fbc45Snonaka sc->sc_iot = pxa->pxa_iot;
928d4fbc45Snonaka
9364488ec1Snonaka aprint_normal(": Real-time Clock\n");
948d4fbc45Snonaka
95eb34e284Skiyohara if (bus_space_map(sc->sc_iot, pxa->pxa_addr, pxa->pxa_size, 0,
968d4fbc45Snonaka &sc->sc_ioh)) {
978d4fbc45Snonaka aprint_error("%s: couldn't map registers\n",
9864488ec1Snonaka device_xname(sc->sc_dev));
998d4fbc45Snonaka return;
1008d4fbc45Snonaka }
1018d4fbc45Snonaka
10216726ef9Sthorpej memset(&sc->sc_todr, 0, sizeof(sc->sc_todr));
10316726ef9Sthorpej sc->sc_todr.cookie = sc;
1048d4fbc45Snonaka if (pxa->pxa_size == PXA270_RTC_SIZE) {
1058d4fbc45Snonaka aprint_normal("%s: using wristwatch register\n",
10664488ec1Snonaka device_xname(sc->sc_dev));
1078d4fbc45Snonaka sc->sc_flags |= FLAG_WRISTWATCH;
10816726ef9Sthorpej sc->sc_todr.todr_gettime_ymdhms = pxartc_wristwatch_gettime;
10916726ef9Sthorpej sc->sc_todr.todr_settime_ymdhms = pxartc_wristwatch_settime;
11016726ef9Sthorpej } else {
1118d4fbc45Snonaka sc->sc_todr.todr_gettime = pxartc_todr_gettime;
1128d4fbc45Snonaka sc->sc_todr.todr_settime = pxartc_todr_settime;
11316726ef9Sthorpej }
1148d4fbc45Snonaka
1158d4fbc45Snonaka todr_attach(&sc->sc_todr);
1168d4fbc45Snonaka }
1178d4fbc45Snonaka
1188d4fbc45Snonaka static int
pxartc_todr_gettime(todr_chip_handle_t ch,struct timeval * tv)119471e528bStsutsui pxartc_todr_gettime(todr_chip_handle_t ch, struct timeval *tv)
1208d4fbc45Snonaka {
1218d4fbc45Snonaka struct pxartc_softc *sc = ch->cookie;
1228d4fbc45Snonaka
1238d4fbc45Snonaka tv->tv_sec = bus_space_read_4(sc->sc_iot, sc->sc_ioh, RTC_RCNR);
1248d4fbc45Snonaka tv->tv_usec = 0;
1258d4fbc45Snonaka #ifdef PXARTC_DEBUG
126*386e06c2Sandvar struct clock_ymdhms dt;
12764488ec1Snonaka DPRINTF(("%s: RCNR = %08llx\n", device_xname(sc->sc_dev),
12864488ec1Snonaka tv->tv_sec));
1298d4fbc45Snonaka clock_secs_to_ymdhms(tv->tv_sec, &dt);
130*386e06c2Sandvar DPRINTF(("%s: %02lld/%02d/%02d %02d:%02d:%02d\n",
13164488ec1Snonaka device_xname(sc->sc_dev),
1328d4fbc45Snonaka dt.dt_year, dt.dt_mon, dt.dt_day,
1338d4fbc45Snonaka dt.dt_hour, dt.dt_min, dt.dt_sec));
1348d4fbc45Snonaka #endif
1358d4fbc45Snonaka return 0;
1368d4fbc45Snonaka }
1378d4fbc45Snonaka
1388d4fbc45Snonaka static int
pxartc_todr_settime(todr_chip_handle_t ch,struct timeval * tv)139471e528bStsutsui pxartc_todr_settime(todr_chip_handle_t ch, struct timeval *tv)
1408d4fbc45Snonaka {
1418d4fbc45Snonaka struct pxartc_softc *sc = ch->cookie;
1428d4fbc45Snonaka
1438d4fbc45Snonaka #ifdef PXARTC_DEBUG
144*386e06c2Sandvar struct clock_ymdhms dt;
14564488ec1Snonaka DPRINTF(("%s: RCNR = %08llx\n", device_xname(sc->sc_dev),
14664488ec1Snonaka tv->tv_sec));
1478d4fbc45Snonaka clock_secs_to_ymdhms(tv->tv_sec, &dt);
148*386e06c2Sandvar DPRINTF(("%s: %02lld/%02d/%02d %02d:%02d:%02d\n",
14964488ec1Snonaka device_xname(sc->sc_dev),
1508d4fbc45Snonaka dt.dt_year, dt.dt_mon, dt.dt_day,
1518d4fbc45Snonaka dt.dt_hour, dt.dt_min, dt.dt_sec));
1528d4fbc45Snonaka #endif
1538d4fbc45Snonaka bus_space_write_4(sc->sc_iot, sc->sc_ioh, RTC_RCNR, tv->tv_sec);
1548d4fbc45Snonaka #ifdef PXARTC_DEBUG
1558d4fbc45Snonaka {
1568d4fbc45Snonaka uint32_t cntr;
1578d4fbc45Snonaka delay(1);
1588d4fbc45Snonaka cntr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, RTC_RCNR);
15964488ec1Snonaka DPRINTF(("%s: new RCNR = %08x\n", device_xname(sc->sc_dev),
16064488ec1Snonaka cntr));
1618d4fbc45Snonaka clock_secs_to_ymdhms(cntr, &dt);
162*386e06c2Sandvar DPRINTF(("%s: %02lld/%02d/%02d %02d:%02d:%02d\n",
16364488ec1Snonaka device_xname(sc->sc_dev),
1648d4fbc45Snonaka dt.dt_year, dt.dt_mon, dt.dt_day,
1658d4fbc45Snonaka dt.dt_hour, dt.dt_min, dt.dt_sec));
1668d4fbc45Snonaka }
1678d4fbc45Snonaka #endif
1688d4fbc45Snonaka return 0;
1698d4fbc45Snonaka }
1708d4fbc45Snonaka
1718d4fbc45Snonaka static int
pxartc_wristwatch_gettime(todr_chip_handle_t ch,struct clock_ymdhms * dt)17216726ef9Sthorpej pxartc_wristwatch_gettime(todr_chip_handle_t ch, struct clock_ymdhms *dt)
1738d4fbc45Snonaka {
17416726ef9Sthorpej struct pxartc_softc *sc = ch->cookie;
1758d4fbc45Snonaka uint32_t dayr, yearr;
1768d4fbc45Snonaka int s;
1778d4fbc45Snonaka
17816726ef9Sthorpej DPRINTF(("%s: pxartc_wristwatch_gettime()\n",
17916726ef9Sthorpej device_xname(sc->sc_dev)));
1808d4fbc45Snonaka
1818d4fbc45Snonaka s = splhigh();
1828d4fbc45Snonaka dayr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, RTC_RDCR);
1838d4fbc45Snonaka yearr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, RTC_RYCR);
1848d4fbc45Snonaka splx(s);
1858d4fbc45Snonaka
18664488ec1Snonaka DPRINTF(("%s: RDCR = %08x, RYCR = %08x\n", device_xname(sc->sc_dev),
1878d4fbc45Snonaka dayr, yearr));
1888d4fbc45Snonaka
1898d4fbc45Snonaka dt->dt_sec = (dayr >> RDCR_SECOND_SHIFT) & RDCR_SECOND_MASK;
1908d4fbc45Snonaka dt->dt_min = (dayr >> RDCR_MINUTE_SHIFT) & RDCR_MINUTE_MASK;
1918d4fbc45Snonaka dt->dt_hour = (dayr >> RDCR_HOUR_SHIFT) & RDCR_HOUR_MASK;
1928d4fbc45Snonaka dt->dt_day = (yearr >> RYCR_DOM_SHIFT) & RYCR_DOM_MASK;
1938d4fbc45Snonaka dt->dt_mon = (yearr >> RYCR_MONTH_SHIFT) & RYCR_MONTH_MASK;
1948d4fbc45Snonaka dt->dt_year = (yearr >> RYCR_YEAR_SHIFT) & RYCR_YEAR_MASK;
1958d4fbc45Snonaka
196*386e06c2Sandvar DPRINTF(("%s: %02lld/%02d/%02d %02d:%02d:%02d\n",
19764488ec1Snonaka device_xname(sc->sc_dev),
1988d4fbc45Snonaka dt->dt_year, dt->dt_mon, dt->dt_day,
1998d4fbc45Snonaka dt->dt_hour, dt->dt_min, dt->dt_sec));
2008d4fbc45Snonaka
20116726ef9Sthorpej return 0;
2028d4fbc45Snonaka }
2038d4fbc45Snonaka
2048d4fbc45Snonaka static int
pxartc_wristwatch_settime(todr_chip_handle_t ch,struct clock_ymdhms * dt)20516726ef9Sthorpej pxartc_wristwatch_settime(todr_chip_handle_t ch, struct clock_ymdhms *dt)
2068d4fbc45Snonaka {
20716726ef9Sthorpej struct pxartc_softc *sc = ch->cookie;
2088d4fbc45Snonaka uint32_t dayr, yearr;
2098d4fbc45Snonaka uint32_t wom; /* week of month: 1=first week of month */
2108d4fbc45Snonaka int s;
2118d4fbc45Snonaka
21216726ef9Sthorpej DPRINTF(("%s: pxartc_wristwatch_settime()\n",
21316726ef9Sthorpej device_xname(sc->sc_dev)));
2148d4fbc45Snonaka
215*386e06c2Sandvar DPRINTF(("%s: %02lld/%02d/%02d %02d:%02d:%02d\n",
21664488ec1Snonaka device_xname(sc->sc_dev),
2178d4fbc45Snonaka dt->dt_year, dt->dt_mon, dt->dt_day,
2188d4fbc45Snonaka dt->dt_hour, dt->dt_min, dt->dt_sec));
2198d4fbc45Snonaka
2208d4fbc45Snonaka dayr = (dt->dt_sec & RDCR_SECOND_MASK) << RDCR_SECOND_SHIFT;
2218d4fbc45Snonaka dayr |= (dt->dt_min & RDCR_MINUTE_MASK) << RDCR_MINUTE_SHIFT;
2228d4fbc45Snonaka dayr |= (dt->dt_hour & RDCR_HOUR_MASK) << RDCR_HOUR_SHIFT;
2238d4fbc45Snonaka dayr |= ((dt->dt_wday + 1) & RDCR_DOW_MASK) << RDCR_DOW_SHIFT;
2248d4fbc45Snonaka wom = ((dt->dt_day - 1 + 6 - dt->dt_wday) / 7) + 1;
2258d4fbc45Snonaka dayr |= (wom & RDCR_WOM_MASK) << RDCR_WOM_SHIFT;
2268d4fbc45Snonaka yearr = (dt->dt_day & RYCR_DOM_MASK) << RYCR_DOM_SHIFT;
2278d4fbc45Snonaka yearr |= (dt->dt_mon & RYCR_MONTH_MASK) << RYCR_MONTH_SHIFT;
2288d4fbc45Snonaka yearr |= (dt->dt_year & RYCR_YEAR_MASK) << RYCR_YEAR_SHIFT;
2298d4fbc45Snonaka
23064488ec1Snonaka DPRINTF(("%s: RDCR = %08x, RYCR = %08x\n", device_xname(sc->sc_dev),
2318d4fbc45Snonaka dayr, yearr));
2328d4fbc45Snonaka
2338d4fbc45Snonaka /*
2348d4fbc45Snonaka * We must write RYCR register before write RDCR register.
2358d4fbc45Snonaka *
2368d4fbc45Snonaka * See PXA270 Processor Family Developer's Manual p.946
2378d4fbc45Snonaka * 21.4.2.3.1 Writing RDCR and RYCR Counter Registers with Valid Data.
2388d4fbc45Snonaka */
2398d4fbc45Snonaka s = splhigh();
2408d4fbc45Snonaka bus_space_write_4(sc->sc_iot, sc->sc_ioh, RTC_RYCR, yearr);
2418d4fbc45Snonaka bus_space_write_4(sc->sc_iot, sc->sc_ioh, RTC_RDCR, dayr);
2428d4fbc45Snonaka splx(s);
2438d4fbc45Snonaka
2448d4fbc45Snonaka #ifdef PXARTC_DEBUG
2458d4fbc45Snonaka {
2468d4fbc45Snonaka struct clock_ymdhms dummy;
247*386e06c2Sandvar pxartc_wristwatch_gettime(ch, &dummy);
2488d4fbc45Snonaka }
2498d4fbc45Snonaka #endif
2508d4fbc45Snonaka
25116726ef9Sthorpej return 0;
2528d4fbc45Snonaka }
253