1202839Sgonzo/*- 2202839Sgonzo * Copyright (c) 2010, Oleksandr Tymoshenko <gonzo@FreeBSD.org> 3202839Sgonzo * All rights reserved. 4202839Sgonzo * 5202839Sgonzo * Redistribution and use in source and binary forms, with or without 6202839Sgonzo * modification, are permitted provided that the following conditions 7202839Sgonzo * are met: 8202839Sgonzo * 1. Redistributions of source code must retain the above copyright 9202839Sgonzo * notice unmodified, this list of conditions, and the following 10202839Sgonzo * disclaimer. 11202839Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 12202839Sgonzo * notice, this list of conditions and the following disclaimer in the 13202839Sgonzo * documentation and/or other materials provided with the distribution. 14202839Sgonzo * 15202839Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16202839Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17202839Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18202839Sgonzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19202839Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20202839Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21202839Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22202839Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23202839Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24202839Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25202839Sgonzo * SUCH DAMAGE. 26202839Sgonzo */ 27202839Sgonzo 28202839Sgonzo#include <sys/cdefs.h> 29202839Sgonzo__FBSDID("$FreeBSD$"); 30202839Sgonzo 31202839Sgonzo#include <sys/param.h> 32202839Sgonzo#include <sys/bus.h> 33202839Sgonzo#include <sys/lock.h> 34202839Sgonzo#include <sys/time.h> 35202839Sgonzo#include <sys/clock.h> 36202839Sgonzo#include <sys/resource.h> 37202839Sgonzo#include <sys/systm.h> 38202839Sgonzo#include <sys/rman.h> 39202839Sgonzo#include <sys/kernel.h> 40202839Sgonzo#include <sys/module.h> 41202839Sgonzo 42202839Sgonzo#include <mips/atheros/pcf2123reg.h> 43202839Sgonzo 44202839Sgonzo#include <dev/spibus/spi.h> 45202839Sgonzo#include "spibus_if.h" 46202839Sgonzo 47202839Sgonzo#include "clock_if.h" 48202839Sgonzo 49202839Sgonzo#define YEAR_BASE 1970 50202839Sgonzo#define PCF2123_DELAY 50 51202839Sgonzo 52202839Sgonzostruct pcf2123_rtc_softc { 53202839Sgonzo device_t dev; 54202839Sgonzo}; 55202839Sgonzo 56202839Sgonzostatic int pcf2123_rtc_probe(device_t dev); 57202839Sgonzostatic int pcf2123_rtc_attach(device_t dev); 58202839Sgonzo 59202839Sgonzostatic int pcf2123_rtc_gettime(device_t dev, struct timespec *ts); 60202839Sgonzostatic int pcf2123_rtc_settime(device_t dev, struct timespec *ts); 61202839Sgonzo 62202839Sgonzostatic int 63202839Sgonzopcf2123_rtc_probe(device_t dev) 64202839Sgonzo{ 65202839Sgonzo 66202839Sgonzo device_set_desc(dev, "PCF2123 SPI RTC"); 67202839Sgonzo return (0); 68202839Sgonzo} 69202839Sgonzo 70202839Sgonzostatic int 71202839Sgonzopcf2123_rtc_attach(device_t dev) 72202839Sgonzo{ 73202839Sgonzo struct pcf2123_rtc_softc *sc; 74202839Sgonzo struct spi_command cmd; 75202839Sgonzo unsigned char rxBuf[3]; 76202839Sgonzo unsigned char txBuf[3]; 77202839Sgonzo int err; 78202839Sgonzo 79202839Sgonzo sc = device_get_softc(dev); 80202839Sgonzo sc->dev = dev; 81202839Sgonzo 82202839Sgonzo clock_register(dev, 1000000); 83202839Sgonzo 84202839Sgonzo memset(&cmd, 0, sizeof(cmd)); 85202839Sgonzo memset(rxBuf, 0, sizeof(rxBuf)); 86202839Sgonzo memset(txBuf, 0, sizeof(txBuf)); 87202839Sgonzo 88202839Sgonzo /* Make sure Ctrl1 and Ctrl2 are zeroes */ 89202839Sgonzo txBuf[0] = PCF2123_WRITE(PCF2123_REG_CTRL1); 90202839Sgonzo cmd.rx_cmd = rxBuf; 91202839Sgonzo cmd.tx_cmd = txBuf; 92202839Sgonzo cmd.rx_cmd_sz = sizeof(rxBuf); 93202839Sgonzo cmd.tx_cmd_sz = sizeof(txBuf); 94202839Sgonzo err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd); 95202839Sgonzo DELAY(PCF2123_DELAY); 96202839Sgonzo 97202839Sgonzo return (0); 98202839Sgonzo} 99202839Sgonzo 100202839Sgonzostatic int 101202839Sgonzopcf2123_rtc_gettime(device_t dev, struct timespec *ts) 102202839Sgonzo{ 103202839Sgonzo struct clocktime ct; 104202839Sgonzo struct spi_command cmd; 105202839Sgonzo unsigned char rxTimedate[8]; 106202839Sgonzo unsigned char txTimedate[8]; 107202839Sgonzo int err; 108202839Sgonzo 109202839Sgonzo memset(&cmd, 0, sizeof(cmd)); 110202839Sgonzo memset(rxTimedate, 0, sizeof(rxTimedate)); 111202839Sgonzo memset(txTimedate, 0, sizeof(txTimedate)); 112202839Sgonzo 113202839Sgonzo /* 114202839Sgonzo * Counter is stopped when access to time registers is in progress 115202839Sgonzo * So there is no need to stop/start counter 116202839Sgonzo */ 117202839Sgonzo /* Start reading from seconds */ 118202839Sgonzo txTimedate[0] = PCF2123_READ(PCF2123_REG_SECONDS); 119202839Sgonzo cmd.rx_cmd = rxTimedate; 120202839Sgonzo cmd.tx_cmd = txTimedate; 121202839Sgonzo cmd.rx_cmd_sz = sizeof(rxTimedate); 122202839Sgonzo cmd.tx_cmd_sz = sizeof(txTimedate); 123202839Sgonzo err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd); 124202839Sgonzo DELAY(PCF2123_DELAY); 125202839Sgonzo 126202839Sgonzo ct.nsec = 0; 127202839Sgonzo ct.sec = FROMBCD(rxTimedate[1] & 0x7f); 128202839Sgonzo ct.min = FROMBCD(rxTimedate[2] & 0x7f); 129202839Sgonzo ct.hour = FROMBCD(rxTimedate[3] & 0x3f); 130202839Sgonzo 131202839Sgonzo ct.dow = FROMBCD(rxTimedate[5] & 0x3f); 132202839Sgonzo 133202839Sgonzo ct.day = FROMBCD(rxTimedate[4] & 0x3f); 134202839Sgonzo ct.mon = FROMBCD(rxTimedate[6] & 0x1f); 135202839Sgonzo ct.year = YEAR_BASE + FROMBCD(rxTimedate[7]); 136202839Sgonzo 137202839Sgonzo return (clock_ct_to_ts(&ct, ts)); 138202839Sgonzo} 139202839Sgonzo 140202839Sgonzostatic int 141202839Sgonzopcf2123_rtc_settime(device_t dev, struct timespec *ts) 142202839Sgonzo{ 143202839Sgonzo struct clocktime ct; 144202839Sgonzo struct pcf2123_rtc_softc *sc; 145202839Sgonzo struct spi_command cmd; 146202839Sgonzo unsigned char rxTimedate[8]; 147202839Sgonzo unsigned char txTimedate[8]; 148202839Sgonzo int err; 149202839Sgonzo 150202839Sgonzo sc = device_get_softc(dev); 151202839Sgonzo 152202839Sgonzo /* Resolution: 1 sec */ 153202839Sgonzo if (ts->tv_nsec >= 500000000) 154202839Sgonzo ts->tv_sec++; 155202839Sgonzo ts->tv_nsec = 0; 156202839Sgonzo clock_ts_to_ct(ts, &ct); 157202839Sgonzo 158202839Sgonzo memset(&cmd, 0, sizeof(cmd)); 159202839Sgonzo memset(rxTimedate, 0, sizeof(rxTimedate)); 160202839Sgonzo memset(txTimedate, 0, sizeof(txTimedate)); 161202839Sgonzo 162202839Sgonzo /* Start reading from seconds */ 163202839Sgonzo cmd.rx_cmd = rxTimedate; 164202839Sgonzo cmd.tx_cmd = txTimedate; 165202839Sgonzo cmd.rx_cmd_sz = sizeof(rxTimedate); 166202839Sgonzo cmd.tx_cmd_sz = sizeof(txTimedate); 167202839Sgonzo 168202839Sgonzo /* 169202839Sgonzo * Counter is stopped when access to time registers is in progress 170202839Sgonzo * So there is no need to stop/start counter 171202839Sgonzo */ 172202839Sgonzo txTimedate[0] = PCF2123_WRITE(PCF2123_REG_SECONDS); 173202839Sgonzo txTimedate[1] = TOBCD(ct.sec); 174202839Sgonzo txTimedate[2] = TOBCD(ct.min); 175202839Sgonzo txTimedate[3] = TOBCD(ct.hour); 176202839Sgonzo txTimedate[4] = TOBCD(ct.day); 177202839Sgonzo txTimedate[5] = TOBCD(ct.dow); 178202839Sgonzo txTimedate[6] = TOBCD(ct.mon); 179202839Sgonzo txTimedate[7] = TOBCD(ct.year - YEAR_BASE); 180202839Sgonzo 181202839Sgonzo err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd); 182202839Sgonzo DELAY(PCF2123_DELAY); 183202839Sgonzo 184202839Sgonzo return (err); 185202839Sgonzo} 186202839Sgonzo 187202839Sgonzostatic device_method_t pcf2123_rtc_methods[] = { 188202839Sgonzo DEVMETHOD(device_probe, pcf2123_rtc_probe), 189202839Sgonzo DEVMETHOD(device_attach, pcf2123_rtc_attach), 190202839Sgonzo 191202839Sgonzo DEVMETHOD(clock_gettime, pcf2123_rtc_gettime), 192202839Sgonzo DEVMETHOD(clock_settime, pcf2123_rtc_settime), 193202839Sgonzo 194202839Sgonzo { 0, 0 }, 195202839Sgonzo}; 196202839Sgonzo 197202839Sgonzostatic driver_t pcf2123_rtc_driver = { 198202839Sgonzo "rtc", 199202839Sgonzo pcf2123_rtc_methods, 200202839Sgonzo sizeof(struct pcf2123_rtc_softc), 201202839Sgonzo}; 202202839Sgonzostatic devclass_t pcf2123_rtc_devclass; 203202839Sgonzo 204202839SgonzoDRIVER_MODULE(pcf2123_rtc, spibus, pcf2123_rtc_driver, pcf2123_rtc_devclass, 0, 0); 205