1 /* $NetBSD: pcf8563.c,v 1.2 2011/01/21 22:42:16 jakllsch Exp $ */ 2 3 /* 4 * Copyright (c) 2011 Jonathan A. Kollasch 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 25 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 26 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: pcf8563.c,v 1.2 2011/01/21 22:42:16 jakllsch Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/device.h> 35 #include <sys/kernel.h> 36 37 #include <dev/clock_subr.h> 38 39 #include <dev/i2c/i2cvar.h> 40 #include <dev/i2c/pcf8563reg.h> 41 42 struct pcf8563rtc_softc { 43 device_t sc_dev; 44 i2c_tag_t sc_tag; 45 int sc_addr; 46 struct todr_chip_handle sc_todr; 47 }; 48 49 static int pcf8563rtc_match(device_t, cfdata_t, void *); 50 static void pcf8563rtc_attach(device_t, device_t, void *); 51 52 CFATTACH_DECL_NEW(pcf8563rtc, sizeof(struct pcf8563rtc_softc), 53 pcf8563rtc_match, pcf8563rtc_attach, NULL, NULL); 54 55 static int pcf8563rtc_clock_read(struct pcf8563rtc_softc *, struct clock_ymdhms *); 56 static int pcf8563rtc_clock_write(struct pcf8563rtc_softc *, struct clock_ymdhms *); 57 static int pcf8563rtc_gettime(struct todr_chip_handle *, struct clock_ymdhms *); 58 static int pcf8563rtc_settime(struct todr_chip_handle *, struct clock_ymdhms *); 59 60 static int 61 pcf8563rtc_match(device_t parent, cfdata_t cf, void *aux) 62 { 63 struct i2c_attach_args *ia = aux; 64 65 if (ia->ia_addr == PCF8563_ADDR) 66 return 1; 67 68 return 0; 69 } 70 71 static void 72 pcf8563rtc_attach(device_t parent, device_t self, void *aux) 73 { 74 struct pcf8563rtc_softc *sc = device_private(self); 75 struct i2c_attach_args *ia = aux; 76 77 aprint_naive(": Real-time Clock\n"); 78 aprint_normal(": NXP PCF8563 Real-time Clock\n"); 79 80 sc->sc_dev = self; 81 sc->sc_tag = ia->ia_tag; 82 sc->sc_addr = ia->ia_addr; 83 sc->sc_todr.cookie = sc; 84 sc->sc_todr.todr_gettime_ymdhms = pcf8563rtc_gettime; 85 sc->sc_todr.todr_settime_ymdhms = pcf8563rtc_settime; 86 sc->sc_todr.todr_setwen = NULL; 87 88 todr_attach(&sc->sc_todr); 89 } 90 91 static int 92 pcf8563rtc_gettime(struct todr_chip_handle *ch, struct clock_ymdhms *dt) 93 { 94 struct pcf8563rtc_softc *sc = ch->cookie; 95 96 if (pcf8563rtc_clock_read(sc, dt) == 0) 97 return -1; 98 99 return 0; 100 } 101 102 static int 103 pcf8563rtc_settime(struct todr_chip_handle *ch, struct clock_ymdhms *dt) 104 { 105 struct pcf8563rtc_softc *sc = ch->cookie; 106 107 if (pcf8563rtc_clock_write(sc, dt) == 0) 108 return -1; 109 110 return 0; 111 } 112 113 static int 114 pcf8563rtc_clock_read(struct pcf8563rtc_softc *sc, struct clock_ymdhms *dt) 115 { 116 uint8_t bcd[PCF8563_NREGS]; 117 uint8_t reg = PCF8563_R_CS1; 118 119 if (iic_acquire_bus(sc->sc_tag, I2C_F_POLL)) { 120 device_printf(sc->sc_dev, "acquire bus for read failed\n"); 121 return 0; 122 } 123 124 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, ®, 1, 125 &bcd[reg], PCF8563_R_YEAR - reg + 1, I2C_F_POLL)) { 126 iic_release_bus(sc->sc_tag, I2C_F_POLL); 127 device_printf(sc->sc_dev, "read failed\n"); 128 return 0; 129 } 130 131 iic_release_bus(sc->sc_tag, I2C_F_POLL); 132 133 dt->dt_sec = FROMBCD(bcd[PCF8563_R_SECOND] & PCF8563_M_SECOND); 134 dt->dt_min = FROMBCD(bcd[PCF8563_R_MINUTE] & PCF8563_M_MINUTE); 135 dt->dt_hour = FROMBCD(bcd[PCF8563_R_HOUR] & PCF8563_M_HOUR); 136 dt->dt_day = FROMBCD(bcd[PCF8563_R_DAY] & PCF8563_M_DAY); 137 dt->dt_mon = FROMBCD(bcd[PCF8563_R_MONTH] & PCF8563_M_MONTH); 138 dt->dt_year = FROMBCD(bcd[PCF8563_R_YEAR] & PCF8563_M_YEAR); 139 dt->dt_year += 2000; 140 141 return 1; 142 } 143 144 static int 145 pcf8563rtc_clock_write(struct pcf8563rtc_softc *sc, struct clock_ymdhms *dt) 146 { 147 uint8_t bcd[PCF8563_NREGS]; 148 uint8_t reg = PCF8563_R_SECOND; 149 150 bcd[PCF8563_R_SECOND] = TOBCD(dt->dt_sec); 151 bcd[PCF8563_R_MINUTE] = TOBCD(dt->dt_min); 152 bcd[PCF8563_R_HOUR] = TOBCD(dt->dt_hour); 153 bcd[PCF8563_R_DAY] = TOBCD(dt->dt_day); 154 bcd[PCF8563_R_WEEKDAY] = TOBCD(dt->dt_wday); 155 bcd[PCF8563_R_MONTH] = TOBCD(dt->dt_mon); 156 bcd[PCF8563_R_YEAR] = TOBCD(dt->dt_year % 100); 157 158 if (iic_acquire_bus(sc->sc_tag, I2C_F_POLL)) { 159 device_printf(sc->sc_dev, "acquire bus for write failed\n"); 160 return 0; 161 } 162 163 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, ®, 1, 164 &bcd[reg], PCF8563_R_YEAR - reg + 1, I2C_F_POLL)) { 165 iic_release_bus(sc->sc_tag, I2C_F_POLL); 166 device_printf(sc->sc_dev, "write failed\n"); 167 return 0; 168 } 169 170 iic_release_bus(sc->sc_tag, I2C_F_POLL); 171 172 return 1; 173 } 174