tps65950: driver for the TPS65950 PMIC
Change-Id: I6b6163e59233d1f823f03550b949d53e1738a7f4
This commit is contained in:
parent
3a0aa55040
commit
bab2a34e1b
11 changed files with 804 additions and 1 deletions
|
@ -113,5 +113,6 @@
|
|||
./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
|
||||
|
|
|
@ -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"
|
||||
|
|
14
drivers/tps65950/Makefile
Normal file
14
drivers/tps65950/Makefile
Normal file
|
@ -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>
|
36
drivers/tps65950/README.txt
Normal file
36
drivers/tps65950/README.txt
Normal file
|
@ -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
|
181
drivers/tps65950/rtc.c
Normal file
181
drivers/tps65950/rtc.c
Normal file
|
@ -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);
|
||||
}
|
11
drivers/tps65950/rtc.h
Normal file
11
drivers/tps65950/rtc.h
Normal file
|
@ -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 */
|
473
drivers/tps65950/tps65950.c
Normal file
473
drivers/tps65950/tps65950.c
Normal file
|
@ -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;
|
||||
}
|
72
drivers/tps65950/tps65950.h
Normal file
72
drivers/tps65950/tps65950.h
Normal file
|
@ -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 */
|
|
@ -631,6 +631,11 @@ service tps65217
|
|||
ipc SYSTEM RS DS PM i2c;
|
||||
};
|
||||
|
||||
service tps65950
|
||||
{
|
||||
ipc SYSTEM RS DS i2c readclock.drv;
|
||||
};
|
||||
|
||||
service vbox
|
||||
{
|
||||
system
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -1322,6 +1322,10 @@
|
|||
#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 */
|
||||
|
||||
|
@ -1329,6 +1333,7 @@
|
|||
#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 */
|
||||
|
|
Loading…
Reference in a new issue