Subject: kern/34683: OMAP patch 4/5: RTC driver
To: None <kern-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: Bucky Katz <bucky@picovex.com>
List: netbsd-bugs
Date: 10/01/2006 03:25:01
>Number:         34683
>Category:       kern
>Synopsis:       OMAP patch 4/5: RTC driver
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sun Oct 01 03:25:00 +0000 2006
>Originator:     Bucky Katz
>Release:        NetBSD 4.99.3
>Organization:
Picovex
>Environment:
System: NetBSD katz.picovex.com 4.99.3 NetBSD 4.99.3 (BUCKY) #5: Fri Sep 29 17:38:54 PDT 2006 root@katz.picovex.com:/usr/obj/sys/arch/evbarm/compile/BUCKY evbarm
Architecture: evbarm
Machine: OSK5912
>Description:

The attached patch adds support for the OMAP Real Time Clock.  The patch 
depends upon the patch "Generic TODR for ARM ports" sent 09/19/2006 by 
Garrett D'Amore.

>How-To-Repeat:

Attempt to build OSK5912 kernel

>Fix:

--=-=-=
Content-Type: text/x-patch
Content-Disposition: inline; filename=omap_rtc.patch
Content-Description: OMAP patch 4/5: RTC driver

Index: netbsd_quilt/src/sys/arch/arm/omap/files.omap
===================================================================
--- netbsd_quilt.orig/src/sys/arch/arm/omap/files.omap
+++ netbsd_quilt/src/sys/arch/arm/omap/files.omap
@@ -124,3 +124,8 @@ defparam opt_omap.h				OMAP_MPU_TIMER_CL
 device omapgpio
 attach omapgpio at tipb
 file  arch/arm/omap/omap_gpio.c       omapgpio	needs-count
+
+# RTC
+device omaprtc
+attach omaprtc at tipb
+file arch/arm/omap/omap_rtc.c			omaprtc
Index: netbsd_quilt/src/sys/arch/arm/omap/omap_rtc.c
===================================================================
--- /dev/null
+++ netbsd_quilt/src/sys/arch/arm/omap/omap_rtc.c
@@ -0,0 +1,248 @@
+/*	$NetBSD: $	*/
+
+/*
+ * OMAP RTC driver, based on i80321_timer.c.
+ *
+ * Copyright (c) 2001, 2002 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Jason R. Thorpe for Wasabi Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed for the NetBSD Project by
+ *	Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ *    or promote products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: $");
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/time.h>
+#include <sys/device.h>
+#include <dev/clock_subr.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <arm/omap/omap_reg.h>
+#include <arm/omap/omap_tipb.h>
+
+/* RTC year values relative to this year */
+
+#define	BASEYEAR		2000
+
+/* Register offsets and bit fields. */
+
+#define SECONDS_REG		0x00
+#define MINUTES_REG		0x04
+#define HOURS_REG		0x08
+#define DAYS_REG		0x0c
+#define MONTHS_REG		0x10
+#define YEARS_REG		0x14
+#define WEEKS_REG		0x18
+#define ALARM_SECONDS_REG	0x20
+#define ALARM_MINUTES_REG	0x24
+#define ALARM_HOURS_REG		0x28
+#define ALARM_DAYS_REG		0x2c
+#define ALARM_MONTHS_REG	0x30
+#define ALARM_YEARS_REG		0x34
+
+#define RTC_CTRL_REG		0x40
+#define STOP_RTC		0
+
+#define RTC_STATUS_REG		0x44
+#define ALARM			6
+#define BUSY			0
+
+#define RTC_INTERRUPTS_REG	0x48
+#define IT_ALARM		3
+
+static int	omaprtc_match(struct device *, struct cfdata *, void *);
+static void	omaprtc_attach(struct device *, struct device *, void *);
+
+struct omaprtc_softc {
+	struct device		sc_dev;
+	bus_space_tag_t		sc_iot;
+	bus_space_handle_t	sc_ioh;
+	int			sc_intr;
+	void			*sc_irqcookie;
+	struct todr_chip_handle	sc_todr;
+};
+
+CFATTACH_DECL(omaprtc, sizeof(struct omaprtc_softc),
+    omaprtc_match, omaprtc_attach, NULL, NULL);
+
+static int omaprtc_gettime(todr_chip_handle_t, struct clock_ymdhms *);
+static int omaprtc_settime(todr_chip_handle_t, struct clock_ymdhms *);
+
+#define rtc_is_busy()	(bus_space_read_1(sc->sc_iot, sc->sc_ioh, \
+					  RTC_STATUS_REG) & 1<<BUSY)
+
+static int
+omaprtc_gettime(todr_chip_handle_t tch, struct clock_ymdhms *dt)
+{
+	struct omaprtc_softc *sc = tch->cookie;
+	int s;
+
+	/* Wait for RTC_STATUS_REG:BUSY to go low to
+	 * guarantee our read is correct.  BUSY will
+	 * only be high for one 32kHz period (30.5us)
+	 * each second, so we'll usually pass right
+	 * through.
+	 *
+	 * Watch RTC_CTRL_REG:STOP_RTC as well so
+	 * we don't spin forever if someone disables the RTC.
+	 *
+	 * Turn interrupts off, because we are only allowed
+	 * to read/write the registers for 1/2 of a 32kHz
+	 * clock period (= 15us) after detecting that BUSY
+	 * is low.
+	 */
+
+	s = disable_interrupts(I32_bit);
+
+	while (rtc_is_busy()) {
+		;
+	}
+
+	dt->dt_year =
+		FROMBCD(bus_space_read_1(sc->sc_iot,
+					 sc->sc_ioh,
+					 YEARS_REG)) + BASEYEAR;
+	dt->dt_mon =
+		FROMBCD(bus_space_read_1(sc->sc_iot,
+					 sc->sc_ioh,
+					 MONTHS_REG));
+	dt->dt_wday =
+		FROMBCD(bus_space_read_1(sc->sc_iot,
+					 sc->sc_ioh,
+					 WEEKS_REG) & 0x0f);
+	dt->dt_day =
+		FROMBCD(bus_space_read_1(sc->sc_iot,
+					 sc->sc_ioh,
+					 DAYS_REG));
+	dt->dt_sec =
+		FROMBCD(bus_space_read_1(sc->sc_iot,
+					 sc->sc_ioh,
+					 SECONDS_REG));
+	dt->dt_hour =
+		FROMBCD(bus_space_read_1(sc->sc_iot,
+					 sc->sc_ioh,
+					 HOURS_REG));
+	dt->dt_min =
+		FROMBCD(bus_space_read_1(sc->sc_iot,
+					 sc->sc_ioh,
+					 MINUTES_REG));
+	restore_interrupts(s);
+        return 0;
+}
+
+static int
+omaprtc_settime(todr_chip_handle_t tch, struct clock_ymdhms *dt)
+{
+	struct omaprtc_softc *sc = tch->cookie;
+	int s;
+
+	s = disable_interrupts(I32_bit);
+
+	while (rtc_is_busy()) {
+		;
+	}
+
+	/* It's ok to write these without stopping the
+	 * RTC, because the BUSY mechanism lets us guarantee
+	 * that we're not in the middle of, e.g., rolling
+	 * seconds into minutes.
+	 */
+
+	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
+			  YEARS_REG, TOBCD(dt->dt_year - BASEYEAR));
+	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
+			  MONTHS_REG, TOBCD(dt->dt_mon));
+	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
+			  WEEKS_REG, TOBCD(dt->dt_wday & 0x0f));
+	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
+			  DAYS_REG, TOBCD(dt->dt_day));
+	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
+			  SECONDS_REG, TOBCD(dt->dt_sec));
+	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
+			  HOURS_REG, TOBCD(dt->dt_hour));
+	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
+			  MINUTES_REG, TOBCD(dt->dt_min));
+	restore_interrupts(s);
+        return 0;
+}
+
+static int
+omaprtc_match(struct device *parent, struct cfdata *match, void *aux)
+{
+	struct tipb_attach_args *tipb = aux;
+
+	if (tipb->tipb_addr == -1)
+	    panic("omaprtc must have addr specified in config.");
+
+	if (tipb->tipb_size == 0)
+		tipb->tipb_size = 2048;	/* Per the OMAP TRM. */
+
+	/* We implicitly trust the config file. */
+	return (1);
+}
+
+void
+omaprtc_attach(struct device *parent, struct device *self, void *aux)
+{
+	struct omaprtc_softc *sc = (struct omaprtc_softc*)self;
+	struct tipb_attach_args *tipb = aux;
+
+	sc->sc_iot = tipb->tipb_iot;
+	sc->sc_intr = tipb->tipb_intr;
+
+	if (bus_space_map(tipb->tipb_iot, tipb->tipb_addr, tipb->tipb_size, 0,
+			 &sc->sc_ioh))
+		panic("%s: Cannot map registers", self->dv_xname);
+
+	aprint_normal(": OMAP RTC\n");
+	aprint_naive("\n");
+
+	/*
+	 * Start RTC (STOP_RTC=1 starts it, go figure), 24 hour mode,
+	 * no autocompensation, no rounding, 32KHz clock enabled,
+	 * cannot use split power, no test mode.
+	 */
+
+	bus_space_write_1(sc->sc_iot, sc->sc_ioh, RTC_CTRL_REG,
+			  1 << STOP_RTC);
+
+	sc->sc_todr.cookie = sc;
+	sc->sc_todr.todr_gettime_ymdhms = omaprtc_gettime;
+	sc->sc_todr.todr_settime_ymdhms = omaprtc_settime;
+	todr_attach(&sc->sc_todr);
+}
+

--=-=-=--

>Unformatted:
 --=-=-=