custom message type for VM_INFO
[minix3.git] / drivers / tps65217 / tps65217.c
blob787954f60731a2e2109b0d176b79ace767cd8109
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>
7 #include <sys/signal.h>
9 /* Register Addresses */
10 #define CHIPID_REG 0x00
11 #define PPATH_REG 0x01
12 #define INT_REG 0x02
13 #define CHGCONFIG0_REG 0x03
14 #define CHGCONFIG1_REG 0x04
15 #define CHGCONFIG2_REG 0x05
16 #define CHGCONFIG3_REG 0x06
17 #define WLEDCTRL1_REG 0x07
18 #define WLEDCTRL2_REG 0x08
19 #define MUXCTRL_REG 0x09
20 #define STATUS_REG 0x0a
21 #define PASSWORD_REG 0x0b
22 #define PGOOD_REG 0x0c
23 #define DEFPG_REG 0x0d
24 #define DEFDCDC1_REG 0x0e
25 #define DEFDCDC2_REG 0x0f
26 #define DEFDCDC3_REG 0x10
27 #define DEFSLEW_REG 0x11
28 #define DEFLDO1_REG 0x12
29 #define DEFLDO2_REG 0x13
30 #define DEFLS1_REG 0x14
31 #define DEFLS2_REG 0x15
32 #define ENABLE_REG 0x16
33 /* no documented register at 0x17 */
34 #define DEFUVLO_REG 0x18
35 #define SEQ1_REG 0x19
36 #define SEQ2_REG 0x1a
37 #define SEQ3_REG 0x1b
38 #define SEQ4_REG 0x1c
39 #define SEQ5_REG 0x1d
40 #define SEQ6_REG 0x1e
42 /* Bits and Masks */
45 * CHIP masks - CHIPID_REG[7:4]
47 #define TPS65217A_CHIP_MASK 0x70
48 #define TPS65217B_CHIP_MASK 0xf0
49 #define TPS65217C_CHIP_MASK 0xe0
50 #define TPS65217D_CHIP_MASK 0x60
53 * Interrupt Enable/Disable Bits/Masks - INT_REG[6:4]
54 * 0=Enable 1=Disable | Default mask: Disable ACM, USBM ~ Enable only PBM
56 #define PBM_INT_DIS_BIT 6
57 #define ACM_INT_DIS_BIT 5
58 #define USBM_INT_DIS_BIT 4
59 #define DEFAULT_INT_MASK ((1<<ACM_INT_DIS_BIT)|(1<<USBM_INT_DIS_BIT))
62 * Interrupt Status Bits - INT_REG[3:0]
64 #define PBI_BIT 2
65 #define ACI_BIT 1
66 #define USBI_BIT 0
67 #define PBI_MASK (1<<PBI_BIT)
70 * Power Off Bit - STATUS[7]
72 #define OFF_BIT 7
73 #define PWR_OFF_MASK (1<<OFF_BIT)
75 /* The TPS65217 is connected to the NMI pin of the AM335X on the BeagleBone and
76 * BeagleBone Black. That line is used to signal to the SoC that an interrupt
77 * has happened in the TPS65217. The NMI pin in turn generates an interrupt
78 * in the SoC which this driver will receive.
80 static int irq = 7;
81 static int irq_hook_id = 7;
82 static int irq_hook_kernel_id = 7;
84 /* Only valid slave address for this device is 0x24 */
85 static i2c_addr_t valid_addrs[2] = {
86 0x24, 0x00
89 /* the bus that this device is on (counting starting at 1) */
90 static uint32_t bus;
92 /* slave address of the device */
93 static i2c_addr_t address;
95 /* endpoint for the driver for the bus itself. */
96 static endpoint_t bus_endpoint;
98 /* logging - use with log_warn(), log_info(), log_debug(), log_trace(), etc */
99 static struct log log = {
100 .name = "tps65217",
101 .log_level = LEVEL_INFO,
102 .log_func = default_log
105 /* Device Specific Functions */
106 static int check_revision(void);
107 static int enable_pwr_off(void);
108 static int intr_enable(void);
109 static int intr_handler(void);
110 static void do_shutdown(int how);
112 /* SEF Related Function Prototypes */
113 static void sef_local_startup(void);
114 static int sef_cb_lu_state_save(int);
115 static int lu_state_restore(void);
116 static int sef_cb_init(int type, sef_init_info_t * info);
118 static int
119 check_revision(void)
121 int r;
122 uint8_t chipid;
124 r = i2creg_read8(bus_endpoint, address, CHIPID_REG, &chipid);
125 if (r != OK) {
126 log_warn(&log, "Failed to read CHIPID\n");
127 return -1;
130 switch (chipid & 0xf0) {
131 case TPS65217A_CHIP_MASK:
132 log_debug(&log, "TPS65217A rev 1.%d\n", (chipid & 0x0f));
133 break;
134 case TPS65217B_CHIP_MASK:
135 log_debug(&log, "TPS65217B rev 1.%d\n", (chipid & 0x0f));
136 break;
137 case TPS65217C_CHIP_MASK:
138 log_debug(&log, "TPS65217C rev 1.%d\n", (chipid & 0x0f));
139 break;
140 case TPS65217D_CHIP_MASK:
141 log_debug(&log, "TPS65217D rev 1.%d\n", (chipid & 0x0f));
142 break;
143 default:
144 log_warn(&log, "Unexpected CHIPID: 0x%x\n", chipid);
145 return -1;
148 return OK;
151 static int
152 enable_pwr_off(void)
154 int r;
156 /* enable power off via the PWR_EN pin. just do the setup here.
157 * the kernel will do the work to toggle the pin when the
158 * system is ready to be powered off. Should be called during startup
159 * so that shutdown(8) can do power-off with reboot().
161 r = i2creg_write8(bus_endpoint, address, STATUS_REG, PWR_OFF_MASK);
162 if (r != OK) {
163 log_warn(&log, "Cannot set power off mask.");
164 return -1;
167 return r;
170 static int
171 intr_enable(void)
173 int r;
174 uint8_t val;
175 static int policy_set = 0;
176 static int irq_enabled = 0;
178 /* Enable IRQ */
179 if (!policy_set) {
180 r = sys_irqsetpolicy(irq, 0, &irq_hook_kernel_id);
181 if (r == OK) {
182 policy_set = 1;
183 } else {
184 log_warn(&log, "Couldn't set irq policy\n");
185 return -1;
188 if (policy_set && !irq_enabled) {
189 r = sys_irqenable(&irq_hook_kernel_id);
190 if (r == OK) {
191 irq_enabled = 1;
192 } else {
193 log_warn(&log, "Couldn't enable irq %d (hooked)\n",
194 irq);
195 return -1;
199 /* Enable/Disable interrupts in the TPS65217 */
200 r = i2creg_write8(bus_endpoint, address, INT_REG, DEFAULT_INT_MASK);
201 if (r != OK) {
202 log_warn(&log, "Failed to set interrupt mask.\n");
203 return -1;
206 /* Read from the interrupt register to clear any pending interrupts */
207 r = i2creg_read8(bus_endpoint, address, INT_REG, &val);
208 if (r != OK) {
209 log_warn(&log, "Failed to read interrupt register.\n");
210 return -1;
213 return OK;
216 static int
217 intr_handler(void)
219 int r;
220 uint8_t val;
221 struct tm t;
223 /* read interrupt register to get interrupt that fired and clear it */
224 r = i2creg_read8(bus_endpoint, address, INT_REG, &val);
225 if (r != OK) {
226 log_warn(&log, "Failed to read interrupt register.\n");
227 return -1;
230 if ((val & PBI_MASK) != 0) {
231 log_info(&log, "Power Button Pressed\n");
232 kill(1, SIGUSR1); /* tell init to powerdwn */
233 return OK;
236 /* re-enable interrupt */
237 r = sys_irqenable(&irq_hook_kernel_id);
238 if (r != OK) {
239 log_warn(&log, "Unable to renable IRQ (r=%d)\n", r);
240 return -1;
243 return OK;
246 static int
247 sef_cb_lu_state_save(int UNUSED(state))
249 ds_publish_u32("bus", bus, DSF_OVERWRITE);
250 ds_publish_u32("address", address, DSF_OVERWRITE);
251 return OK;
254 static int
255 lu_state_restore(void)
257 /* Restore the state. */
258 u32_t value;
260 ds_retrieve_u32("bus", &value);
261 ds_delete_u32("bus");
262 bus = (int) value;
264 ds_retrieve_u32("address", &value);
265 ds_delete_u32("address");
266 address = (int) value;
268 return OK;
271 static int
272 sef_cb_init(int type, sef_init_info_t * UNUSED(info))
274 int r;
276 if (type == SEF_INIT_LU) {
277 /* Restore the state. */
278 lu_state_restore();
281 /* look-up the endpoint for the bus driver */
282 bus_endpoint = i2cdriver_bus_endpoint(bus);
283 if (bus_endpoint == 0) {
284 log_warn(&log, "Couldn't find bus driver.\n");
285 return EXIT_FAILURE;
288 /* claim the device */
289 r = i2cdriver_reserve_device(bus_endpoint, address);
290 if (r != OK) {
291 log_warn(&log, "Couldn't reserve device 0x%x (r=%d)\n",
292 address, r);
293 return EXIT_FAILURE;
296 /* check that the chip / rev is reasonable */
297 r = check_revision();
298 if (r != OK) {
299 /* prevent user from using the driver with a different chip */
300 log_warn(&log, "Bad CHIPID\n");
301 return EXIT_FAILURE;
304 /* enable interrupts */
305 r = intr_enable();
306 if (r != OK) {
307 log_warn(&log, "Failed to enable interrupts.\n");
308 return EXIT_FAILURE;
311 /* enable power-off pin so the kernel can cut power to the SoC */
312 enable_pwr_off();
314 if (type != SEF_INIT_LU) {
316 /* sign up for updates about the i2c bus going down/up */
317 r = i2cdriver_subscribe_bus_updates(bus);
318 if (r != OK) {
319 log_warn(&log, "Couldn't subscribe to bus updates\n");
320 return EXIT_FAILURE;
323 i2cdriver_announce(bus);
324 log_debug(&log, "announced\n");
327 return OK;
330 static void
331 sef_local_startup(void)
334 * Register init callbacks. Use the same function for all event types
336 sef_setcb_init_fresh(sef_cb_init);
337 sef_setcb_init_lu(sef_cb_init);
338 sef_setcb_init_restart(sef_cb_init);
341 * Register live update callbacks.
343 /* Agree to update immediately when LU is requested in a valid state. */
344 sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready);
345 /* Support live update starting from any standard state. */
346 sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_standard);
347 /* Register a custom routine to save the state. */
348 sef_setcb_lu_state_save(sef_cb_lu_state_save);
350 /* Let SEF perform startup. */
351 sef_startup();
355 main(int argc, char *argv[])
357 int r;
358 message m;
359 int ipc_status;
361 env_setargs(argc, argv);
363 r = i2cdriver_env_parse(&bus, &address, valid_addrs);
364 if (r < 0) {
365 log_warn(&log, "Expecting -args 'bus=X address=0xYY'\n");
366 log_warn(&log, "Example -args 'bus=1 address=0x24'\n");
367 return EXIT_FAILURE;
368 } else if (r > 0) {
369 log_warn(&log,
370 "Invalid slave address for device, expecting 0x24\n");
371 return EXIT_FAILURE;
374 sef_local_startup();
376 while (TRUE) {
378 /* Receive Message */
379 r = sef_receive_status(ANY, &m, &ipc_status);
380 if (r != OK) {
381 log_warn(&log, "sef_receive_status() failed\n");
382 continue;
385 log_trace(&log, "Got a message 0x%x from 0x%x\n", m.m_type,
386 m.m_source);
388 if (is_ipc_notify(ipc_status)) {
390 switch (m.m_source) {
392 case DS_PROC_NR:
393 /* bus driver changed state, update endpoint */
394 i2cdriver_handle_bus_update(&bus_endpoint, bus,
395 address);
396 break;
397 case HARDWARE:
398 intr_handler();
399 break;
400 default:
401 break;
404 /* Do not reply to notifications. */
405 continue;
408 log_warn(&log, "Ignoring message 0x%x from 0x%x\n", m.m_type,
409 m.m_source);
412 return 0;