1210311Sjmallett/***********************license start***************
2210311Sjmallett *  Copyright (c) 2003-2008 Cavium Networks (support@cavium.com). All rights
3210311Sjmallett *  reserved.
4210311Sjmallett *
5210311Sjmallett *
6210311Sjmallett *  Redistribution and use in source and binary forms, with or without
7210311Sjmallett *  modification, are permitted provided that the following conditions are
8210311Sjmallett *  met:
9210311Sjmallett *
10210311Sjmallett *      * Redistributions of source code must retain the above copyright
11210311Sjmallett *        notice, this list of conditions and the following disclaimer.
12210311Sjmallett *
13210311Sjmallett *      * Redistributions in binary form must reproduce the above
14210311Sjmallett *        copyright notice, this list of conditions and the following
15210311Sjmallett *        disclaimer in the documentation and/or other materials provided
16210311Sjmallett *        with the distribution.
17210311Sjmallett *
18210311Sjmallett *      * Neither the name of Cavium Networks nor the names of
19210311Sjmallett *        its contributors may be used to endorse or promote products
20210311Sjmallett *        derived from this software without specific prior written
21210311Sjmallett *        permission.
22210311Sjmallett *
23210311Sjmallett *  TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
24210311Sjmallett *  AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS
25210311Sjmallett *  OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH
26210311Sjmallett *  RESPECT TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY
27210311Sjmallett *  REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT
28210311Sjmallett *  DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES
29210311Sjmallett *  OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR
30210311Sjmallett *  PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET
31210311Sjmallett *  POSSESSION OR CORRESPONDENCE TO DESCRIPTION.  THE ENTIRE RISK ARISING OUT
32210311Sjmallett *  OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
33210311Sjmallett *
34210311Sjmallett *
35210311Sjmallett *  For any questions regarding licensing please contact marketing@caviumnetworks.com
36210311Sjmallett *
37210311Sjmallett ***********************license end**************************************/
38210311Sjmallett
39210311Sjmallett
40210311Sjmallett
41210311Sjmallett
42210311Sjmallett
43210311Sjmallett
44210311Sjmallett/**
45210311Sjmallett * @file
46210311Sjmallett *
47210311Sjmallett * Interface to the EBH-30xx specific devices
48210311Sjmallett *
49210311Sjmallett * <hr>$Revision: 41586 $<hr>
50210311Sjmallett *
51210311Sjmallett */
52210311Sjmallett
53210311Sjmallett#include <sys/cdefs.h>
54210311Sjmallett__FBSDID("$FreeBSD$");
55210311Sjmallett
56210311Sjmallett#include <sys/param.h>
57210311Sjmallett#include <sys/timespec.h>
58210311Sjmallett#include <sys/clock.h>
59210311Sjmallett#include <sys/libkern.h>
60210311Sjmallett
61210311Sjmallett#include <contrib/octeon-sdk/cvmx.h>
62210311Sjmallett#include <contrib/octeon-sdk/cvmx-cn3010-evb-hs5.h>
63210311Sjmallett#include <contrib/octeon-sdk/cvmx-twsi.h>
64210311Sjmallett
65210311Sjmallett#define CT_CHECK(_expr, _msg) \
66210311Sjmallett        do { \
67210311Sjmallett            if (_expr) { \
68210311Sjmallett                cvmx_dprintf("Warning: RTC has invalid %s field\n", (_msg)); \
69210311Sjmallett                rc = -1; \
70210311Sjmallett            } \
71210311Sjmallett        } while(0);
72210311Sjmallett
73210311Sjmallettstatic int validate_ct_struct(struct clocktime *ct)
74210311Sjmallett{
75210311Sjmallett    int rc = 0;
76210311Sjmallett
77210311Sjmallett    if (!ct)
78210311Sjmallett	return -1;
79210311Sjmallett
80210311Sjmallett    CT_CHECK(ct->sec < 0  || ct->sec > 60,  "second"); /* + Leap sec */
81210311Sjmallett    CT_CHECK(ct->min < 0  || ct->min > 59,  "minute");
82210311Sjmallett    CT_CHECK(ct->hour < 0 || ct->hour > 23, "hour");
83210311Sjmallett    CT_CHECK(ct->day < 1 || ct->day > 31, "day");
84271562Skan    CT_CHECK(ct->dow < 0 || ct->dow > 6,  "day of week");
85229161Sgonzo    CT_CHECK(ct->mon < 1  || ct->mon > 12,  "month");
86229161Sgonzo    CT_CHECK(ct->year > 2037,"year");
87210311Sjmallett
88210311Sjmallett    return rc;
89210311Sjmallett}
90210311Sjmallett
91210311Sjmallett/*
92210311Sjmallett * Board-specifc RTC read
93210311Sjmallett * Time is expressed in seconds from epoch (Jan 1 1970 at 00:00:00 UTC)
94210311Sjmallett * and converted internally to calendar format.
95210311Sjmallett */
96210311Sjmallettuint32_t cvmx_rtc_ds1337_read(void)
97210311Sjmallett{
98210311Sjmallett    int       i, retry;
99210311Sjmallett    uint8_t   reg[8];
100210311Sjmallett    uint8_t   sec;
101210311Sjmallett    struct clocktime ct;
102210311Sjmallett    struct timespec ts;
103210311Sjmallett
104210311Sjmallett
105210311Sjmallett    memset(&reg, 0, sizeof(reg));
106210311Sjmallett    memset(&ct, 0, sizeof(ct));
107210311Sjmallett
108210311Sjmallett    for(retry=0; retry<2; retry++)
109210311Sjmallett    {
110210311Sjmallett	/* Lockless read: detects the infrequent roll-over and retries */
111210311Sjmallett	reg[0] = cvmx_twsi_read8(CVMX_RTC_DS1337_ADDR, 0x0);
112210311Sjmallett	for(i=1; i<7; i++)
113210311Sjmallett	    reg[i] = cvmx_twsi_read8_cur_addr(CVMX_RTC_DS1337_ADDR);
114210311Sjmallett
115210311Sjmallett	sec = cvmx_twsi_read8(CVMX_RTC_DS1337_ADDR, 0x0);
116210311Sjmallett	if ((sec & 0xf) == (reg[0] & 0xf))
117210311Sjmallett	    break; /* Time did not roll-over, value is correct */
118210311Sjmallett    }
119210311Sjmallett
120210311Sjmallett    ct.sec  = bcd2bin(reg[0] & 0x7f);
121210311Sjmallett    ct.min  = bcd2bin(reg[1] & 0x7f);
122210311Sjmallett    ct.hour = bcd2bin(reg[2] & 0x3f);
123210311Sjmallett    if ((reg[2] & 0x40) && (reg[2] & 0x20))   /* AM/PM format and is PM time */
124210311Sjmallett    {
125210311Sjmallett	ct.hour = (ct.hour + 12) % 24;
126210311Sjmallett    }
127271562Skan    ct.dow = (reg[3] & 0x7) - 1; /* Day of week field is 0..6 */
128210311Sjmallett    ct.day = bcd2bin(reg[4] & 0x3f);
129229161Sgonzo    ct.mon  = bcd2bin(reg[5] & 0x1f); /* Month field is 1..12 */
130229161Sgonzo#if defined(OCTEON_BOARD_CAPK_0100ND)
131229161Sgonzo    /*
132229161Sgonzo     * CAPK-0100ND uses DS1307 that does not have century bit
133229161Sgonzo     */
134229161Sgonzo    ct.year = 2000 + bcd2bin(reg[6]);
135229161Sgonzo#else
136229161Sgonzo    ct.year = ((reg[5] & 0x80) ? 2000 : 1900) + bcd2bin(reg[6]);
137229161Sgonzo#endif
138210311Sjmallett
139210311Sjmallett    if (validate_ct_struct(&ct))
140210311Sjmallett	cvmx_dprintf("Warning: RTC calendar is not configured properly\n");
141210311Sjmallett
142210311Sjmallett    if (clock_ct_to_ts(&ct, &ts) != 0) {
143210311Sjmallett	cvmx_dprintf("Warning: RTC calendar is not configured properly\n");
144210311Sjmallett        return 0;
145210311Sjmallett    }
146210311Sjmallett
147210311Sjmallett    return ts.tv_sec;
148210311Sjmallett}
149210311Sjmallett
150210311Sjmallett/*
151210311Sjmallett * Board-specific RTC write
152210311Sjmallett * Time returned is in seconds from epoch (Jan 1 1970 at 00:00:00 UTC)
153210311Sjmallett */
154210311Sjmallettint cvmx_rtc_ds1337_write(uint32_t time)
155210311Sjmallett{
156210311Sjmallett    struct clocktime ct;
157210311Sjmallett    struct timespec ts;
158210311Sjmallett    int       i, rc, retry;
159210311Sjmallett    uint8_t   reg[8];
160210311Sjmallett    uint8_t   sec;
161210311Sjmallett
162210311Sjmallett    ts.tv_sec = time;
163210311Sjmallett    ts.tv_nsec = 0;
164210311Sjmallett
165210311Sjmallett    clock_ts_to_ct(&ts, &ct);
166210311Sjmallett
167210311Sjmallett    if (validate_ct_struct(&ct))
168210311Sjmallett    {
169210311Sjmallett	cvmx_dprintf("Error: RTC was passed wrong calendar values, write failed\n");
170210311Sjmallett	goto ct_invalid;
171210311Sjmallett    }
172210311Sjmallett
173210311Sjmallett    reg[0] = bin2bcd(ct.sec);
174210311Sjmallett    reg[1] = bin2bcd(ct.min);
175229161Sgonzo    reg[2] = bin2bcd(ct.hour);       /* Force 0..23 format even if using AM/PM */
176271562Skan    reg[3] = bin2bcd(ct.dow + 1);
177210311Sjmallett    reg[4] = bin2bcd(ct.day);
178229161Sgonzo    reg[5] = bin2bcd(ct.mon);
179271562Skan#if !defined(OCTEON_BOARD_CAPK_0100ND)
180229161Sgonzo    if (ct.year >= 2000)             /* Set century bit*/
181210311Sjmallett    {
182210311Sjmallett	reg[5] |= 0x80;
183210311Sjmallett    }
184271562Skan#endif
185210311Sjmallett    reg[6] = bin2bcd(ct.year % 100);
186210311Sjmallett
187210311Sjmallett    /* Lockless write: detects the infrequent roll-over and retries */
188210311Sjmallett    for(retry=0; retry<2; retry++)
189210311Sjmallett    {
190210311Sjmallett	rc = 0;
191210311Sjmallett	for(i=0; i<7; i++)
192210311Sjmallett	{
193210311Sjmallett	    rc |= cvmx_twsi_write8(CVMX_RTC_DS1337_ADDR, i, reg[i]);
194210311Sjmallett	}
195210311Sjmallett
196210311Sjmallett	sec = cvmx_twsi_read8(CVMX_RTC_DS1337_ADDR, 0x0);
197210311Sjmallett	if ((sec & 0xf) == (reg[0] & 0xf))
198210311Sjmallett	    break; /* Time did not roll-over, value is correct */
199210311Sjmallett    }
200210311Sjmallett
201210311Sjmallett    return (rc ? -1 : 0);
202210311Sjmallett
203210311Sjmallett ct_invalid:
204210311Sjmallett    return -1;
205210311Sjmallett}
206210311Sjmallett
207210311Sjmallett#ifdef CVMX_RTC_DEBUG
208210311Sjmallett
209210311Sjmallettvoid cvmx_rtc_ds1337_dump_state(void)
210210311Sjmallett{
211210311Sjmallett    int i = 0;
212210311Sjmallett
213210311Sjmallett    printf("RTC:\n");
214210311Sjmallett    printf("%d : %02X ", i, cvmx_twsi_read8(CVMX_RTC_DS1337_ADDR, 0x0));
215210311Sjmallett    for(i=1; i<16; i++) {
216210311Sjmallett	printf("%02X ", cvmx_twsi_read8_cur_addr(CVMX_RTC_DS1337_ADDR));
217210311Sjmallett    }
218210311Sjmallett    printf("\n");
219210311Sjmallett}
220210311Sjmallett
221210311Sjmallett#endif /* CVMX_RTC_DEBUG */
222