diff --git a/distrib/sets/lists/minix/md.evbarm b/distrib/sets/lists/minix/md.evbarm index 031e291d3..bcd293634 100644 --- a/distrib/sets/lists/minix/md.evbarm +++ b/distrib/sets/lists/minix/md.evbarm @@ -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 diff --git a/drivers/Makefile b/drivers/Makefile index 97f33b9dd..6c9f811ef 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -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 index 000000000..bb95536e8 --- /dev/null +++ b/drivers/tps65950/Makefile @@ -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 diff --git a/drivers/tps65950/README.txt b/drivers/tps65950/README.txt new file mode 100644 index 000000000..e80b1e45b --- /dev/null +++ b/drivers/tps65950/README.txt @@ -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 index 000000000..c3a13f0d2 --- /dev/null +++ b/drivers/tps65950/rtc.c @@ -0,0 +1,181 @@ +#include +#include +#include +#include +#include + +#include + +#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 index 000000000..cb64aeaba --- /dev/null +++ b/drivers/tps65950/rtc.h @@ -0,0 +1,11 @@ +#ifndef __RTC_H +#define __RTC_H + +#include + +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 index 000000000..2f139e073 --- /dev/null +++ b/drivers/tps65950/tps65950.c @@ -0,0 +1,473 @@ +#include +#include +#include +#include +#include +#include + +#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 index 000000000..1508f898a --- /dev/null +++ b/drivers/tps65950/tps65950.h @@ -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 */ diff --git a/etc/system.conf b/etc/system.conf index 74ec801d1..ff5e595de 100644 --- a/etc/system.conf +++ b/etc/system.conf @@ -631,6 +631,11 @@ service tps65217 ipc SYSTEM RS DS PM i2c; }; +service tps65950 +{ + ipc SYSTEM RS DS i2c readclock.drv; +}; + service vbox { system diff --git a/etc/usr/rc b/etc/usr/rc index e3a1fa07f..7b830faef 100644 --- a/etc/usr/rc +++ b/etc/usr/rc @@ -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 diff --git a/include/minix/com.h b/include/minix/com.h index d1e4752de..e8320e02d 100644 --- a/include/minix/com.h +++ b/include/minix/com.h @@ -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 */