1/*	$OpenBSD: mk48txx.c,v 1.10 2022/10/16 01:22:39 jsg Exp $	*/
2/*	$NetBSD: mk48txx.c,v 1.7 2001/04/08 17:05:10 tsutsui Exp $ */
3/*-
4 * Copyright (c) 2000 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Paul Kranenburg.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 * Mostek MK48T02, MK48T08, MK48T59 time-of-day chip subroutines.
34 */
35
36#include <sys/param.h>
37#include <sys/malloc.h>
38#include <sys/systm.h>
39#include <sys/errno.h>
40
41#include <machine/bus.h>
42#include <dev/clock_subr.h>
43#include <dev/ic/mk48txxreg.h>
44
45
46struct mk48txx {
47	bus_space_tag_t	mk_bt;		/* bus tag & handle */
48	bus_space_handle_t mk_bh;	/* */
49	bus_size_t	mk_nvramsz;	/* Size of NVRAM on the chip */
50	bus_size_t	mk_clkoffset;	/* Offset in NVRAM to clock bits */
51	u_int		mk_year0;	/* What year is represented on the system
52					   by the chip's year counter at 0 */
53};
54
55int mk48txx_gettime(todr_chip_handle_t, struct timeval *);
56int mk48txx_settime(todr_chip_handle_t, struct timeval *);
57
58int mk48txx_auto_century_adjust = 1;
59
60struct {
61	const char *name;
62	bus_size_t nvramsz;
63	bus_size_t clkoff;
64	int flags;
65#define MK48TXX_EXT_REGISTERS	1	/* Has extended register set */
66} mk48txx_models[] = {
67	{ "mk48t02", MK48T02_CLKSZ, MK48T02_CLKOFF, 0 },
68	{ "mk48t08", MK48T08_CLKSZ, MK48T08_CLKOFF, 0 },
69	{ "mk48t18", MK48T18_CLKSZ, MK48T18_CLKOFF, 0 },
70	{ "mk48t59", MK48T59_CLKSZ, MK48T59_CLKOFF, MK48TXX_EXT_REGISTERS },
71};
72
73todr_chip_handle_t
74mk48txx_attach(bus_space_tag_t bt, bus_space_handle_t bh, const char *model,
75    int year0)
76{
77	todr_chip_handle_t handle;
78	struct mk48txx *mk;
79	bus_size_t nvramsz, clkoff;
80	int sz;
81	int i;
82
83	printf(": %s", model);
84
85	i = nitems(mk48txx_models);
86	while (--i >= 0) {
87		if (strcmp(model, mk48txx_models[i].name) == 0) {
88			nvramsz = mk48txx_models[i].nvramsz;
89			clkoff = mk48txx_models[i].clkoff;
90			break;
91		}
92	}
93	if (i < 0) {
94		printf(": unsupported model");
95		return (NULL);
96	}
97
98	sz = ALIGN(sizeof(struct todr_chip_handle)) + sizeof(struct mk48txx);
99	handle = malloc(sz, M_DEVBUF, M_NOWAIT);
100	if (handle == NULL) {
101		printf(": failed to allocate memory");
102		return NULL;
103	}
104	mk = (struct mk48txx *)((u_long)handle +
105				 ALIGN(sizeof(struct todr_chip_handle)));
106	handle->cookie = mk;
107	handle->todr_gettime = mk48txx_gettime;
108	handle->todr_settime = mk48txx_settime;
109	handle->todr_setwen = NULL;
110	handle->todr_quality = 0;
111	mk->mk_bt = bt;
112	mk->mk_bh = bh;
113	mk->mk_nvramsz = nvramsz;
114	mk->mk_clkoffset = clkoff;
115	mk->mk_year0 = year0;
116
117	return (handle);
118}
119
120/*
121 * Get time-of-day and convert to a `struct timeval'
122 * Return 0 on success; an error number otherwise.
123 */
124int
125mk48txx_gettime(todr_chip_handle_t handle, struct timeval *tv)
126{
127	struct mk48txx *mk = handle->cookie;
128	bus_space_tag_t bt = mk->mk_bt;
129	bus_space_handle_t bh = mk->mk_bh;
130	bus_size_t clkoff = mk->mk_clkoffset;
131	struct clock_ymdhms dt;
132	int year;
133	u_int8_t csr;
134
135	todr_wenable(handle, 1);
136
137	/* enable read (stop time) */
138	csr = bus_space_read_1(bt, bh, clkoff + MK48TXX_ICSR);
139	csr |= MK48TXX_CSR_READ;
140	bus_space_write_1(bt, bh, clkoff + MK48TXX_ICSR, csr);
141
142	dt.dt_sec = FROMBCD(bus_space_read_1(bt, bh, clkoff + MK48TXX_ISEC));
143	dt.dt_min = FROMBCD(bus_space_read_1(bt, bh, clkoff + MK48TXX_IMIN));
144	dt.dt_hour = FROMBCD(bus_space_read_1(bt, bh, clkoff + MK48TXX_IHOUR));
145	dt.dt_day = FROMBCD(bus_space_read_1(bt, bh, clkoff + MK48TXX_IDAY));
146	dt.dt_wday = FROMBCD(bus_space_read_1(bt, bh, clkoff + MK48TXX_IWDAY));
147	dt.dt_mon = FROMBCD(bus_space_read_1(bt, bh, clkoff + MK48TXX_IMON));
148	year = FROMBCD(bus_space_read_1(bt, bh, clkoff + MK48TXX_IYEAR));
149
150	year += mk->mk_year0;
151	if (year < POSIX_BASE_YEAR && mk48txx_auto_century_adjust != 0 &&
152	    mk->mk_year0 >= POSIX_BASE_YEAR)
153		year += 100;
154
155	dt.dt_year = year;
156
157	/* time wears on */
158	csr = bus_space_read_1(bt, bh, clkoff + MK48TXX_ICSR);
159	csr &= ~MK48TXX_CSR_READ;
160	bus_space_write_1(bt, bh, clkoff + MK48TXX_ICSR, csr);
161	todr_wenable(handle, 0);
162
163	/* simple sanity checks */
164	if (dt.dt_mon > 12 || dt.dt_day > 31 ||
165	    dt.dt_hour >= 24 || dt.dt_min >= 60 || dt.dt_sec >= 60)
166		return (1);
167
168	tv->tv_sec = clock_ymdhms_to_secs(&dt);
169	tv->tv_usec = 0;
170	return (0);
171}
172
173/*
174 * Set the time-of-day clock based on the value of the `struct timeval' arg.
175 * Return 0 on success; an error number otherwise.
176 */
177int
178mk48txx_settime(todr_chip_handle_t handle, struct timeval *tv)
179{
180	struct mk48txx *mk = handle->cookie;
181	bus_space_tag_t bt = mk->mk_bt;
182	bus_space_handle_t bh = mk->mk_bh;
183	bus_size_t clkoff = mk->mk_clkoffset;
184	struct clock_ymdhms dt;
185	u_int8_t csr;
186	int year;
187
188	/* Note: we ignore `tv_usec' */
189	clock_secs_to_ymdhms(tv->tv_sec, &dt);
190
191	year = dt.dt_year - mk->mk_year0;
192	if (year > 99 && mk48txx_auto_century_adjust != 0)
193		year -= 100;
194
195	todr_wenable(handle, 1);
196	/* enable write */
197	csr = bus_space_read_1(bt, bh, clkoff + MK48TXX_ICSR);
198	csr |= MK48TXX_CSR_WRITE;
199	bus_space_write_1(bt, bh, clkoff + MK48TXX_ICSR, csr);
200
201	bus_space_write_1(bt, bh, clkoff + MK48TXX_ISEC, TOBCD(dt.dt_sec));
202	bus_space_write_1(bt, bh, clkoff + MK48TXX_IMIN, TOBCD(dt.dt_min));
203	bus_space_write_1(bt, bh, clkoff + MK48TXX_IHOUR, TOBCD(dt.dt_hour));
204	bus_space_write_1(bt, bh, clkoff + MK48TXX_IWDAY, TOBCD(dt.dt_wday));
205	bus_space_write_1(bt, bh, clkoff + MK48TXX_IDAY, TOBCD(dt.dt_day));
206	bus_space_write_1(bt, bh, clkoff + MK48TXX_IMON, TOBCD(dt.dt_mon));
207	bus_space_write_1(bt, bh, clkoff + MK48TXX_IYEAR, TOBCD(year));
208
209	/* load them up */
210	csr = bus_space_read_1(bt, bh, clkoff + MK48TXX_ICSR);
211	csr &= ~MK48TXX_CSR_WRITE;
212	bus_space_write_1(bt, bh, clkoff + MK48TXX_ICSR, csr);
213	todr_wenable(handle, 0);
214	return (0);
215}
216
217int
218mk48txx_get_nvram_size(todr_chip_handle_t handle, bus_size_t *vp)
219{
220	struct mk48txx *mk = handle->cookie;
221	*vp = mk->mk_nvramsz;
222	return (0);
223}
224