Changes to twl6030 Mackerel file to allow send register manipulations over i2c bus...
authorSimon Gerber <simon.gerber@inf.ethz.ch>
Thu, 27 Jun 2013 09:17:55 +0000 (11:17 +0200)
committerGerd Zellweger <mail@gerdzellweger.com>
Tue, 16 Jul 2013 12:55:00 +0000 (14:55 +0200)
devices/omap/omap44xx_ctrlmod.dev
devices/ti_i2c.dev
devices/ti_twl6030.dev
kernel/include/arch/armv7/ti_i2c.h
usr/drivers/omap44xx/mmchs/ctrlmod.c
usr/drivers/omap44xx/mmchs/i2c.c
usr/drivers/omap44xx/mmchs/mmchs.c
usr/drivers/omap44xx/mmchs/ti_twl6030.h [new file with mode: 0644]
usr/drivers/omap44xx/mmchs/twl6030.c [new file with mode: 0644]

index 57c8f1d..c509d33 100644 (file)
@@ -21,7 +21,7 @@
  
  device omap44xx_ctrlmod msbfirst (addr gen_core, 
                                    addr gen_wkup, 
-                                       addr pad_core, 
+                                   addr pad_core, 
                                    addr pad_wkup) "TI Control Module" {
 
 
index 4ad2fbd..d49ba9a 100644 (file)
@@ -143,23 +143,24 @@ device ti_i2c msbfirst (addr b) "TI I2C controller" {
         type(irqenable);
 
     // dummy constants for polling stat flags
-    constants irqstatus_flags "irqstatus bitmasks" {
-        irq_flag_xdr  =0b1110    "Transmit draining";
-        irq_flag_rdr  =0b1101    "Receive draining";
-        irq_flag_bb   =0b1100    "Bus busy";
-        irq_flag_rovr =0b1011    "Receive overrun";
-        irq_flag_xudf =0b1010    "Transmit underflow";
-        irq_flag_aas  =0b1001    "Address recognized as slave IRQ status";
-        irq_flag_bf   =0b1000    "Bus free";
-        irq_flag_aerr =0b0111    "Access error";
-        irq_flag_stc  =0b0110    "Start condition";
-        irq_flag_gc   =0b0101    "General call";
-        irq_flag_xrdy =0b0100    "Transmit data ready";
-        irq_flag_rrdy =0b0011    "Receive data ready";
-        irq_flag_ardy =0b0010    "Register access ready";
-        irq_flag_nack =0b0001    "No acknowledgement";
-        irq_flag_al   =0b0000    "Arbitration lost";
+    constants irqstatus_flags width(16) "irqstatus bitmasks" {
+        irq_flag_xdr  = 0x4000 "Transmit draining";
+        irq_flag_rdr  = 0x2000 "Receive draining";
+        irq_flag_bb   = 0x1000 "Bus busy";
+        irq_flag_rovr = 0x0800 "Receive overrun";
+        irq_flag_xudf = 0x0400 "Transmit underflow";
+        irq_flag_aas  = 0x0200 "Address recognized as slave IRQ status";
+        irq_flag_bf   = 0x0100 "Bus free";
+        irq_flag_aerr = 0x0080 "Access error";
+        irq_flag_stc  = 0x0040 "Start condition";
+        irq_flag_gc   = 0x0020 "General call";
+        irq_flag_xrdy = 0x0010 "Transmit data ready";
+        irq_flag_rrdy = 0x0008 "Receive data ready";
+        irq_flag_ardy = 0x0004 "Register access ready";
+        irq_flag_nack = 0x0002 "No acknowledgement";
+        irq_flag_al   = 0x0001 "Arbitration lost";
     };
+
     // Table 23-63
     register stat rw1c addr(b, 0x88) "Interrupt status vector (legacy)"
         type(irqstatus);
index a213d43..9dfa056 100644 (file)
@@ -28,6 +28,9 @@ device ti_twl6030 msbfirst (addr b) "TI TWL6030 Power Companion (via I2C)" {
         i2c_addr_vmmc = 0x48 "VMMC I2C address";
     };
 
+    // address spaces for I2C physical adresses
+    // id1 == i2c addr 0x48
+    space id1(i) valuewise "Registers accessed via I2C addr 0x48";
 
     regtype cfg_grp "Group Configuration" {
         _       5 rsvd;
@@ -36,7 +39,7 @@ device ti_twl6030 msbfirst (addr b) "TI TWL6030 Power Companion (via I2C)" {
         grp_app 1 "Application Power Group";
     };
 
-    constants transition_cmd "Transition Command" {
+    constants transition_cmd width(2) "Transition Command" {
         tcmd_off = 0b00 "Off";
         tcmd_ams = 0b01 "Sleep/Active";
         // 0b10 is reserved
@@ -49,7 +52,7 @@ device ti_twl6030 msbfirst (addr b) "TI TWL6030 Power Companion (via I2C)" {
         active 2 type(transition_cmd) "active state";
     };
 
-    constants pwrstate "Power State" {
+    constants pwrstate width(2) "Power State" {
         pwr_off   = 0b00 "Off";
         pwr_on    = 0b01 "On";
         pwr_off_2 = 0b10 "Off";
@@ -71,12 +74,12 @@ device ti_twl6030 msbfirst (addr b) "TI TWL6030 Power Companion (via I2C)" {
         state     2 type(pwrstate) "Resource state after power group arbitration";
     };
 
-    constants wr "Warm reset" {
+    constants wr width(1) "Warm reset" {
         wr_reload_dflt = 0b0 "Reload default VSEL value on warm reset";
         wr_keep        = 0b1 "Keep voltage configuration settings on warm reset";
     };
     // Table 121
-    constants vsel "Voltage selector" {
+    constants vsel width(5) "Voltage selector" {
         v0v0 = 0b00000 "0.0V";
         v1v0 = 0b00001 "1.0V";
         v1v1 = 0b00010 "1.1V";
@@ -113,13 +116,22 @@ device ti_twl6030 msbfirst (addr b) "TI TWL6030 Power Companion (via I2C)" {
 
     // VMMC registers
     // Table 152
-    register vmmc_cfg_grp addr(b, 0x98) "VMMC Group Configuration" type(cfg_grp);
+    //register vmmc_cfg_grp addr(b, 0x98) "VMMC Group Configuration" type(cfg_grp);
+    register vmmc_cfg_grp id1(0x98) "VMMC Group Configuration" type(cfg_grp);
+
     // Table 153
-    register vmmc_cfg_trans addr(b, 0x99) "VMMC Transition Configuration" type(cfg_trans);
+    //register vmmc_cfg_trans addr(b, 0x99) "VMMC Transition Configuration" type(cfg_trans);
+    register vmmc_cfg_trans id1(0x99) "VMMC Transition Configuration" type(cfg_trans);
+
     // Table 154
-    register vmmc_cfg_state_w wo addr(b, 0x9A) "VMMC State Configuration (write format)" type(cfg_state_w);
+    //register vmmc_cfg_state_w wo addr(b, 0x9A) "VMMC State Configuration (write format)" type(cfg_state_w);
+    register vmmc_cfg_state_w wo id1(0x9A) "VMMC State Configuration (write format)" type(cfg_state_w);
+
     // Table 155
-    register vmmc_cfg_state_r ro also addr(b, 0x9A) "VMMC State Configuration (read format)" type(cfg_state_r);
+    //register vmmc_cfg_state_r ro also addr(b, 0x9A) "VMMC State Configuration (read format)" type(cfg_state_r);
+    register vmmc_cfg_state_r ro also id1(0x9A) "VMMC State Configuration (read format)" type(cfg_state_r);
+
     // Table 156
-    register vmmc_cfg_voltage addr(b, 0x9B) "VMMC Voltage Configuration" type(cfg_voltage);
+    //register vmmc_cfg_voltage addr(b, 0x9B) "VMMC Voltage Configuration" type(cfg_voltage);
+    register vmmc_cfg_voltage id1(0x9B) "VMMC Voltage Configuration" type(cfg_voltage);
 };
index ecba14f..6f3fddd 100644 (file)
@@ -10,8 +10,9 @@
 #define __TI_I2C_H__
 
 enum i2c_flags {
-    I2C_RD = 0x0,
-    I2C_WR = 0x1,
+    I2C_RD     = 0x1,
+    I2C_WR     = 0x2,
+    I2C_NOSTOP = 0x4,
 };
 
 struct i2c_msg {
index b5a2e6b..f86d600 100644 (file)
@@ -12,6 +12,7 @@
 
 #include <arm_hal.h>
 #include <gic.h>
+#include <ti_twl6030.h>
 
 #include <omap44xx_ctrlmod.h>
 
@@ -86,6 +87,9 @@ void sdmmc1_enable_power(void)
     // Step 3: Program desired SDMMC1_VDDS for MMC I/O in I2C attached power
     // controller TODO? -- assuming 3.0V for now, manual says reset value is
     // 3.0V -SG
+    // controller (3.0V)
+    errval_t err = ti_twl6030_set_vmmc_vsel(3000);
+    assert(err_is_ok(err));
 
     // Step 4: Set VMODE bit according to Step 3 (0x1 == 3.0V)
     printk(LOG_NOTE, "%s: Step 4\n", __FUNCTION__);
@@ -93,6 +97,7 @@ void sdmmc1_enable_power(void)
 
     // Step 5: wait for SDMMC1_VDDS voltage to stabilize TODO
     // might already be stable after reset? -SG
+    ti_twl6030_vmmc_pr();
 
     // Step 6: Disable PWRDNZ mode for MMC1_PBIAS and MMC1 I/O cell
     printk(LOG_NOTE, "%s: Step 6\n", __FUNCTION__);
index 3d49048..49a24a3 100644 (file)
@@ -101,9 +101,10 @@ void ti_i2c_init(int i)
     ti_i2c_con_mst_wrf(dev, 0x1);
 
     // enable interrupts for receive and transmit data ready
-    ti_i2c_irqenable_clr_wr(dev, 0xffff);
-    ti_i2c_irqenable_set_xrdy_ie_wrf(dev, 0x1);
-    ti_i2c_irqenable_set_rrdy_ie_wrf(dev, 0x1);
+    // Not using interrupts for now -SG
+    // ti_i2c_irqenable_clr_wr(dev, 0xffff);
+    // ti_i2c_irqenable_set_xrdy_ie_wrf(dev, 0x1);
+    // ti_i2c_irqenable_set_rrdy_ie_wrf(dev, 0x1);
 
     // enable DMA
     // we're not using DMA for now... -SG
@@ -121,10 +122,22 @@ static inline bool ti_i2c_poll_stat(ti_i2c_t *dev, ti_i2c_irqstatus_t flags,
     uint32_t start_ticks = tsc_read();
     uint32_t ticks;
     int32_t waittime = timeout;
+    printk(LOG_NOTE, "waittime = %"PRIu32"\n", waittime);
 
     while (waittime > 0) {
         ti_i2c_irqstatus_t stat = ti_i2c_stat_rd(dev);
-        if (stat & flags) {
+        printk(LOG_NOTE, "stat = 0x%"PRIx16"\n", stat);
+        if (stat & ti_i2c_irq_flag_aas) {
+            // address recognized as slave interrupt
+            if (stat & ti_i2c_irq_flag_rrdy) {
+                printk(LOG_NOTE, "AAS && RRDY\n");
+                printk(LOG_NOTE, "data = 0x%"PRIx8"\n",
+                        ti_i2c_data_data_rdf(dev));
+                ti_i2c_stat_aas_wrf(dev, 1);
+                ti_i2c_stat_rrdy_wrf(dev, 1);
+            }
+        }
+        else if (stat & flags) {
             if (retflags) {
                 *retflags = stat;
             }
@@ -132,6 +145,7 @@ static inline bool ti_i2c_poll_stat(ti_i2c_t *dev, ti_i2c_irqstatus_t flags,
         }
         ticks = tsc_read();
         waittime -= (ticks - start_ticks);
+        printk(LOG_NOTE, "waittime = %"PRIu32"\n", waittime);
         start_ticks = ticks;
     }
     return false;
@@ -151,6 +165,7 @@ static bool ti_i2c_wait_for_free_bus(ti_i2c_t *dev, int32_t timeout)
 static errval_t
 ti_i2c_read(ti_i2c_t *dev, uint8_t *buf, uint16_t length)
 {
+    printk(LOG_NOTE, "ti_i2c_read\n");
     bool wfb;
     wfb = ti_i2c_wait_for_free_bus(dev, DEFAULT_TIMEOUT);
     if (!wfb) {
@@ -172,7 +187,7 @@ ti_i2c_read(ti_i2c_t *dev, uint8_t *buf, uint16_t length)
     con = ti_i2c_con_stt_insert(con, 1);
     ti_i2c_con_wr(dev, con);
 
-    ti_i2c_irqstatus_t events, retevents;
+    ti_i2c_irqstatus_t events = 0, retevents;
 
     events = ti_i2c_irq_flag_al // arbitration lost
            | ti_i2c_irq_flag_nack // no acknowledgement
@@ -185,6 +200,7 @@ ti_i2c_read(ti_i2c_t *dev, uint8_t *buf, uint16_t length)
     // reading loop
     while (true) {
         // poll for NACK, AL, ARDY, RDR and RRDY
+        printk(LOG_NOTE, "waiting for 0x%"PRIx16"\n", events);
         while(!ti_i2c_poll_stat(dev, events, &retevents, DEFAULT_TIMEOUT)) {
             // poll for receive ready
         }
@@ -248,12 +264,14 @@ ti_i2c_read(ti_i2c_t *dev, uint8_t *buf, uint16_t length)
 static errval_t
 ti_i2c_write(ti_i2c_t *dev, uint8_t *buf, uint16_t length)
 {
+    printk(LOG_NOTE, "ti_i2c_write\n");
     bool wfb;
     wfb = ti_i2c_wait_for_free_bus(dev, DEFAULT_TIMEOUT);
     if (!wfb) {
         printk(LOG_ERR, "wait for bus free timed out\n");
         return SYS_ERR_I2C_WAIT_FOR_BUS;
     }
+    printk(LOG_NOTE, "bus is free, proceeding\n");
 
     // TODO: interrupt-driven?
 
@@ -269,7 +287,7 @@ ti_i2c_write(ti_i2c_t *dev, uint8_t *buf, uint16_t length)
     con = ti_i2c_con_stt_insert(con, 1);
     ti_i2c_con_wr(dev, con);
 
-    ti_i2c_irqstatus_t events, retevents;
+    ti_i2c_irqstatus_t events = 0, retevents;
     events = ti_i2c_irq_flag_al // arbitration lost
            | ti_i2c_irq_flag_nack // no acknowledgement
            | ti_i2c_irq_flag_ardy // register access ready
@@ -282,6 +300,7 @@ ti_i2c_write(ti_i2c_t *dev, uint8_t *buf, uint16_t length)
     // writing loop
     while (true) {
         // poll for NACK, AL, ARDY, XDR and XRDY
+        printk(LOG_NOTE, "waiting for 0x%"PRIx16"\n", events);
         while(!ti_i2c_poll_stat(dev, events, &retevents, DEFAULT_TIMEOUT)) {
             // poll for events
         }
@@ -313,6 +332,7 @@ ti_i2c_write(ti_i2c_t *dev, uint8_t *buf, uint16_t length)
 
             /* get the number of bytes that fit in the FIFO */
             amount = ti_i2c_bufstat_txstat_rdf(dev);
+            printk(LOG_NOTE, "#bytes = %"PRIu16"\n", amount);
         }
         else if (retevents & ti_i2c_irq_flag_xrdy) {
             // transmit data ready interrupt --> can send data
@@ -330,6 +350,7 @@ ti_i2c_write(ti_i2c_t *dev, uint8_t *buf, uint16_t length)
         }
 
         // write the bytes to the fifo
+        printk(LOG_NOTE, "writing %"PRIu16" bytes\n", amount);
         for (int i = 0; i < amount; i++) {
             ti_i2c_data_data_wrf(dev, buf[sofar++]);
         }
@@ -355,6 +376,7 @@ ti_i2c_write(ti_i2c_t *dev, uint8_t *buf, uint16_t length)
  */
 errval_t ti_i2c_transfer(int devid, struct i2c_msg *msgs, size_t msgcount)
 {
+    printk(LOG_NOTE, "ti_i2c_transfer\n");
     if (!i2c_initialized[devid]) {
         return SYS_ERR_I2C_UNINITIALIZED;
     }
index fedd6cb..9671c3c 100644 (file)
@@ -15,6 +15,7 @@
 #include <omap44xx_mmchs.h>
 #include <omap44xx_cm2.h>
 #include <omap44xx_ctrlmod.h>
+#include <ti_twl6030.h>
 
 #include <dev/omap/omap44xx_mmchs_dev.h>
 #include <dev/sdhc_dev.h>
@@ -668,6 +669,11 @@ static void mmchs_pre_configure(void)
 {
     printf("mmchs: pre_configure()\n");
 
+    // need connection to TWL6030 for sdmmc1_enable_power()
+    ti_twl6030_init();
+    // for testing
+    ti_twl6030_vmmc_pr();
+
     // TRM chapter 18: Control module? Is that CM2?
     sdmmc1_enable_power();
 
diff --git a/usr/drivers/omap44xx/mmchs/ti_twl6030.h b/usr/drivers/omap44xx/mmchs/ti_twl6030.h
new file mode 100644 (file)
index 0000000..42b74ed
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2012, ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, CAB F.78, Universitaetstr 6, CH-8092 Zurich.
+ */
+#ifndef __TI_TWL6030_H__
+#define __TI_TWL6030_H__
+
+void ti_twl6030_init(void);
+void ti_twl6030_scan(void);
+void ti_twl6030_vmmc_pr(void);
+errval_t ti_twl6030_set_vmmc_vsel(int millis);
+
+#endif // __TI_TWL6030_H__
diff --git a/usr/drivers/omap44xx/mmchs/twl6030.c b/usr/drivers/omap44xx/mmchs/twl6030.c
new file mode 100644 (file)
index 0000000..ad22856
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2012, ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, CAB F.78, Universitaetstr 6, CH-8092 Zurich.
+ */
+
+#include <kernel.h>
+#include <paging_kernel_arch.h>
+
+#include <arm_hal.h>
+#include <ti_i2c.h>
+#include <ti_twl6030.h>
+
+static uint8_t _ti_twl6030_id1_read_8(void *d, size_t off);
+static void _ti_twl6030_id1_write_8(void *d, size_t off, uint8_t regval);
+
+#define ti_twl6030_id1_read_8(dev, off) _ti_twl6030_id1_read_8(dev, off)
+#define ti_twl6030_id1_write_8(dev, off, regval) _ti_twl6030_id1_write_8(dev, off, regval)
+
+#include <dev/ti_twl6030_dev.h>
+// I2C Host controller id
+#define I2C_HC 0
+
+// I2C slave address for id1 reads and writes is 0x48
+#define ID1_I2C_ADDR 0x48
+static inline uint8_t _ti_twl6030_id1_read_8(void *d, size_t off)
+{
+    struct i2c_msg msg[2];
+
+    uint8_t reg = off & 0xff;
+    uint8_t result = 0;
+
+    errval_t err;
+
+    printk(LOG_NOTE, "id1_read_8(reg=0x%"PRIx8")\n", reg);
+
+    /* set register to read from */
+    msg[0].slave = ID1_I2C_ADDR;
+    msg[0].flags = I2C_WR | I2C_NOSTOP;
+    msg[0].length = 1;
+    msg[0].buf = &reg;
+    /* read data back */
+    msg[1].slave = ID1_I2C_ADDR;
+    msg[1].flags = I2C_RD;
+    msg[1].length = 1;
+    msg[1].buf = &result;
+
+    err = ti_i2c_transfer(I2C_HC, msg, 2);
+
+    if (err_is_fail(err)) {
+        printk(LOG_NOTE, "ti_i2c_transfer: %"PRIuERRV"\n", err);
+        return 0;
+    }
+
+    return result;
+}
+static inline void _ti_twl6030_id1_write_8(void *d, size_t off, uint8_t regval)
+{
+    struct i2c_msg msg;
+    errval_t err;
+
+    uint8_t addr = off & 0xff;
+    uint8_t msgbuf[2];
+
+    msgbuf[0] = addr;
+    msgbuf[1] = regval;
+
+    msg.slave = ID1_I2C_ADDR;
+    msg.flags = I2C_WR;
+    msg.length = 2;
+    msg.buf = msgbuf;
+
+    err = ti_i2c_transfer(I2C_HC, &msg, 1);
+
+    if (err_is_fail(err)) {
+        printk(LOG_ERR, "ti_i2c_transfer failed in mackerel: %"PRIuERRV"\n", err);
+    }
+
+    return;
+}
+
+#define PBS (8*1024)
+static char PRBUF[PBS];
+#define PRBUFL PRBUF, (PBS-1)
+
+static ti_twl6030_t twl;
+
+void ti_twl6030_init(void)
+{
+    printk(LOG_NOTE, "twl init\n");
+    //ti_twl6030_initialize(&twl, 0x0);
+
+    // initialize I2C1 host controller
+    ti_i2c_init(I2C_HC);
+    //printf("read VMMC_CFG_VOLTAGE %"PRIu8"\n", _ti_twl6030_id1_read_8(NULL, 0x9B));
+
+
+    printk(LOG_NOTE, "scanning TWL\n");
+    ti_twl6030_scan();
+    printk(LOG_NOTE, "after scan\n");
+    while (1);
+}
+
+void ti_twl6030_vmmc_pr(void)
+{
+    ti_twl6030_pr(PRBUFL, &twl);
+    printk(LOG_NOTE, "%s\n", PRBUF);
+}
+
+void ti_twl6030_scan(void)
+{
+    int i, base = 0x48;
+    for (i = 0; i < 4; i++) {
+        struct i2c_msg msg;
+        uint8_t tmp;
+        msg.slave = base + i;
+        msg.flags = I2C_RD;
+        msg.length = 1;
+        msg.buf = &tmp;
+
+        errval_t err = ti_i2c_transfer(I2C_HC, &msg, 1);
+        if (err_is_ok(err)) {
+            printk(LOG_NOTE, "found subdev at 0x%x\n", base + i);
+        }
+    }
+    return;
+}
+
+static ti_twl6030_vsel_t millis_to_vsel(int millis)
+{
+    switch (millis) {
+        case 0:
+            return ti_twl6030_v0v0;
+        case 1000:
+            return ti_twl6030_v1v0;
+        case 1100:
+            return ti_twl6030_v1v1;
+        case 1200:
+            return ti_twl6030_v1v2;
+        case 1300:
+            return ti_twl6030_v1v3;
+        case 1400:
+            return ti_twl6030_v1v4;
+        case 1500:
+            return ti_twl6030_v1v5;
+        case 1600:
+            return ti_twl6030_v1v6;
+        case 1700:
+            return ti_twl6030_v1v7;
+        case 1800:
+            return ti_twl6030_v1v8;
+        case 1900:
+            return ti_twl6030_v1v9;
+        case 2000:
+            return ti_twl6030_v2v0;
+        case 2100:
+            return ti_twl6030_v2v1;
+        case 2200:
+            return ti_twl6030_v2v2;
+        case 2300:
+            return ti_twl6030_v2v3;
+        case 2400:
+            return ti_twl6030_v2v4;
+        case 2500:
+            return ti_twl6030_v2v5;
+        case 2600:
+            return ti_twl6030_v2v6;
+        case 2700:
+            return ti_twl6030_v2v7;
+        case 2750:
+            return ti_twl6030_v2v75;
+        case 2800:
+            return ti_twl6030_v2v8;
+        case 2900:
+            return ti_twl6030_v2v9;
+        case 3000:
+            return ti_twl6030_v3v0;
+        case 3100:
+            return ti_twl6030_v3v1;
+        case 3200:
+            return ti_twl6030_v3v2;
+        case 3300:
+            return ti_twl6030_v3v3;
+        default:
+            printk(LOG_WARN, "voltage (%d) not available, returning 0.0V\n", millis);
+            return ti_twl6030_v0v0;
+    }
+}
+
+errval_t ti_twl6030_set_vmmc_vsel(int millis)
+{
+    printk(LOG_NOTE, "ti_twl6030_vmmc_vsel\n");
+
+    ti_twl6030_vsel_t vsel = millis_to_vsel(millis);
+
+    // set regulator to application mode
+    ti_twl6030_vmmc_cfg_grp_grp_app_wrf(&twl, 0x1);
+    // turn on
+    ti_twl6030_cfg_state_w_t st = ti_twl6030_cfg_state_w_default;
+    st = ti_twl6030_cfg_state_w_grp_app_insert(st, 0x1);
+    st = ti_twl6030_cfg_state_w_state_insert(st, ti_twl6030_pwr_on);
+    ti_twl6030_vmmc_cfg_state_r_rawwr(&twl, st);
+
+    ti_twl6030_vmmc_cfg_voltage_vsel_wrf(&twl, vsel);
+
+    return SYS_ERR_OK;
+}