xref: /minix3/minix/drivers/power/tps65950/tps65950.c (revision 3f82ac6a4e188419336747098d0d6616cd2f3d3d)
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