]>
Commit | Line | Data |
---|---|---|
affae2bf WD |
1 | /* |
2 | * (C) Copyright 2002 SIXNET, dge@sixnetio.com. | |
3 | * | |
4 | * See file CREDITS for list of people who contributed to this | |
5 | * project. | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU General Public License as | |
9 | * published by the Free Software Foundation; either version 2 of | |
10 | * the License, or (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program; if not, write to the Free Software | |
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
20 | * MA 02111-1307 USA | |
21 | */ | |
22 | ||
23 | /* | |
24 | * Date & Time support for DS1306 RTC using software SPI | |
25 | */ | |
26 | ||
27 | #include <common.h> | |
28 | #include <command.h> | |
29 | #include <rtc.h> | |
30 | ||
31 | #if defined(CONFIG_RTC_DS1306) && (CONFIG_COMMANDS & CFG_CMD_DATE) | |
32 | ||
33 | static unsigned int bin2bcd(unsigned int n); | |
34 | static unsigned char bcd2bin(unsigned char c); | |
35 | static void soft_spi_send(unsigned char n); | |
36 | static unsigned char soft_spi_read(void); | |
37 | static void init_spi(void); | |
38 | ||
39 | /*----------------------------------------------------------------------- | |
40 | * Definitions | |
41 | */ | |
42 | ||
43 | #define PB_SPISCK 0x00000002 /* PB 30 */ | |
44 | #define PB_SPIMOSI 0x00000004 /* PB 29 */ | |
45 | #define PB_SPIMISO 0x00000008 /* PB 28 */ | |
46 | #define PB_SPI_CE 0x00010000 /* PB 15 */ | |
47 | ||
48 | /* ------------------------------------------------------------------------- */ | |
49 | ||
50 | /* read clock time from DS1306 and return it in *tmp */ | |
51 | void rtc_get(struct rtc_time *tmp) | |
52 | { | |
53 | volatile immap_t *immap = (immap_t *)CFG_IMMR; | |
54 | unsigned char spi_byte; /* Data Byte */ | |
55 | ||
56 | init_spi(); /* set port B for software SPI */ | |
57 | ||
58 | /* Now we can enable the DS1306 RTC */ | |
59 | immap->im_cpm.cp_pbdat |= PB_SPI_CE; | |
60 | udelay(10); | |
61 | ||
62 | /* Shift out the address (0) of the time in the Clock Chip */ | |
63 | soft_spi_send(0); | |
64 | ||
65 | /* Put the clock readings into the rtc_time structure */ | |
66 | tmp->tm_sec = bcd2bin(soft_spi_read()); /* Read seconds */ | |
67 | tmp->tm_min = bcd2bin(soft_spi_read()); /* Read minutes */ | |
68 | ||
69 | /* Hours are trickier */ | |
70 | spi_byte = soft_spi_read(); /* Read Hours into temporary value */ | |
71 | if (spi_byte & 0x40) { | |
72 | /* 12 hour mode bit is set (time is in 1-12 format) */ | |
73 | if (spi_byte & 0x20) { | |
74 | /* since PM we add 11 to get 0-23 for hours */ | |
75 | tmp->tm_hour = (bcd2bin(spi_byte & 0x1F)) + 11; | |
76 | } | |
77 | else { | |
78 | /* since AM we subtract 1 to get 0-23 for hours */ | |
79 | tmp->tm_hour = (bcd2bin(spi_byte & 0x1F)) - 1; | |
80 | } | |
81 | } | |
82 | else { | |
83 | /* Otherwise, 0-23 hour format */ | |
84 | tmp->tm_hour = (bcd2bin(spi_byte & 0x3F)); | |
85 | } | |
86 | ||
87 | soft_spi_read(); /* Read and discard Day of week */ | |
88 | tmp->tm_mday = bcd2bin(soft_spi_read()); /* Read Day of the Month */ | |
89 | tmp->tm_mon = bcd2bin(soft_spi_read()); /* Read Month */ | |
90 | ||
91 | /* Read Year and convert to this century */ | |
92 | tmp->tm_year = bcd2bin(soft_spi_read()) + 2000; | |
93 | ||
94 | /* Now we can disable the DS1306 RTC */ | |
95 | immap->im_cpm.cp_pbdat &= ~PB_SPI_CE; /* Disable DS1306 Chip */ | |
96 | udelay(10); | |
97 | ||
98 | GregorianDay(tmp); /* Determine the day of week */ | |
99 | ||
100 | debug("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", | |
101 | tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, | |
102 | tmp->tm_hour, tmp->tm_min, tmp->tm_sec); | |
103 | } | |
104 | ||
105 | /* ------------------------------------------------------------------------- */ | |
106 | ||
107 | /* set clock time in DS1306 RTC and in MPC8xx RTC */ | |
108 | void rtc_set(struct rtc_time *tmp) | |
109 | { | |
110 | volatile immap_t *immap = (immap_t *)CFG_IMMR; | |
111 | ||
112 | init_spi(); /* set port B for software SPI */ | |
113 | ||
114 | /* Now we can enable the DS1306 RTC */ | |
115 | immap->im_cpm.cp_pbdat |= PB_SPI_CE; /* Enable DS1306 Chip */ | |
116 | udelay(10); | |
117 | ||
118 | /* First disable write protect in the clock chip control register */ | |
119 | soft_spi_send(0x8F); /* send address of the control register */ | |
120 | soft_spi_send(0x00); /* send control register contents */ | |
121 | ||
122 | /* Now disable the DS1306 to terminate the write */ | |
123 | immap->im_cpm.cp_pbdat &= ~PB_SPI_CE; | |
124 | udelay(10); | |
125 | ||
126 | /* Now enable the DS1306 to initiate a new write */ | |
127 | immap->im_cpm.cp_pbdat |= PB_SPI_CE; | |
128 | udelay(10); | |
129 | ||
130 | /* Next, send the address of the clock time write registers */ | |
131 | soft_spi_send(0x80); /* send address of the first time register */ | |
132 | ||
133 | /* Use Burst Mode to send all of the time data to the clock */ | |
134 | bin2bcd(tmp->tm_sec); | |
135 | soft_spi_send(bin2bcd(tmp->tm_sec)); /* Send Seconds */ | |
136 | soft_spi_send(bin2bcd(tmp->tm_min)); /* Send Minutes */ | |
137 | soft_spi_send(bin2bcd(tmp->tm_hour)); /* Send Hour */ | |
138 | soft_spi_send(bin2bcd(tmp->tm_wday)); /* Send Day of the Week */ | |
139 | soft_spi_send(bin2bcd(tmp->tm_mday)); /* Send Day of Month */ | |
140 | soft_spi_send(bin2bcd(tmp->tm_mon)); /* Send Month */ | |
141 | soft_spi_send(bin2bcd(tmp->tm_year - 2000)); /* Send Year */ | |
142 | ||
143 | /* Now we can disable the Clock chip to terminate the burst write */ | |
144 | immap->im_cpm.cp_pbdat &= ~PB_SPI_CE; /* Disable DS1306 Chip */ | |
145 | udelay(10); | |
146 | ||
147 | /* Now we can enable the Clock chip to initiate a new write */ | |
148 | immap->im_cpm.cp_pbdat |= PB_SPI_CE; /* Enable DS1306 Chip */ | |
149 | udelay(10); | |
150 | ||
151 | /* First we Enable write protect in the clock chip control register */ | |
152 | soft_spi_send(0x8F); /* send address of the control register */ | |
153 | soft_spi_send(0x40); /* send out Control Register contents */ | |
154 | ||
155 | /* Now disable the DS1306 */ | |
156 | immap->im_cpm.cp_pbdat &= ~PB_SPI_CE; /* Disable DS1306 Chip */ | |
157 | udelay(10); | |
158 | ||
159 | /* Set standard MPC8xx clock to the same time so Linux will | |
160 | * see the time even if it doesn't have a DS1306 clock driver. | |
161 | * This helps with experimenting with standard kernels. | |
162 | */ | |
163 | { | |
164 | ulong tim; | |
165 | ||
166 | tim = mktime(tmp->tm_year, tmp->tm_mon, tmp->tm_mday, | |
167 | tmp->tm_hour, tmp->tm_min, tmp->tm_sec); | |
168 | ||
169 | immap->im_sitk.sitk_rtck = KAPWR_KEY; | |
170 | immap->im_sit.sit_rtc = tim; | |
171 | } | |
172 | ||
173 | debug("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", | |
174 | tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, | |
175 | tmp->tm_hour, tmp->tm_min, tmp->tm_sec); | |
176 | } | |
177 | ||
178 | /* ------------------------------------------------------------------------- */ | |
179 | ||
180 | void rtc_reset(void) | |
181 | { | |
182 | return; /* nothing to do */ | |
183 | } | |
184 | ||
185 | /* ------------------------------------------------------------------------- */ | |
186 | ||
187 | static unsigned char bcd2bin(unsigned char n) | |
188 | { | |
189 | return ((((n >> 4) & 0x0F) * 10) + (n & 0x0F)); | |
190 | } | |
191 | ||
192 | /* ------------------------------------------------------------------------- */ | |
193 | ||
194 | static unsigned int bin2bcd(unsigned int n) | |
195 | { | |
196 | return (((n / 10) << 4) | (n % 10)); | |
197 | } | |
198 | ||
199 | /* ------------------------------------------------------------------------- */ | |
200 | ||
201 | /* Initialize Port B for software SPI */ | |
202 | static void init_spi(void) { | |
203 | volatile immap_t *immap = (immap_t *)CFG_IMMR; | |
204 | ||
205 | /* Force output pins to begin at logic 0 */ | |
206 | immap->im_cpm.cp_pbdat &= ~(PB_SPI_CE | PB_SPIMOSI | PB_SPISCK); | |
207 | ||
208 | /* Set these 3 signals as outputs */ | |
209 | immap->im_cpm.cp_pbdir |= (PB_SPIMOSI | PB_SPI_CE | PB_SPISCK); | |
210 | ||
211 | immap->im_cpm.cp_pbdir &= ~PB_SPIMISO; /* Make MISO pin an input */ | |
212 | udelay(10); | |
213 | } | |
214 | ||
215 | /* ------------------------------------------------------------------------- */ | |
216 | ||
217 | /* NOTE: soft_spi_send() assumes that the I/O lines are configured already */ | |
218 | static void soft_spi_send(unsigned char n) | |
219 | { | |
220 | volatile immap_t *immap = (immap_t *)CFG_IMMR; | |
221 | unsigned char bitpos; /* bit position to receive */ | |
222 | unsigned char i; /* Loop Control */ | |
223 | ||
224 | /* bit position to send, start with most significant bit */ | |
225 | bitpos = 0x80; | |
226 | ||
227 | /* Send 8 bits to software SPI */ | |
228 | for (i = 0; i < 8; i++) { /* Loop for 8 bits */ | |
229 | immap->im_cpm.cp_pbdat |= PB_SPISCK; /* Raise SCK */ | |
230 | ||
231 | if (n & bitpos) | |
232 | immap->im_cpm.cp_pbdat |= PB_SPIMOSI; /* Set MOSI to 1 */ | |
233 | else | |
234 | immap->im_cpm.cp_pbdat &= ~PB_SPIMOSI; /* Set MOSI to 0 */ | |
235 | udelay(10); | |
236 | ||
237 | immap->im_cpm.cp_pbdat &= ~PB_SPISCK; /* Lower SCK */ | |
238 | udelay(10); | |
239 | ||
240 | bitpos >>= 1; /* Shift for next bit position */ | |
241 | } | |
242 | } | |
243 | ||
244 | /* ------------------------------------------------------------------------- */ | |
245 | ||
246 | /* NOTE: soft_spi_read() assumes that the I/O lines are configured already */ | |
247 | static unsigned char soft_spi_read(void) | |
248 | { | |
249 | volatile immap_t *immap = (immap_t *)CFG_IMMR; | |
250 | ||
251 | unsigned char spi_byte = 0; /* Return value, assume success */ | |
252 | unsigned char bitpos; /* bit position to receive */ | |
253 | unsigned char i; /* Loop Control */ | |
254 | ||
255 | /* bit position to receive, start with most significant bit */ | |
256 | bitpos = 0x80; | |
257 | ||
258 | /* Read 8 bits here */ | |
259 | for (i = 0; i < 8; i++) { /* Do 8 bits in loop */ | |
260 | immap->im_cpm.cp_pbdat |= PB_SPISCK; /* Raise SCK */ | |
261 | udelay(10); | |
262 | if (immap->im_cpm.cp_pbdat & PB_SPIMISO) /* Get a bit of data */ | |
263 | spi_byte |= bitpos; /* Set data accordingly */ | |
264 | immap->im_cpm.cp_pbdat &= ~PB_SPISCK; /* Lower SCK */ | |
265 | udelay(10); | |
266 | bitpos >>= 1; /* Shift for next bit position */ | |
267 | } | |
268 | ||
269 | return spi_byte; /* Return the byte read */ | |
270 | } | |
271 | ||
272 | /* ------------------------------------------------------------------------- */ | |
273 | ||
274 | #endif |