xref: /minix/minix/drivers/power/tps65950/tps65950.c (revision 83133719)
1 #include <minix/ds.h>
2 #include <minix/drivers.h>
3 #include <minix/i2c.h>
4 #include <minix/i2cdriver.h>
5 #include <minix/log.h>
6 #include <minix/safecopies.h>
7 
8 #include "tps65950.h"
9 #include "rtc.h"
10 
11 /* logging - use with log_warn(), log_info(), log_debug(), log_trace(), etc */
12 static struct log log = {
13 	.name = "tps65950",
14 	.log_level = LEVEL_INFO,
15 	.log_func = default_log
16 };
17 
18 /* TPS65950 doesn't support configuring the addresses, so there is only 1
19  * configuration possible. The chip does have multiple addresses (0x48,
20  * 0x49, 0x4a, 0x4b), but because they're all fixed, we only have the
21  * user pass the base address as a sanity check.
22  */
23 static i2c_addr_t valid_addrs[2] = {
24 	0x48, 0x00
25 };
26 
27 /* the bus that this device is on (counting starting at 1) */
28 static uint32_t bus;
29 
30 /* endpoint for the driver for the bus itself. */
31 endpoint_t bus_endpoint;
32 
33 /* slave addresses of the device */
34 i2c_addr_t addresses[NADDRESSES] = {
35 	0x48, 0x49, 0x4a, 0x4b
36 };
37 
38 /* local functions */
39 static int check_revision(void);
40 
41 /* SEF related functions */
42 static void sef_local_startup(void);
43 static int sef_cb_lu_state_save(int);
44 static int lu_state_restore(void);
45 static int sef_cb_init(int type, sef_init_info_t * info);
46 
47 /* functions for transfering struct tm to/from this driver and calling proc. */
48 static int fetch_t(endpoint_t ep, cp_grant_id_t gid, struct tm *t);
49 static int store_t(endpoint_t ep, cp_grant_id_t gid, struct tm *t);
50 
51 static int
52 fetch_t(endpoint_t ep, cp_grant_id_t gid, struct tm *t)
53 {
54 	int r;
55 
56 	r = sys_safecopyfrom(ep, gid, (vir_bytes) 0, (vir_bytes) t,
57 	    sizeof(struct tm));
58 	if (r != OK) {
59 		log_warn(&log, "sys_safecopyfrom() failed (r=%d)\n", r);
60 		return r;
61 	}
62 
63 	return OK;
64 }
65 
66 static int
67 store_t(endpoint_t ep, cp_grant_id_t gid, struct tm *t)
68 {
69 	int r;
70 
71 	r = sys_safecopyto(ep, gid, (vir_bytes) 0, (vir_bytes) t,
72 	    sizeof(struct tm));
73 	if (r != OK) {
74 		log_warn(&log, "sys_safecopyto() failed (r=%d)\n", r);
75 		return r;
76 	}
77 
78 	return OK;
79 }
80 
81 static int
82 check_revision(void)
83 {
84 	int r;
85 	uint32_t idcode;
86 	uint8_t idcode_7_0, idcode_15_8, idcode_23_16, idcode_31_24;
87 
88 	/* need to write a special code to unlock read protect on IDCODE */
89 	r = i2creg_write8(bus_endpoint, addresses[ID2], UNLOCK_TEST_REG,
90 	    UNLOCK_TEST_CODE);
91 	if (r != OK) {
92 		log_warn(&log, "Failed to write unlock code to UNLOCK_TEST\n");
93 		return -1;
94 	}
95 
96 	/*
97 	 * read each part of the IDCODE
98 	 */
99 	r = i2creg_read8(bus_endpoint, addresses[ID2], IDCODE_7_0_REG,
100 	    &idcode_7_0);
101 	if (r != OK) {
102 		log_warn(&log, "Failed to read IDCODE part 1\n");
103 	}
104 
105 	r = i2creg_read8(bus_endpoint, addresses[ID2], IDCODE_15_8_REG,
106 	    &idcode_15_8);
107 	if (r != OK) {
108 		log_warn(&log, "Failed to read IDCODE part 2\n");
109 	}
110 
111 	r = i2creg_read8(bus_endpoint, addresses[ID2], IDCODE_23_16_REG,
112 	    &idcode_23_16);
113 	if (r != OK) {
114 		log_warn(&log, "Failed to read IDCODE part 3\n");
115 	}
116 
117 	r = i2creg_read8(bus_endpoint, addresses[ID2], IDCODE_31_24_REG,
118 	    &idcode_31_24);
119 	if (r != OK) {
120 		log_warn(&log, "Failed to read IDCODE part 4\n");
121 	}
122 
123 	/* combine the parts to get the full IDCODE */
124 	idcode =
125 	    ((idcode_31_24 << 24) | (idcode_23_16 << 16) | (idcode_15_8 << 8) |
126 	    (idcode_7_0 << 0));
127 
128 	log_debug(&log, "IDCODE = 0x%x\n", idcode);
129 	switch (idcode) {
130 	case IDCODE_REV_1_0:
131 		log_debug(&log, "TPS65950 rev 1.0\n");
132 		break;
133 	case IDCODE_REV_1_1:
134 		log_debug(&log, "TPS65950 rev 1.1\n");
135 		break;
136 	case IDCODE_REV_1_2:
137 		log_debug(&log, "TPS65950 rev 1.2\n");
138 		break;
139 	case 0:
140 		log_debug(&log, "TPS65950 missing in qemu\n");
141 		break;
142 	default:
143 		log_warn(&log, "Unexpected IDCODE: 0x%x\n", idcode);
144 		return -1;
145 	}
146 
147 	return OK;
148 }
149 
150 static int
151 sef_cb_lu_state_save(int UNUSED(state))
152 {
153 	/* The addresses are fixed/non-configurable so bus is the only state */
154 	ds_publish_u32("bus", bus, DSF_OVERWRITE);
155 	return OK;
156 }
157 
158 static int
159 lu_state_restore(void)
160 {
161 	/* Restore the state. */
162 	u32_t value;
163 
164 	ds_retrieve_u32("bus", &value);
165 	ds_delete_u32("bus");
166 	bus = (int) value;
167 
168 	return OK;
169 }
170 
171 static int
172 sef_cb_init(int type, sef_init_info_t * UNUSED(info))
173 {
174 	int r, i;
175 
176 	if (type == SEF_INIT_LU) {
177 		/* Restore the state. */
178 		lu_state_restore();
179 	}
180 
181 	/* look-up the endpoint for the bus driver */
182 	bus_endpoint = i2cdriver_bus_endpoint(bus);
183 	if (bus_endpoint == 0) {
184 		log_warn(&log, "Couldn't find bus driver.\n");
185 		return EXIT_FAILURE;
186 	}
187 
188 	for (i = 0; i < NADDRESSES; i++) {
189 
190 		/* claim the device */
191 		r = i2cdriver_reserve_device(bus_endpoint, addresses[i]);
192 		if (r != OK) {
193 			log_warn(&log, "Couldn't reserve device 0x%x (r=%d)\n",
194 			    addresses[i], r);
195 			return EXIT_FAILURE;
196 		}
197 	}
198 
199 	/* check that the chip / rev is reasonable */
200 	r = check_revision();
201 	if (r != OK) {
202 		/* prevent user from using the driver with a different chip */
203 		log_warn(&log, "Bad IDCODE\n");
204 		return EXIT_FAILURE;
205 	}
206 
207 	r = rtc_init();
208 	if (r != OK) {
209 		log_warn(&log, "RTC Start-up Failed\n");
210 		return EXIT_FAILURE;
211 	}
212 
213 	if (type != SEF_INIT_LU) {
214 
215 		/* sign up for updates about the i2c bus going down/up */
216 		r = i2cdriver_subscribe_bus_updates(bus);
217 		if (r != OK) {
218 			log_warn(&log, "Couldn't subscribe to bus updates\n");
219 			return EXIT_FAILURE;
220 		}
221 
222 		i2cdriver_announce(bus);
223 		log_debug(&log, "announced\n");
224 	}
225 
226 	return OK;
227 }
228 
229 static void
230 sef_local_startup(void)
231 {
232 	/*
233 	 * Register init callbacks. Use the same function for all event types
234 	 */
235 	sef_setcb_init_fresh(sef_cb_init);
236 	sef_setcb_init_lu(sef_cb_init);
237 	sef_setcb_init_restart(sef_cb_init);
238 
239 	/*
240 	 * Register live update callbacks.
241 	 */
242 	/* Agree to update immediately when LU is requested in a valid state. */
243 	sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready);
244 	/* Support live update starting from any standard state. */
245 	sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_standard);
246 	/* Register a custom routine to save the state. */
247 	sef_setcb_lu_state_save(sef_cb_lu_state_save);
248 
249 	/* Let SEF perform startup. */
250 	sef_startup();
251 }
252 
253 int
254 main(int argc, char *argv[])
255 {
256 	int r, i;
257 	struct tm t;
258 	endpoint_t user, caller;
259 	message m;
260 	int ipc_status, reply_status;
261 
262 	env_setargs(argc, argv);
263 
264 	r = i2cdriver_env_parse(&bus, &addresses[0], valid_addrs);
265 	if (r < 0) {
266 		log_warn(&log, "Expecting -args 'bus=X address=0xYY'\n");
267 		log_warn(&log, "Example -args 'bus=1 address=0x48'\n");
268 		return EXIT_FAILURE;
269 	} else if (r > 0) {
270 		log_warn(&log,
271 		    "Invalid slave address for device, expecting 0x48\n");
272 		return EXIT_FAILURE;
273 	}
274 
275 	sef_local_startup();
276 
277 	while (TRUE) {
278 
279 		/* Receive Message */
280 		r = sef_receive_status(ANY, &m, &ipc_status);
281 		if (r != OK) {
282 			log_warn(&log, "sef_receive_status() failed\n");
283 			continue;
284 		}
285 
286 		if (is_ipc_notify(ipc_status)) {
287 
288 			if (m.m_source == DS_PROC_NR) {
289 				for (i = 0; i < NADDRESSES; i++) {
290 					/* changed state, update endpoint */
291 					i2cdriver_handle_bus_update
292 					    (&bus_endpoint, bus, addresses[i]);
293 				}
294 			}
295 
296 			/* Do not reply to notifications. */
297 			continue;
298 		}
299 
300 		caller = m.m_source;
301 
302 		log_debug(&log, "Got message 0x%x from 0x%x\n", m.m_type,
303 		    caller);
304 
305 		switch (m.m_type) {
306 		case RTCDEV_GET_TIME_G:
307 			/* Any user can read the time */
308 			reply_status = rtc_get_time(&t, m.m_lc_readclock_rtcdev.flags);
309 			if (reply_status != OK) {
310 				break;
311 			}
312 
313 			/* write results back to calling process */
314 			reply_status =
315 			    store_t(caller, m.m_lc_readclock_rtcdev.grant, &t);
316 			break;
317 
318 		case RTCDEV_SET_TIME_G:
319 			/* Only super user is allowed to set the time */
320 			if (getnuid(caller) == SUPER_USER) {
321 				/* read time from calling process */
322 				reply_status =
323 				    fetch_t(caller,
324 					    m.m_lc_readclock_rtcdev.grant, &t);
325 				if (reply_status != OK) {
326 					break;
327 				}
328 
329 				reply_status =
330 				    rtc_set_time(&t,
331 					    m.m_lc_readclock_rtcdev.flags);
332 			} else {
333 				reply_status = EPERM;
334 			}
335 			break;
336 
337 		case RTCDEV_PWR_OFF:
338 			reply_status = ENOSYS;
339 			break;
340 
341 		default:
342 			/* Unrecognized call */
343 			reply_status = EINVAL;
344 			break;
345 		}
346 
347 		/* Send Reply */
348 		m.m_type = RTCDEV_REPLY;
349 		m.m_readclock_lc_rtcdev.status = reply_status;
350 
351 		log_debug(&log, "Sending Reply");
352 
353 		r = ipc_sendnb(caller, &m);
354 		if (r != OK) {
355 			log_warn(&log, "ipc_sendnb() failed\n");
356 			continue;
357 		}
358 	}
359 
360 	rtc_exit();
361 
362 	return 0;
363 }
364