]> Zhao Yanbai Git Server - minix.git/commitdiff
tps65950: driver for the TPS65950 PMIC 20/720/3
authorThomas Cort <tcort@minix3.org>
Tue, 6 Aug 2013 13:52:46 +0000 (09:52 -0400)
committerGerrit Code Review <gerrit@gerrit>
Fri, 9 Aug 2013 10:41:43 +0000 (12:41 +0200)
Change-Id: I6b6163e59233d1f823f03550b949d53e1738a7f4

distrib/sets/lists/minix/md.evbarm
drivers/Makefile
drivers/tps65950/Makefile [new file with mode: 0644]
drivers/tps65950/README.txt [new file with mode: 0644]
drivers/tps65950/rtc.c [new file with mode: 0644]
drivers/tps65950/rtc.h [new file with mode: 0644]
drivers/tps65950/tps65950.c [new file with mode: 0644]
drivers/tps65950/tps65950.h [new file with mode: 0644]
etc/system.conf
etc/usr/rc
include/minix/com.h

index 031e291d3d94f07974496b1d44aeec9146dcfcee..bcd293634a248e31b4b6b3c121b6436a8eb70cf9 100644 (file)
 ./usr/sbin/random                      minix-sys
 ./usr/sbin/tda19988                    minix-sys
 ./usr/sbin/tps65217                    minix-sys
+./usr/sbin/tps65950                    minix-sys
 ./usr/tests/minix-posix/mod            minix-sys
 ./usr/tests/minix-posix/test63         minix-sys
index 97f33b9dd63f5ae29316086f83d4b2bc57d6d3d8..6c9f811ef7c863e77a7d7d1417a98336ce2922ea 100644 (file)
@@ -24,7 +24,7 @@ SUBDIR= ahci amddev atl2 at_wini audio dec21140A dp8390 dpeth \
 
 .if ${MACHINE_ARCH} == "earm"
 SUBDIR= cat24c256 fb gpio i2c mmc lan8710a log readclock \
-       tda19988 tps65217 tty random
+       tda19988 tps65217 tps65950 tty random
 .endif
 
 .endif # ${MKIMAGEONLY} != "yes"
diff --git a/drivers/tps65950/Makefile b/drivers/tps65950/Makefile
new file mode 100644 (file)
index 0000000..bb95536
--- /dev/null
@@ -0,0 +1,14 @@
+# Makefile for the tps65950 PMIC found on the BeagleBoard-xM.
+PROG=   tps65950
+SRCS=   tps65950.c tps65950.h rtc.c rtc.h
+
+DPADD+= ${LIBI2CDRIVER} ${LIBSYS} ${LIBTIMERS}
+LDADD+= -li2cdriver -lsys -ltimers
+
+MAN=
+
+BINDIR?= /usr/sbin
+
+CPPFLAGS+=      -I${NETBSDSRCDIR}
+
+.include <minix.service.mk>
diff --git a/drivers/tps65950/README.txt b/drivers/tps65950/README.txt
new file mode 100644 (file)
index 0000000..e80b1e4
--- /dev/null
@@ -0,0 +1,36 @@
+TPS65950 Driver (Power Management IC)
+=====================================
+
+Overview
+--------
+
+This driver is for the power management chip commonly found on the
+BeagleBoard-xM.
+
+Limitations
+-----------
+
+The TPS65950 has a pin labelled MSECURE which provides a form of write
+protection. Depending on the pin's state (high or low), writing to certain
+registers is disabled or enabled. The pin is driven by a GPIO on the SoC. 
+There isn't a good way to access that pin on the SoC yet, so the PMIC
+is always in the default insecure mode. It isn't really insecure as this
+driver is the only one that the i2c bus driver will allow to access the
+PMIC.
+
+This is a huge chip. It has two I2C controllers, one with 4 slave addresses
+hosting 256 registers per address. The TRM is over 900 pages. Given the
+limited usefulness of some peripherals on the chip, not everything is
+implemented.
+
+Testing the Code
+----------------
+
+Starting up an instance:
+
+/bin/service up /usr/sbin/tps65950 -label tps65950.1.48 \
+       -args 'bus=1 address=0x48'
+
+Killing an instance:
+
+/bin/service down tps65950.1.48
diff --git a/drivers/tps65950/rtc.c b/drivers/tps65950/rtc.c
new file mode 100644 (file)
index 0000000..c3a13f0
--- /dev/null
@@ -0,0 +1,181 @@
+#include <minix/ds.h>
+#include <minix/drivers.h>
+#include <minix/i2c.h>
+#include <minix/i2cdriver.h>
+#include <minix/log.h>
+
+#include <time.h>
+
+#include "tps65950.h"
+#include "rtc.h"
+
+/* logging - use with log_warn(), log_info(), log_debug(), log_trace(), etc */
+static struct log log = {
+       .name = "tps65950.rtc",
+       .log_level = LEVEL_INFO,
+       .log_func = default_log
+};
+
+static int bcd_to_dec(int n);
+static int dec_to_bcd(int n);
+
+int
+rtc_init(void)
+{
+       int r;
+       uint8_t val;
+       struct tm t;
+
+       r = reg_set(ID4, RTC_CTRL_REG, (1 << STOP_RTC_BIT));
+       if (r != OK) {
+               log_warn(&log, "Failed to start RTC\n");
+               return -1;
+       }
+
+       r = reg_read(ID4, RTC_STATUS_REG, &val);
+       if (r != OK) {
+               log_warn(&log, "Failed to read RTC_STATUS_REG\n");
+               return -1;
+       }
+
+       if ((val & (1 << RUN_BIT)) != (1 << RUN_BIT)) {
+               log_warn(&log, "RTC did not start. Bad MSECURE?\n");
+               return -1;
+       }
+
+       log_debug(&log, "RTC Started\n");
+
+       return OK;
+}
+
+int
+rtc_get_time(struct tm *t, int flags)
+{
+       int r;
+       uint8_t val;
+
+       memset(t, '\0', sizeof(struct tm));
+
+       /* Write GET_TIME_BIT to RTC_CTRL_REG to latch the RTC values into
+        * the RTC registers. This is required before each read.
+        */
+       r = reg_set(ID4, RTC_CTRL_REG, (1 << GET_TIME_BIT));
+       if (r != OK) {
+               return -1;
+       }
+
+       /* Read and Convert BCD to binary (default RTC mode). */
+
+       /* Seconds - 0 to 59 */
+       r = reg_read(ID4, SECONDS_REG, &val);
+       if (r != OK) {
+               return -1;
+       }
+       t->tm_sec = bcd_to_dec(val & 0x7f);
+
+       /* Minutes - 0 to 59 */
+       r = reg_read(ID4, MINUTES_REG, &val);
+       if (r != OK) {
+               return -1;
+       }
+       t->tm_min = bcd_to_dec(val & 0x7f);
+
+       /* Hours - 0 to 23 */
+       r = reg_read(ID4, HOURS_REG, &val);
+       if (r != OK) {
+               return -1;
+       }
+       t->tm_hour = bcd_to_dec(val & 0x3f);
+
+       /* Days - 1 to 31 */
+       r = reg_read(ID4, DAYS_REG, &val);
+       if (r != OK) {
+               return -1;
+       }
+       t->tm_mday = bcd_to_dec(val & 0x3f);
+
+       /* Months - Jan=1 to Dec=12 */
+       r = reg_read(ID4, MONTHS_REG, &val);
+       if (r != OK) {
+               return -1;
+       }
+       t->tm_mon = bcd_to_dec(val & 0x1f) - 1;
+
+       /* Years - last 2 digits of year */
+       r = reg_read(ID4, YEARS_REG, &val);
+       if (r != OK) {
+               return -1;
+       }
+       t->tm_year = bcd_to_dec(val & 0x1f) + 100;
+
+       if (t->tm_year == 100) {
+               /* Cold start - no date/time set - default to 2013-01-01 */
+               t->tm_sec = 0;
+               t->tm_min = 0;
+               t->tm_hour = 0;
+               t->tm_mday = 1;
+               t->tm_mon = 0;
+               t->tm_year = 113;
+
+               rtc_set_time(t, RTCDEV_NOFLAGS);
+       }
+
+       return OK;
+}
+
+int
+rtc_set_time(struct tm *t, int flags)
+{
+       int r;
+
+       /* Write the date/time to the RTC registers. */
+       r = reg_write(ID4, SECONDS_REG, (dec_to_bcd(t->tm_sec) & 0x7f));
+       if (r != OK) {
+               return -1;
+       }
+
+       r = reg_write(ID4, MINUTES_REG, (dec_to_bcd(t->tm_min) & 0x7f));
+       if (r != OK) {
+               return -1;
+       }
+
+       r = reg_write(ID4, HOURS_REG, (dec_to_bcd(t->tm_hour) & 0x3f));
+       if (r != OK) {
+               return -1;
+       }
+
+       r = reg_write(ID4, DAYS_REG, (dec_to_bcd(t->tm_mday) & 0x3f));
+       if (r != OK) {
+               return -1;
+       }
+
+       r = reg_write(ID4, MONTHS_REG, (dec_to_bcd(t->tm_mon + 1) & 0x1f));
+       if (r != OK) {
+               return -1;
+       }
+
+       r = reg_write(ID4, YEARS_REG, (dec_to_bcd(t->tm_year % 100) & 0xff));
+       if (r != OK) {
+               return -1;
+       }
+
+       return OK;
+}
+
+int
+rtc_exit(void)
+{
+       return OK;
+}
+
+static int
+bcd_to_dec(int n)
+{
+       return ((n >> 4) & 0x0F) * 10 + (n & 0x0F);
+}
+
+static int
+dec_to_bcd(int n)
+{
+       return ((n / 10) << 4) | (n % 10);
+}
diff --git a/drivers/tps65950/rtc.h b/drivers/tps65950/rtc.h
new file mode 100644 (file)
index 0000000..cb64aea
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef __RTC_H
+#define __RTC_H
+
+#include <time.h>
+
+int rtc_init(void);
+int rtc_get_time(struct tm *t, int flags); /* read the hardware clock into t */
+int rtc_set_time(struct tm *t, int flags); /* set the hardware clock to t */
+int rtc_exit(void);
+
+#endif /* __RTC_H */
diff --git a/drivers/tps65950/tps65950.c b/drivers/tps65950/tps65950.c
new file mode 100644 (file)
index 0000000..2f139e0
--- /dev/null
@@ -0,0 +1,473 @@
+#include <minix/ds.h>
+#include <minix/drivers.h>
+#include <minix/i2c.h>
+#include <minix/i2cdriver.h>
+#include <minix/log.h>
+#include <minix/safecopies.h>
+
+#include "tps65950.h"
+#include "rtc.h"
+
+/* logging - use with log_warn(), log_info(), log_debug(), log_trace(), etc */
+static struct log log = {
+       .name = "tps65950",
+       .log_level = LEVEL_INFO,
+       .log_func = default_log
+};
+
+/* TPS65950 doesn't support configuring the addresses, so there is only 1
+ * configuration possible. The chip does have multiple addresses (0x48,
+ * 0x49, 0x4a, 0x4b), but because they're all fixed, we only have the
+ * user pass the base address as a sanity check.
+ */
+static i2c_addr_t valid_addrs[2] = {
+       0x48, 0x00
+};
+
+/* the bus that this device is on (counting starting at 1) */
+static uint32_t bus;
+
+/* endpoint for the driver for the bus itself. */
+static endpoint_t bus_endpoint;
+
+/* slave addresses of the device */
+#define NADDRESSES 4
+static i2c_addr_t addresses[NADDRESSES] = {
+       0x48, 0x49, 0x4a, 0x4b
+};
+
+/* local functions */
+static int check_revision(void);
+
+/* SEF related functions */
+static void sef_local_startup(void);
+static int sef_cb_lu_state_save(int);
+static int lu_state_restore(void);
+static int sef_cb_init(int type, sef_init_info_t * info);
+
+/* functions for transfering struct tm to/from this driver and calling proc. */
+static int fetch_t(endpoint_t ep, cp_grant_id_t gid, struct tm *t);
+static int store_t(endpoint_t ep, cp_grant_id_t gid, struct tm *t);
+
+int
+reg_read(uint8_t id, uint8_t reg, uint8_t * val)
+{
+       int r;
+       minix_i2c_ioctl_exec_t ioctl_exec;
+
+       if (id < 0 || id >= NADDRESSES) {
+               log_warn(&log, "id parameter out of range.\n");
+               return EINVAL;
+       }
+
+       if (val == NULL) {
+               log_warn(&log, "Read called with NULL pointer\n");
+               return EINVAL;
+       }
+
+       memset(&ioctl_exec, '\0', sizeof(minix_i2c_ioctl_exec_t));
+
+       /* Read from chip */
+       ioctl_exec.iie_op = I2C_OP_READ_WITH_STOP;
+       ioctl_exec.iie_addr = addresses[id];
+
+       /* write the register address */
+       ioctl_exec.iie_cmd[0] = reg;
+       ioctl_exec.iie_cmdlen = 1;
+
+       /* read 1 byte */
+       ioctl_exec.iie_buflen = 1;
+
+       r = i2cdriver_exec(bus_endpoint, &ioctl_exec);
+       if (r != OK) {
+               log_warn(&log, "reg_read() failed (r=%d)\n", r);
+               return -1;
+       }
+
+       *val = ioctl_exec.iie_buf[0];
+
+       log_trace(&log, "Read 0x%x from reg 0x%x", *val, reg);
+
+       return OK;
+}
+
+int
+reg_write(uint8_t id, uint8_t reg, uint8_t val)
+{
+       int r;
+       minix_i2c_ioctl_exec_t ioctl_exec;
+
+       if (id < 0 || id >= NADDRESSES) {
+               log_warn(&log, "id parameter out of range.\n");
+               return EINVAL;
+       }
+
+       memset(&ioctl_exec, '\0', sizeof(minix_i2c_ioctl_exec_t));
+
+       /* Write to chip */
+       ioctl_exec.iie_op = I2C_OP_WRITE_WITH_STOP;
+       ioctl_exec.iie_addr = addresses[id];
+
+       /* write the register address and value */
+       ioctl_exec.iie_buf[0] = reg;
+       ioctl_exec.iie_buf[1] = val;
+       ioctl_exec.iie_buflen = 2;
+
+       r = i2cdriver_exec(bus_endpoint, &ioctl_exec);
+       if (r != OK) {
+               log_warn(&log, "reg_write() failed (r=%d)\n", r);
+               return -1;
+       }
+
+       log_trace(&log, "Successfully wrote 0x%x to reg 0x%x\n", val, reg);
+
+       return OK;
+}
+
+int
+reg_set(uint8_t id, uint8_t reg, uint8_t mask)
+{
+       int r;
+       uint8_t val;
+
+       r = reg_read(id, reg, &val);
+       if (r != OK) {
+               return -1;
+       }
+
+       val |= mask;
+
+       r = reg_write(id, reg, val);
+       if (r != OK) {
+               return -1;
+       }
+
+       return OK;
+}
+
+int
+reg_clear(uint8_t id, uint8_t reg, uint8_t mask)
+{
+       int r;
+       uint8_t val;
+
+       r = reg_read(id, reg, &val);
+       if (r != OK) {
+               return -1;
+       }
+
+       val &= ~mask;
+
+       r = reg_write(id, reg, val);
+       if (r != OK) {
+               return -1;
+       }
+
+       return OK;
+}
+
+static int
+fetch_t(endpoint_t ep, cp_grant_id_t gid, struct tm *t)
+{
+       int r;
+
+       r = sys_safecopyfrom(ep, gid, (vir_bytes) 0, (vir_bytes) t,
+           sizeof(struct tm));
+       if (r != OK) {
+               log_warn(&log, "sys_safecopyfrom() failed (r=%d)\n", r);
+               return r;
+       }
+
+       return OK;
+}
+
+static int
+store_t(endpoint_t ep, cp_grant_id_t gid, struct tm *t)
+{
+       int r;
+
+       r = sys_safecopyto(ep, gid, (vir_bytes) 0, (vir_bytes) t,
+           sizeof(struct tm));
+       if (r != OK) {
+               log_warn(&log, "sys_safecopyto() failed (r=%d)\n", r);
+               return r;
+       }
+
+       return OK;
+}
+
+static int
+check_revision(void)
+{
+       int r;
+       uint32_t idcode;
+       uint8_t idcode_7_0, idcode_15_8, idcode_23_16, idcode_31_24;
+
+       /* need to write a special code to unlock read protect on IDCODE */
+       r = reg_write(ID2, UNLOCK_TEST_REG, UNLOCK_TEST_CODE);
+       if (r != OK) {
+               log_warn(&log, "Failed to write unlock code to UNLOCK_TEST\n");
+               return -1;
+       }
+
+       /*
+        * read each part of the IDCODE
+        */
+       r = reg_read(ID2, IDCODE_7_0_REG, &idcode_7_0);
+       if (r != OK) {
+               log_warn(&log, "Failed to read IDCODE part 1\n");
+       }
+
+       r = reg_read(ID2, IDCODE_15_8_REG, &idcode_15_8);
+       if (r != OK) {
+               log_warn(&log, "Failed to read IDCODE part 2\n");
+       }
+
+       r = reg_read(ID2, IDCODE_23_16_REG, &idcode_23_16);
+       if (r != OK) {
+               log_warn(&log, "Failed to read IDCODE part 3\n");
+       }
+
+       r = reg_read(ID2, IDCODE_31_24_REG, &idcode_31_24);
+       if (r != OK) {
+               log_warn(&log, "Failed to read IDCODE part 4\n");
+       }
+
+       /* combine the parts to get the full IDCODE */
+       idcode =
+           ((idcode_31_24 << 24) | (idcode_23_16 << 16) | (idcode_15_8 << 8) |
+           (idcode_7_0 << 0));
+
+       log_debug(&log, "IDCODE = 0x%x\n", idcode);
+       switch (idcode) {
+       case IDCODE_REV_1_0:
+               log_debug(&log, "TPS65950 rev 1.0\n");
+               break;
+       case IDCODE_REV_1_1:
+               log_debug(&log, "TPS65950 rev 1.1\n");
+               break;
+       case IDCODE_REV_1_2:
+               log_debug(&log, "TPS65950 rev 1.2\n");
+               break;
+       default:
+               log_warn(&log, "Unexpected IDCODE: 0x%x\n", idcode);
+               return -1;
+       }
+
+       return OK;
+}
+
+static int
+sef_cb_lu_state_save(int UNUSED(state))
+{
+       /* The addresses are fixed/non-configurable so bus is the only state */
+       ds_publish_u32("bus", bus, DSF_OVERWRITE);
+       return OK;
+}
+
+static int
+lu_state_restore(void)
+{
+       /* Restore the state. */
+       u32_t value;
+
+       ds_retrieve_u32("bus", &value);
+       ds_delete_u32("bus");
+       bus = (int) value;
+
+       return OK;
+}
+
+static int
+sef_cb_init(int type, sef_init_info_t * UNUSED(info))
+{
+       int r, i;
+
+       if (type == SEF_INIT_LU) {
+               /* Restore the state. */
+               lu_state_restore();
+       }
+
+       /* look-up the endpoint for the bus driver */
+       bus_endpoint = i2cdriver_bus_endpoint(bus);
+       if (bus_endpoint == 0) {
+               log_warn(&log, "Couldn't find bus driver.\n");
+               return EXIT_FAILURE;
+       }
+
+       for (i = 0; i < NADDRESSES; i++) {
+
+               /* claim the device */
+               r = i2cdriver_reserve_device(bus_endpoint, addresses[i]);
+               if (r != OK) {
+                       log_warn(&log, "Couldn't reserve device 0x%x (r=%d)\n",
+                           addresses[i], r);
+                       return EXIT_FAILURE;
+               }
+       }
+
+       /* check that the chip / rev is reasonable */
+       r = check_revision();
+       if (r != OK) {
+               /* prevent user from using the driver with a different chip */
+               log_warn(&log, "Bad IDCODE\n");
+               return EXIT_FAILURE;
+       }
+
+       r = rtc_init();
+       if (r != OK) {
+               log_warn(&log, "RTC Start-up Failed\n");
+               return EXIT_FAILURE;
+       }
+
+       if (type != SEF_INIT_LU) {
+
+               /* sign up for updates about the i2c bus going down/up */
+               r = i2cdriver_subscribe_bus_updates(bus);
+               if (r != OK) {
+                       log_warn(&log, "Couldn't subscribe to bus updates\n");
+                       return EXIT_FAILURE;
+               }
+
+               i2cdriver_announce(bus);
+               log_debug(&log, "announced\n");
+       }
+
+       return OK;
+}
+
+static void
+sef_local_startup(void)
+{
+       /*
+        * Register init callbacks. Use the same function for all event types
+        */
+       sef_setcb_init_fresh(sef_cb_init);
+       sef_setcb_init_lu(sef_cb_init);
+       sef_setcb_init_restart(sef_cb_init);
+
+       /*
+        * Register live update callbacks.
+        */
+       /* Agree to update immediately when LU is requested in a valid state. */
+       sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready);
+       /* Support live update starting from any standard state. */
+       sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_standard);
+       /* Register a custom routine to save the state. */
+       sef_setcb_lu_state_save(sef_cb_lu_state_save);
+
+       /* Let SEF perform startup. */
+       sef_startup();
+}
+
+int
+main(int argc, char *argv[])
+{
+       int r, i;
+       struct tm t;
+       endpoint_t user, caller;
+       message m;
+       int ipc_status, reply_status;
+
+       env_setargs(argc, argv);
+
+       r = i2cdriver_env_parse(&bus, &addresses[0], valid_addrs);
+       if (r < 0) {
+               log_warn(&log, "Expecting -args 'bus=X address=0xYY'\n");
+               log_warn(&log, "Example -args 'bus=1 address=0x48'\n");
+               return EXIT_FAILURE;
+       } else if (r > 0) {
+               log_warn(&log,
+                   "Invalid slave address for device, expecting 0x48\n");
+               return EXIT_FAILURE;
+       }
+
+       sef_local_startup();
+
+       while (TRUE) {
+
+               /* Receive Message */
+               r = sef_receive_status(ANY, &m, &ipc_status);
+               if (r != OK) {
+                       log_warn(&log, "sef_receive_status() failed\n");
+                       continue;
+               }
+
+               if (is_ipc_notify(ipc_status)) {
+
+                       if (m.m_source == DS_PROC_NR) {
+                               for (i = 0; i < NADDRESSES; i++) {
+                                       /* changed state, update endpoint */
+                                       i2cdriver_handle_bus_update
+                                           (&bus_endpoint, bus, addresses[i]);
+                               }
+                       }
+
+                       /* Do not reply to notifications. */
+                       continue;
+               }
+
+               caller = m.m_source;
+
+               log_debug(&log, "Got message 0x%x from 0x%x\n", m.m_type,
+                   caller);
+
+               switch (m.m_type) {
+               case RTCDEV_GET_TIME_G:
+                       /* Any user can read the time */
+                       reply_status = rtc_get_time(&t, m.RTCDEV_FLAGS);
+                       if (reply_status != OK) {
+                               break;
+                       }
+
+                       /* write results back to calling process */
+                       reply_status =
+                           store_t(caller, (cp_grant_id_t) m.RTCDEV_GRANT,
+                           &t);
+                       break;
+
+               case RTCDEV_SET_TIME_G:
+                       /* Only super user is allowed to set the time */
+                       if (getnuid(caller) == SUPER_USER) {
+                               /* read time from calling process */
+                               reply_status =
+                                   fetch_t(caller,
+                                   (cp_grant_id_t) m.RTCDEV_GRANT, &t);
+                               if (reply_status != OK) {
+                                       break;
+                               }
+
+                               reply_status =
+                                   rtc_set_time(&t, m.RTCDEV_FLAGS);
+                       } else {
+                               reply_status = EPERM;
+                       }
+                       break;
+
+               case RTCDEV_PWR_OFF:
+                       reply_status = ENOSYS;
+                       break;
+
+               default:
+                       /* Unrecognized call */
+                       reply_status = EINVAL;
+                       break;
+               }
+
+               /* Send Reply */
+               m.m_type = RTCDEV_REPLY;
+               m.RTCDEV_STATUS = reply_status;
+
+               log_debug(&log, "Sending Reply");
+
+               r = sendnb(caller, &m);
+               if (r != OK) {
+                       log_warn(&log, "sendnb() failed\n");
+                       continue;
+               }
+       }
+
+       rtc_exit();
+
+       return 0;
+}
diff --git a/drivers/tps65950/tps65950.h b/drivers/tps65950/tps65950.h
new file mode 100644 (file)
index 0000000..1508f89
--- /dev/null
@@ -0,0 +1,72 @@
+#ifndef __TPS65950_H
+#define __TPS65950_H
+
+/*
+ * The chip is so huge it's split into multiple sub-modules, each with 256
+ * byte register maps on it's own slave addresses. Use ID1, ID2, ... to
+ * specify which sub-module to communicate with. Each slave address can have
+ * registers for one or more peripherals.
+ */
+
+/* Slave Address 0x48 (USB) */
+#define ID1 0
+
+/* Slave Address 0x49 (AUDIO_VOICE/GPIO/INTBR/PIH/TEST) */
+#define ID2 1
+
+/* Interface Bit Registers (INTBR) */
+
+/* Chip Identifier
+ *
+ * When combined to uint32_t, these are the subfields:
+ *   [31:28] - Silicon version number
+ *   [27:12] - Part code number
+ *   [11: 1] - Manufacturer code
+ *   [    0] - Least-significant bit (LSB)
+ */
+#define IDCODE_7_0_REG 0x85
+#define IDCODE_15_8_REG 0x86
+#define IDCODE_23_16_REG 0x87
+#define IDCODE_31_24_REG 0x88
+
+#define IDCODE_REV_1_0 0x00009002f
+#define IDCODE_REV_1_1 0x01009002f
+#define IDCODE_REV_1_2 0x03009002f
+
+/* Write 0x49 to UNLOCK_TEST_REG to unlock reading of IDCODE and DIEID */
+#define UNLOCK_TEST_REG 0x97
+#define UNLOCK_TEST_CODE 0x49
+
+
+/* Slave Address 0x4a (BCI/MADC/MAIN_CHARGE/PWM01/LED/PWMAB/KEYPAD) */
+#define ID3 2
+
+
+/* Slave Address 0x4b (BACKUP_REG/INT/PM_MASTER/SECURED_REG) */
+#define ID4 3
+
+/*
+ * Real Time Clock
+ */
+
+#define SECONDS_REG 0x1c
+#define MINUTES_REG 0x1d
+#define HOURS_REG 0x1e
+#define DAYS_REG 0x1f
+#define MONTHS_REG 0x20
+#define YEARS_REG 0x21
+
+#define RTC_CTRL_REG 0x00000029
+#define GET_TIME_BIT 6
+#define STOP_RTC_BIT 0
+
+#define RTC_STATUS_REG 0x0000002A
+#define RUN_BIT 1
+
+
+int reg_read(uint8_t id, uint8_t reg, uint8_t * val);
+int reg_write(uint8_t id, uint8_t reg, uint8_t val);
+int reg_set(uint8_t id, uint8_t reg, uint8_t mask);
+int reg_clear(uint8_t id, uint8_t reg, uint8_t mask);
+
+#endif /* __TPS65950_H */
index 74ec801d1b43faf07515d7c6630034816d5d8684..ff5e595de38a20cfde68cf2106cc2354cec271ae 100644 (file)
@@ -631,6 +631,11 @@ service tps65217
        ipc SYSTEM RS DS PM i2c;
 };
 
+service tps65950
+{
+       ipc SYSTEM RS DS i2c readclock.drv;
+};
+
 service vbox
 {
        system
index e3a1fa07fd4fa6c289b3c3a873041e4abd7fc360..7b830faefeecc5c18d9a0f1d9189580cfcba12a8 100644 (file)
@@ -228,6 +228,11 @@ start)
                UNKNOWN)
                        echo "Unable to detect board -- assuming BeagleBoard-xM"
                        echo -n "Starting i2c device drivers: "
+
+                       # Start TPS65950 driver for power management.
+                       up tps65950 -label tps65950.1.48 \
+                               -args 'bus=1 address=0x48'
+
                        ;;
        esac
 
index d1e4752de2d5925a41223bbbdf4a9dc784e8d6e6..e8320e02d154b58a8150b0238c933f0b2cb504b2 100644 (file)
 #define RTCDEV_SET_TIME        (RTCDEV_RQ_BASE + 1)    /* set time in hw clock */
 #define RTCDEV_PWR_OFF (RTCDEV_RQ_BASE + 2)    /* set time to cut the power */
 
+/* Same as GET/SET above but using grants */
+#define RTCDEV_GET_TIME_G (RTCDEV_RQ_BASE + 3) /* get time from hw clock */
+#define RTCDEV_SET_TIME_G (RTCDEV_RQ_BASE + 4) /* set time in hw clock */
+
 /* Message types for real time clock responses. */
 #define RTCDEV_REPLY   (RTCDEV_RS_BASE + 0)    /* general reply code */
 
 #define RTCDEV_TM      m2_p1   /* pointer to struct tm */
 #define RTCDEV_FLAGS   m2_s1   /* clock flags flags */
 #define RTCDEV_STATUS  m2_i2   /* OK or error code */
+#define RTCDEV_GRANT   m2_p1   /* grant containing struct tm */
 
 /* Bits in 'RTCDEV_FLAGS' field of real time clock requests. */
 #define RTCDEV_NOFLAGS 0x00    /* no flags are set */