1433d6423SLionel Sambuc #include <minix/ds.h>
2433d6423SLionel Sambuc #include <minix/drivers.h>
3433d6423SLionel Sambuc #include <minix/i2c.h>
4433d6423SLionel Sambuc #include <minix/i2cdriver.h>
5433d6423SLionel Sambuc #include <minix/log.h>
6433d6423SLionel Sambuc #include <minix/safecopies.h>
7433d6423SLionel Sambuc
8433d6423SLionel Sambuc #include "tps65950.h"
9433d6423SLionel Sambuc #include "rtc.h"
10433d6423SLionel Sambuc
11433d6423SLionel Sambuc /* logging - use with log_warn(), log_info(), log_debug(), log_trace(), etc */
12433d6423SLionel Sambuc static struct log log = {
13433d6423SLionel Sambuc .name = "tps65950",
14433d6423SLionel Sambuc .log_level = LEVEL_INFO,
15433d6423SLionel Sambuc .log_func = default_log
16433d6423SLionel Sambuc };
17433d6423SLionel Sambuc
18433d6423SLionel Sambuc /* TPS65950 doesn't support configuring the addresses, so there is only 1
19433d6423SLionel Sambuc * configuration possible. The chip does have multiple addresses (0x48,
20433d6423SLionel Sambuc * 0x49, 0x4a, 0x4b), but because they're all fixed, we only have the
21433d6423SLionel Sambuc * user pass the base address as a sanity check.
22433d6423SLionel Sambuc */
23433d6423SLionel Sambuc static i2c_addr_t valid_addrs[2] = {
24433d6423SLionel Sambuc 0x48, 0x00
25433d6423SLionel Sambuc };
26433d6423SLionel Sambuc
27433d6423SLionel Sambuc /* the bus that this device is on (counting starting at 1) */
28433d6423SLionel Sambuc static uint32_t bus;
29433d6423SLionel Sambuc
30433d6423SLionel Sambuc /* endpoint for the driver for the bus itself. */
31433d6423SLionel Sambuc endpoint_t bus_endpoint;
32433d6423SLionel Sambuc
33433d6423SLionel Sambuc /* slave addresses of the device */
34433d6423SLionel Sambuc i2c_addr_t addresses[NADDRESSES] = {
35433d6423SLionel Sambuc 0x48, 0x49, 0x4a, 0x4b
36433d6423SLionel Sambuc };
37433d6423SLionel Sambuc
38433d6423SLionel Sambuc /* local functions */
39433d6423SLionel Sambuc static int check_revision(void);
40433d6423SLionel Sambuc
41433d6423SLionel Sambuc /* functions for transfering struct tm to/from this driver and calling proc. */
42433d6423SLionel Sambuc static int fetch_t(endpoint_t ep, cp_grant_id_t gid, struct tm *t);
43433d6423SLionel Sambuc static int store_t(endpoint_t ep, cp_grant_id_t gid, struct tm *t);
44433d6423SLionel Sambuc
45433d6423SLionel Sambuc static int
fetch_t(endpoint_t ep,cp_grant_id_t gid,struct tm * t)46433d6423SLionel Sambuc fetch_t(endpoint_t ep, cp_grant_id_t gid, struct tm *t)
47433d6423SLionel Sambuc {
48433d6423SLionel Sambuc int r;
49433d6423SLionel Sambuc
50433d6423SLionel Sambuc r = sys_safecopyfrom(ep, gid, (vir_bytes) 0, (vir_bytes) t,
51433d6423SLionel Sambuc sizeof(struct tm));
52433d6423SLionel Sambuc if (r != OK) {
53433d6423SLionel Sambuc log_warn(&log, "sys_safecopyfrom() failed (r=%d)\n", r);
54433d6423SLionel Sambuc return r;
55433d6423SLionel Sambuc }
56433d6423SLionel Sambuc
57433d6423SLionel Sambuc return OK;
58433d6423SLionel Sambuc }
59433d6423SLionel Sambuc
60433d6423SLionel Sambuc static int
store_t(endpoint_t ep,cp_grant_id_t gid,struct tm * t)61433d6423SLionel Sambuc store_t(endpoint_t ep, cp_grant_id_t gid, struct tm *t)
62433d6423SLionel Sambuc {
63433d6423SLionel Sambuc int r;
64433d6423SLionel Sambuc
65433d6423SLionel Sambuc r = sys_safecopyto(ep, gid, (vir_bytes) 0, (vir_bytes) t,
66433d6423SLionel Sambuc sizeof(struct tm));
67433d6423SLionel Sambuc if (r != OK) {
68433d6423SLionel Sambuc log_warn(&log, "sys_safecopyto() failed (r=%d)\n", r);
69433d6423SLionel Sambuc return r;
70433d6423SLionel Sambuc }
71433d6423SLionel Sambuc
72433d6423SLionel Sambuc return OK;
73433d6423SLionel Sambuc }
74433d6423SLionel Sambuc
75433d6423SLionel Sambuc static int
check_revision(void)76433d6423SLionel Sambuc check_revision(void)
77433d6423SLionel Sambuc {
78433d6423SLionel Sambuc int r;
79433d6423SLionel Sambuc uint32_t idcode;
80433d6423SLionel Sambuc uint8_t idcode_7_0, idcode_15_8, idcode_23_16, idcode_31_24;
81433d6423SLionel Sambuc
82433d6423SLionel Sambuc /* need to write a special code to unlock read protect on IDCODE */
83433d6423SLionel Sambuc r = i2creg_write8(bus_endpoint, addresses[ID2], UNLOCK_TEST_REG,
84433d6423SLionel Sambuc UNLOCK_TEST_CODE);
85433d6423SLionel Sambuc if (r != OK) {
86433d6423SLionel Sambuc log_warn(&log, "Failed to write unlock code to UNLOCK_TEST\n");
87433d6423SLionel Sambuc return -1;
88433d6423SLionel Sambuc }
89433d6423SLionel Sambuc
90433d6423SLionel Sambuc /*
91433d6423SLionel Sambuc * read each part of the IDCODE
92433d6423SLionel Sambuc */
93433d6423SLionel Sambuc r = i2creg_read8(bus_endpoint, addresses[ID2], IDCODE_7_0_REG,
94433d6423SLionel Sambuc &idcode_7_0);
95433d6423SLionel Sambuc if (r != OK) {
96433d6423SLionel Sambuc log_warn(&log, "Failed to read IDCODE part 1\n");
97433d6423SLionel Sambuc }
98433d6423SLionel Sambuc
99433d6423SLionel Sambuc r = i2creg_read8(bus_endpoint, addresses[ID2], IDCODE_15_8_REG,
100433d6423SLionel Sambuc &idcode_15_8);
101433d6423SLionel Sambuc if (r != OK) {
102433d6423SLionel Sambuc log_warn(&log, "Failed to read IDCODE part 2\n");
103433d6423SLionel Sambuc }
104433d6423SLionel Sambuc
105433d6423SLionel Sambuc r = i2creg_read8(bus_endpoint, addresses[ID2], IDCODE_23_16_REG,
106433d6423SLionel Sambuc &idcode_23_16);
107433d6423SLionel Sambuc if (r != OK) {
108433d6423SLionel Sambuc log_warn(&log, "Failed to read IDCODE part 3\n");
109433d6423SLionel Sambuc }
110433d6423SLionel Sambuc
111433d6423SLionel Sambuc r = i2creg_read8(bus_endpoint, addresses[ID2], IDCODE_31_24_REG,
112433d6423SLionel Sambuc &idcode_31_24);
113433d6423SLionel Sambuc if (r != OK) {
114433d6423SLionel Sambuc log_warn(&log, "Failed to read IDCODE part 4\n");
115433d6423SLionel Sambuc }
116433d6423SLionel Sambuc
117433d6423SLionel Sambuc /* combine the parts to get the full IDCODE */
118433d6423SLionel Sambuc idcode =
119433d6423SLionel Sambuc ((idcode_31_24 << 24) | (idcode_23_16 << 16) | (idcode_15_8 << 8) |
120433d6423SLionel Sambuc (idcode_7_0 << 0));
121433d6423SLionel Sambuc
122433d6423SLionel Sambuc log_debug(&log, "IDCODE = 0x%x\n", idcode);
123433d6423SLionel Sambuc switch (idcode) {
124433d6423SLionel Sambuc case IDCODE_REV_1_0:
125433d6423SLionel Sambuc log_debug(&log, "TPS65950 rev 1.0\n");
126433d6423SLionel Sambuc break;
127433d6423SLionel Sambuc case IDCODE_REV_1_1:
128433d6423SLionel Sambuc log_debug(&log, "TPS65950 rev 1.1\n");
129433d6423SLionel Sambuc break;
130433d6423SLionel Sambuc case IDCODE_REV_1_2:
131433d6423SLionel Sambuc log_debug(&log, "TPS65950 rev 1.2\n");
132433d6423SLionel Sambuc break;
13385ebb0e0SBen Gras case 0:
13485ebb0e0SBen Gras log_debug(&log, "TPS65950 missing in qemu\n");
13585ebb0e0SBen Gras break;
136433d6423SLionel Sambuc default:
137433d6423SLionel Sambuc log_warn(&log, "Unexpected IDCODE: 0x%x\n", idcode);
138433d6423SLionel Sambuc return -1;
139433d6423SLionel Sambuc }
140433d6423SLionel Sambuc
141433d6423SLionel Sambuc return OK;
142433d6423SLionel Sambuc }
143433d6423SLionel Sambuc
144433d6423SLionel Sambuc static int
sef_cb_lu_state_save(int UNUSED (result),int UNUSED (flags))145*e1f889d2SCristiano Giuffrida sef_cb_lu_state_save(int UNUSED(result), int UNUSED(flags))
146433d6423SLionel Sambuc {
147433d6423SLionel Sambuc /* The addresses are fixed/non-configurable so bus is the only state */
148433d6423SLionel Sambuc ds_publish_u32("bus", bus, DSF_OVERWRITE);
149433d6423SLionel Sambuc return OK;
150433d6423SLionel Sambuc }
151433d6423SLionel Sambuc
152433d6423SLionel Sambuc static int
lu_state_restore(void)153433d6423SLionel Sambuc lu_state_restore(void)
154433d6423SLionel Sambuc {
155433d6423SLionel Sambuc /* Restore the state. */
156433d6423SLionel Sambuc u32_t value;
157433d6423SLionel Sambuc
158433d6423SLionel Sambuc ds_retrieve_u32("bus", &value);
159433d6423SLionel Sambuc ds_delete_u32("bus");
160433d6423SLionel Sambuc bus = (int) value;
161433d6423SLionel Sambuc
162433d6423SLionel Sambuc return OK;
163433d6423SLionel Sambuc }
164433d6423SLionel Sambuc
165433d6423SLionel Sambuc static int
sef_cb_init(int type,sef_init_info_t * UNUSED (info))166433d6423SLionel Sambuc sef_cb_init(int type, sef_init_info_t * UNUSED(info))
167433d6423SLionel Sambuc {
168433d6423SLionel Sambuc int r, i;
169433d6423SLionel Sambuc
170433d6423SLionel Sambuc if (type == SEF_INIT_LU) {
171433d6423SLionel Sambuc /* Restore the state. */
172433d6423SLionel Sambuc lu_state_restore();
173433d6423SLionel Sambuc }
174433d6423SLionel Sambuc
175433d6423SLionel Sambuc /* look-up the endpoint for the bus driver */
176433d6423SLionel Sambuc bus_endpoint = i2cdriver_bus_endpoint(bus);
177433d6423SLionel Sambuc if (bus_endpoint == 0) {
178433d6423SLionel Sambuc log_warn(&log, "Couldn't find bus driver.\n");
179433d6423SLionel Sambuc return EXIT_FAILURE;
180433d6423SLionel Sambuc }
181433d6423SLionel Sambuc
182433d6423SLionel Sambuc for (i = 0; i < NADDRESSES; i++) {
183433d6423SLionel Sambuc
184433d6423SLionel Sambuc /* claim the device */
185433d6423SLionel Sambuc r = i2cdriver_reserve_device(bus_endpoint, addresses[i]);
186433d6423SLionel Sambuc if (r != OK) {
187433d6423SLionel Sambuc log_warn(&log, "Couldn't reserve device 0x%x (r=%d)\n",
188433d6423SLionel Sambuc addresses[i], r);
189433d6423SLionel Sambuc return EXIT_FAILURE;
190433d6423SLionel Sambuc }
191433d6423SLionel Sambuc }
192433d6423SLionel Sambuc
193433d6423SLionel Sambuc /* check that the chip / rev is reasonable */
194433d6423SLionel Sambuc r = check_revision();
195433d6423SLionel Sambuc if (r != OK) {
196433d6423SLionel Sambuc /* prevent user from using the driver with a different chip */
197433d6423SLionel Sambuc log_warn(&log, "Bad IDCODE\n");
198433d6423SLionel Sambuc return EXIT_FAILURE;
199433d6423SLionel Sambuc }
200433d6423SLionel Sambuc
201433d6423SLionel Sambuc r = rtc_init();
202433d6423SLionel Sambuc if (r != OK) {
203433d6423SLionel Sambuc log_warn(&log, "RTC Start-up Failed\n");
204433d6423SLionel Sambuc return EXIT_FAILURE;
205433d6423SLionel Sambuc }
206433d6423SLionel Sambuc
207433d6423SLionel Sambuc if (type != SEF_INIT_LU) {
208433d6423SLionel Sambuc
209433d6423SLionel Sambuc /* sign up for updates about the i2c bus going down/up */
210433d6423SLionel Sambuc r = i2cdriver_subscribe_bus_updates(bus);
211433d6423SLionel Sambuc if (r != OK) {
212433d6423SLionel Sambuc log_warn(&log, "Couldn't subscribe to bus updates\n");
213433d6423SLionel Sambuc return EXIT_FAILURE;
214433d6423SLionel Sambuc }
215433d6423SLionel Sambuc
216433d6423SLionel Sambuc i2cdriver_announce(bus);
217433d6423SLionel Sambuc log_debug(&log, "announced\n");
218433d6423SLionel Sambuc }
219433d6423SLionel Sambuc
220433d6423SLionel Sambuc return OK;
221433d6423SLionel Sambuc }
222433d6423SLionel Sambuc
223433d6423SLionel Sambuc static void
sef_local_startup(void)224433d6423SLionel Sambuc sef_local_startup(void)
225433d6423SLionel Sambuc {
226433d6423SLionel Sambuc /*
227433d6423SLionel Sambuc * Register init callbacks. Use the same function for all event types
228433d6423SLionel Sambuc */
229433d6423SLionel Sambuc sef_setcb_init_fresh(sef_cb_init);
230433d6423SLionel Sambuc sef_setcb_init_lu(sef_cb_init);
231433d6423SLionel Sambuc sef_setcb_init_restart(sef_cb_init);
232433d6423SLionel Sambuc
233433d6423SLionel Sambuc /*
234433d6423SLionel Sambuc * Register live update callbacks.
235433d6423SLionel Sambuc */
236433d6423SLionel Sambuc sef_setcb_lu_state_save(sef_cb_lu_state_save);
237433d6423SLionel Sambuc
238433d6423SLionel Sambuc /* Let SEF perform startup. */
239433d6423SLionel Sambuc sef_startup();
240433d6423SLionel Sambuc }
241433d6423SLionel Sambuc
242433d6423SLionel Sambuc int
main(int argc,char * argv[])243433d6423SLionel Sambuc main(int argc, char *argv[])
244433d6423SLionel Sambuc {
245433d6423SLionel Sambuc int r, i;
246433d6423SLionel Sambuc struct tm t;
247433d6423SLionel Sambuc endpoint_t user, caller;
248433d6423SLionel Sambuc message m;
249433d6423SLionel Sambuc int ipc_status, reply_status;
250433d6423SLionel Sambuc
251433d6423SLionel Sambuc env_setargs(argc, argv);
252433d6423SLionel Sambuc
253433d6423SLionel Sambuc r = i2cdriver_env_parse(&bus, &addresses[0], valid_addrs);
254433d6423SLionel Sambuc if (r < 0) {
255433d6423SLionel Sambuc log_warn(&log, "Expecting -args 'bus=X address=0xYY'\n");
256433d6423SLionel Sambuc log_warn(&log, "Example -args 'bus=1 address=0x48'\n");
257433d6423SLionel Sambuc return EXIT_FAILURE;
258433d6423SLionel Sambuc } else if (r > 0) {
259433d6423SLionel Sambuc log_warn(&log,
260433d6423SLionel Sambuc "Invalid slave address for device, expecting 0x48\n");
261433d6423SLionel Sambuc return EXIT_FAILURE;
262433d6423SLionel Sambuc }
263433d6423SLionel Sambuc
264433d6423SLionel Sambuc sef_local_startup();
265433d6423SLionel Sambuc
266433d6423SLionel Sambuc while (TRUE) {
267433d6423SLionel Sambuc
268433d6423SLionel Sambuc /* Receive Message */
269433d6423SLionel Sambuc r = sef_receive_status(ANY, &m, &ipc_status);
270433d6423SLionel Sambuc if (r != OK) {
271433d6423SLionel Sambuc log_warn(&log, "sef_receive_status() failed\n");
272433d6423SLionel Sambuc continue;
273433d6423SLionel Sambuc }
274433d6423SLionel Sambuc
275433d6423SLionel Sambuc if (is_ipc_notify(ipc_status)) {
276433d6423SLionel Sambuc
277433d6423SLionel Sambuc if (m.m_source == DS_PROC_NR) {
278433d6423SLionel Sambuc for (i = 0; i < NADDRESSES; i++) {
279433d6423SLionel Sambuc /* changed state, update endpoint */
280433d6423SLionel Sambuc i2cdriver_handle_bus_update
281433d6423SLionel Sambuc (&bus_endpoint, bus, addresses[i]);
282433d6423SLionel Sambuc }
283433d6423SLionel Sambuc }
284433d6423SLionel Sambuc
285433d6423SLionel Sambuc /* Do not reply to notifications. */
286433d6423SLionel Sambuc continue;
287433d6423SLionel Sambuc }
288433d6423SLionel Sambuc
289433d6423SLionel Sambuc caller = m.m_source;
290433d6423SLionel Sambuc
291433d6423SLionel Sambuc log_debug(&log, "Got message 0x%x from 0x%x\n", m.m_type,
292433d6423SLionel Sambuc caller);
293433d6423SLionel Sambuc
294433d6423SLionel Sambuc switch (m.m_type) {
295433d6423SLionel Sambuc case RTCDEV_GET_TIME_G:
296433d6423SLionel Sambuc /* Any user can read the time */
297433d6423SLionel Sambuc reply_status = rtc_get_time(&t, m.m_lc_readclock_rtcdev.flags);
298433d6423SLionel Sambuc if (reply_status != OK) {
299433d6423SLionel Sambuc break;
300433d6423SLionel Sambuc }
301433d6423SLionel Sambuc
302433d6423SLionel Sambuc /* write results back to calling process */
303433d6423SLionel Sambuc reply_status =
304433d6423SLionel Sambuc store_t(caller, m.m_lc_readclock_rtcdev.grant, &t);
305433d6423SLionel Sambuc break;
306433d6423SLionel Sambuc
307433d6423SLionel Sambuc case RTCDEV_SET_TIME_G:
308433d6423SLionel Sambuc /* Only super user is allowed to set the time */
309433d6423SLionel Sambuc if (getnuid(caller) == SUPER_USER) {
310433d6423SLionel Sambuc /* read time from calling process */
311433d6423SLionel Sambuc reply_status =
312433d6423SLionel Sambuc fetch_t(caller,
313433d6423SLionel Sambuc m.m_lc_readclock_rtcdev.grant, &t);
314433d6423SLionel Sambuc if (reply_status != OK) {
315433d6423SLionel Sambuc break;
316433d6423SLionel Sambuc }
317433d6423SLionel Sambuc
318433d6423SLionel Sambuc reply_status =
319433d6423SLionel Sambuc rtc_set_time(&t,
320433d6423SLionel Sambuc m.m_lc_readclock_rtcdev.flags);
321433d6423SLionel Sambuc } else {
322433d6423SLionel Sambuc reply_status = EPERM;
323433d6423SLionel Sambuc }
324433d6423SLionel Sambuc break;
325433d6423SLionel Sambuc
326433d6423SLionel Sambuc case RTCDEV_PWR_OFF:
327433d6423SLionel Sambuc reply_status = ENOSYS;
328433d6423SLionel Sambuc break;
329433d6423SLionel Sambuc
330433d6423SLionel Sambuc default:
331433d6423SLionel Sambuc /* Unrecognized call */
332433d6423SLionel Sambuc reply_status = EINVAL;
333433d6423SLionel Sambuc break;
334433d6423SLionel Sambuc }
335433d6423SLionel Sambuc
336433d6423SLionel Sambuc /* Send Reply */
337433d6423SLionel Sambuc m.m_type = RTCDEV_REPLY;
338433d6423SLionel Sambuc m.m_readclock_lc_rtcdev.status = reply_status;
339433d6423SLionel Sambuc
340433d6423SLionel Sambuc log_debug(&log, "Sending Reply");
341433d6423SLionel Sambuc
342433d6423SLionel Sambuc r = ipc_sendnb(caller, &m);
343433d6423SLionel Sambuc if (r != OK) {
344433d6423SLionel Sambuc log_warn(&log, "ipc_sendnb() failed\n");
345433d6423SLionel Sambuc continue;
346433d6423SLionel Sambuc }
347433d6423SLionel Sambuc }
348433d6423SLionel Sambuc
349433d6423SLionel Sambuc rtc_exit();
350433d6423SLionel Sambuc
351433d6423SLionel Sambuc return 0;
352433d6423SLionel Sambuc }
353