mirror of
https://github.com/followmsi/android_kernel_google_msm.git
synced 2024-11-06 23:17:41 +00:00
rtc: Add MSM RTC driver
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
This commit is contained in:
parent
6b6a72adc0
commit
3668c403db
5 changed files with 877 additions and 1 deletions
|
@ -728,6 +728,30 @@ config RTC_DRV_NUC900
|
|||
|
||||
comment "on-CPU RTC drivers"
|
||||
|
||||
config RTC_DRV_MSM
|
||||
tristate "RTC on Qualcomm Chipsets"
|
||||
depends on ARCH_MSM
|
||||
default y
|
||||
help
|
||||
RTC driver for Qualcomm chipsets
|
||||
|
||||
|
||||
config RTC_SECURE_TIME_SUPPORT
|
||||
bool "Support for secure time on Qualcomm Chipsets"
|
||||
depends on RTC_DRV_MSM = y
|
||||
default y
|
||||
help
|
||||
Say yes here to have additional handle for reading secure time
|
||||
maintained by ARM9.
|
||||
|
||||
config RTC_ASYNC_MODEM_SUPPORT
|
||||
bool "Support for time update on async modem boot"
|
||||
depends on RTC_DRV_MSM && (ARCH_MSM8X60 || ARCH_QSD8X50)
|
||||
default n
|
||||
help
|
||||
Say yes here to have the system time updated if there is
|
||||
an asynchronous MODEM boot.
|
||||
|
||||
config RTC_DRV_DAVINCI
|
||||
tristate "TI DaVinci RTC"
|
||||
depends on ARCH_DAVINCI_DM365
|
||||
|
|
|
@ -67,6 +67,7 @@ obj-$(CONFIG_RTC_DRV_MAX8925) += rtc-max8925.o
|
|||
obj-$(CONFIG_RTC_DRV_MAX8998) += rtc-max8998.o
|
||||
obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
|
||||
obj-$(CONFIG_RTC_DRV_MC13XXX) += rtc-mc13xxx.o
|
||||
obj-$(CONFIG_RTC_DRV_MSM) += rtc-msm.o
|
||||
obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o
|
||||
obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o
|
||||
obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
int rtc_hctosys_ret = -ENODEV;
|
||||
|
||||
static int __init rtc_hctosys(void)
|
||||
int rtc_hctosys(void)
|
||||
{
|
||||
int err = -ENODEV;
|
||||
struct rtc_time tm;
|
||||
|
|
819
drivers/rtc/rtc-msm.c
Normal file
819
drivers/rtc/rtc-msm.c
Normal file
|
@ -0,0 +1,819 @@
|
|||
/*
|
||||
* Copyright (C) 2008 Google, Inc.
|
||||
* Copyright (c) 2009-2011 Code Aurora Forum. All rights reserved.
|
||||
* Author: San Mehat <san@google.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/android_alarm.h>
|
||||
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/rtc-msm.h>
|
||||
#include <linux/msm_rpcrouter.h>
|
||||
#include <mach/msm_rpcrouter.h>
|
||||
|
||||
#define APP_TIMEREMOTE_PDEV_NAME "rs00000000"
|
||||
|
||||
#define TIMEREMOTE_PROCEEDURE_SET_JULIAN 6
|
||||
#define TIMEREMOTE_PROCEEDURE_GET_JULIAN 7
|
||||
#ifdef CONFIG_RTC_SECURE_TIME_SUPPORT
|
||||
#define TIMEREMOTE_PROCEEDURE_GET_SECURE_JULIAN 11
|
||||
#define TIMEREMOTE_PROCEEDURE_SET_SECURE_JULIAN 16
|
||||
#endif
|
||||
#define TIMEREMOTE_PROG_NUMBER 0x30000048
|
||||
#define TIMEREMOTE_PROG_VER_1 0x00010001
|
||||
#define TIMEREMOTE_PROG_VER_2 0x00040001
|
||||
|
||||
#define RTC_REQUEST_CB_PROC 0x17
|
||||
#define RTC_CLIENT_INIT_PROC 0x12
|
||||
#define RTC_EVENT_CB_PROC 0x1
|
||||
#define RTC_CB_ID 0x1
|
||||
|
||||
/* Client request errors */
|
||||
enum rtc_rpc_err {
|
||||
ERR_NONE,
|
||||
ERR_CLIENT_ID_PTR, /* Invalid client ID pointer */
|
||||
ERR_CLIENT_TYPE, /* Invalid client type */
|
||||
ERR_CLIENT_ID, /* Invalid client ID */
|
||||
ERR_TASK_NOT_READY, /* task is not ready for clients */
|
||||
ERR_INVALID_PROCESSOR, /* Invalid processor id */
|
||||
ERR_UNSUPPORTED, /* Unsupported request */
|
||||
ERR_GENERAL, /* Any General Error */
|
||||
ERR_RPC, /* Any ONCRPC Error */
|
||||
ERR_ALREADY_REG, /* Client already registered */
|
||||
ERR_MAX
|
||||
};
|
||||
|
||||
enum processor_type {
|
||||
CLIENT_PROCESSOR_NONE = 0,
|
||||
CLIENT_PROCESSOR_MODEM,
|
||||
CLIENT_PROCESSOR_APP1,
|
||||
CLIENT_PROCESSOR_APP2,
|
||||
CLIENT_PROCESSOR_MAX
|
||||
};
|
||||
|
||||
/* Client types */
|
||||
enum client_type {
|
||||
CLIENT_TYPE_GEN1 = 0,
|
||||
CLIENT_FLOATING1,
|
||||
CLIENT_FLOATING2,
|
||||
CLIENT_TYPE_INTERNAL,
|
||||
CLIENT_TYPE_GENOFF_UPDATE,
|
||||
CLIENT_TYPE_MAX
|
||||
};
|
||||
|
||||
/* Event types */
|
||||
enum event_type {
|
||||
EVENT_TOD_CHANGE = 0,
|
||||
EVENT_GENOFF_CHANGE,
|
||||
EVENT_MAX
|
||||
};
|
||||
|
||||
struct tod_update_info {
|
||||
uint32_t tick;
|
||||
uint64_t stamp;
|
||||
uint32_t freq;
|
||||
};
|
||||
|
||||
enum time_bases_info {
|
||||
TIME_RTC = 0,
|
||||
TIME_TOD,
|
||||
TIME_USER,
|
||||
TIME_SECURE,
|
||||
TIME_INVALID
|
||||
};
|
||||
|
||||
struct genoff_update_info {
|
||||
enum time_bases_info time_base;
|
||||
uint64_t offset;
|
||||
};
|
||||
|
||||
union cb_info {
|
||||
struct tod_update_info tod_update;
|
||||
struct genoff_update_info genoff_update;
|
||||
};
|
||||
|
||||
struct rtc_cb_recv {
|
||||
uint32_t client_cb_id;
|
||||
enum event_type event;
|
||||
uint32_t cb_info_ptr;
|
||||
union cb_info cb_info_data;
|
||||
};
|
||||
|
||||
struct msm_rtc {
|
||||
int proc;
|
||||
struct msm_rpc_client *rpc_client;
|
||||
u8 client_id;
|
||||
struct rtc_device *rtc;
|
||||
#ifdef CONFIG_RTC_SECURE_TIME_SUPPORT
|
||||
struct rtc_device *rtcsecure;
|
||||
#endif
|
||||
unsigned long rtcalarm_time;
|
||||
};
|
||||
|
||||
struct rpc_time_julian {
|
||||
uint32_t year;
|
||||
uint32_t month;
|
||||
uint32_t day;
|
||||
uint32_t hour;
|
||||
uint32_t minute;
|
||||
uint32_t second;
|
||||
uint32_t day_of_week;
|
||||
};
|
||||
|
||||
struct rtc_tod_args {
|
||||
int proc;
|
||||
struct rtc_time *tm;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
struct suspend_state_info {
|
||||
atomic_t state;
|
||||
int64_t tick_at_suspend;
|
||||
};
|
||||
|
||||
static struct suspend_state_info suspend_state = {ATOMIC_INIT(0), 0};
|
||||
|
||||
void msmrtc_updateatsuspend(struct timespec *ts)
|
||||
{
|
||||
int64_t now, sleep, sclk_max;
|
||||
|
||||
if (atomic_read(&suspend_state.state)) {
|
||||
now = msm_timer_get_sclk_time(&sclk_max);
|
||||
|
||||
if (now && suspend_state.tick_at_suspend) {
|
||||
if (now < suspend_state.tick_at_suspend) {
|
||||
sleep = sclk_max -
|
||||
suspend_state.tick_at_suspend + now;
|
||||
} else
|
||||
sleep = now - suspend_state.tick_at_suspend;
|
||||
|
||||
timespec_add_ns(ts, sleep);
|
||||
suspend_state.tick_at_suspend = now;
|
||||
} else
|
||||
pr_err("%s: Invalid ticks from SCLK now=%lld"
|
||||
"tick_at_suspend=%lld", __func__, now,
|
||||
suspend_state.tick_at_suspend);
|
||||
}
|
||||
|
||||
}
|
||||
#else
|
||||
void msmrtc_updateatsuspend(struct timespec *ts) { }
|
||||
#endif
|
||||
EXPORT_SYMBOL(msmrtc_updateatsuspend);
|
||||
|
||||
static int msmrtc_tod_proc_args(struct msm_rpc_client *client, void *buff,
|
||||
void *data)
|
||||
{
|
||||
struct rtc_tod_args *rtc_args = data;
|
||||
|
||||
if ((rtc_args->proc == TIMEREMOTE_PROCEEDURE_SET_JULIAN)
|
||||
#ifdef CONFIG_RTC_SECURE_TIME_SUPPORT
|
||||
|| (rtc_args->proc == TIMEREMOTE_PROCEEDURE_SET_SECURE_JULIAN)
|
||||
#endif
|
||||
) {
|
||||
struct timeremote_set_julian_req {
|
||||
uint32_t opt_arg;
|
||||
struct rpc_time_julian time;
|
||||
};
|
||||
struct timeremote_set_julian_req *set_req = buff;
|
||||
|
||||
set_req->opt_arg = cpu_to_be32(0x1);
|
||||
set_req->time.year = cpu_to_be32(rtc_args->tm->tm_year);
|
||||
set_req->time.month = cpu_to_be32(rtc_args->tm->tm_mon + 1);
|
||||
set_req->time.day = cpu_to_be32(rtc_args->tm->tm_mday);
|
||||
set_req->time.hour = cpu_to_be32(rtc_args->tm->tm_hour);
|
||||
set_req->time.minute = cpu_to_be32(rtc_args->tm->tm_min);
|
||||
set_req->time.second = cpu_to_be32(rtc_args->tm->tm_sec);
|
||||
set_req->time.day_of_week = cpu_to_be32(rtc_args->tm->tm_wday);
|
||||
|
||||
return sizeof(*set_req);
|
||||
|
||||
} else if ((rtc_args->proc == TIMEREMOTE_PROCEEDURE_GET_JULIAN)
|
||||
#ifdef CONFIG_RTC_SECURE_TIME_SUPPORT
|
||||
|| (rtc_args->proc == TIMEREMOTE_PROCEEDURE_GET_SECURE_JULIAN)
|
||||
#endif
|
||||
) {
|
||||
*(uint32_t *)buff = (uint32_t) cpu_to_be32(0x1);
|
||||
|
||||
return sizeof(uint32_t);
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool rtc_check_overflow(struct rtc_time *tm)
|
||||
{
|
||||
if (tm->tm_year < 138)
|
||||
return false;
|
||||
|
||||
if (tm->tm_year > 138)
|
||||
return true;
|
||||
|
||||
if ((tm->tm_year == 138) && (tm->tm_mon == 0) && (tm->tm_mday < 19))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int msmrtc_tod_proc_result(struct msm_rpc_client *client, void *buff,
|
||||
void *data)
|
||||
{
|
||||
struct rtc_tod_args *rtc_args = data;
|
||||
|
||||
if ((rtc_args->proc == TIMEREMOTE_PROCEEDURE_GET_JULIAN)
|
||||
#ifdef CONFIG_RTC_SECURE_TIME_SUPPORT
|
||||
|| (rtc_args->proc == TIMEREMOTE_PROCEEDURE_GET_SECURE_JULIAN)
|
||||
#endif
|
||||
) {
|
||||
struct timeremote_get_julian_rep {
|
||||
uint32_t opt_arg;
|
||||
struct rpc_time_julian time;
|
||||
};
|
||||
struct timeremote_get_julian_rep *result = buff;
|
||||
|
||||
if (be32_to_cpu(result->opt_arg) != 0x1)
|
||||
return -ENODATA;
|
||||
|
||||
rtc_args->tm->tm_year = be32_to_cpu(result->time.year);
|
||||
rtc_args->tm->tm_mon = be32_to_cpu(result->time.month);
|
||||
rtc_args->tm->tm_mday = be32_to_cpu(result->time.day);
|
||||
rtc_args->tm->tm_hour = be32_to_cpu(result->time.hour);
|
||||
rtc_args->tm->tm_min = be32_to_cpu(result->time.minute);
|
||||
rtc_args->tm->tm_sec = be32_to_cpu(result->time.second);
|
||||
rtc_args->tm->tm_wday = be32_to_cpu(result->time.day_of_week);
|
||||
|
||||
pr_debug("%s: %.2u/%.2u/%.4u %.2u:%.2u:%.2u (%.2u)\n",
|
||||
__func__, rtc_args->tm->tm_mon, rtc_args->tm->tm_mday,
|
||||
rtc_args->tm->tm_year, rtc_args->tm->tm_hour,
|
||||
rtc_args->tm->tm_min, rtc_args->tm->tm_sec,
|
||||
rtc_args->tm->tm_wday);
|
||||
|
||||
/* RTC layer expects years to start at 1900 */
|
||||
rtc_args->tm->tm_year -= 1900;
|
||||
/* RTC layer expects mons to be 0 based */
|
||||
rtc_args->tm->tm_mon--;
|
||||
|
||||
if (rtc_valid_tm(rtc_args->tm) < 0) {
|
||||
pr_err("%s: Retrieved data/time not valid\n", __func__);
|
||||
rtc_time_to_tm(0, rtc_args->tm);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the time received is > 01-19-2038, to prevent
|
||||
* overflow. In such a case, return the EPOCH time.
|
||||
*/
|
||||
if (rtc_check_overflow(rtc_args->tm) == true) {
|
||||
pr_err("Invalid time (year > 2038)\n");
|
||||
rtc_time_to_tm(0, rtc_args->tm);
|
||||
}
|
||||
|
||||
return 0;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
msmrtc_timeremote_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
int rc;
|
||||
struct rtc_tod_args rtc_args;
|
||||
struct msm_rtc *rtc_pdata = dev_get_drvdata(dev);
|
||||
|
||||
if (tm->tm_year < 1900)
|
||||
tm->tm_year += 1900;
|
||||
|
||||
if (tm->tm_year < 1970)
|
||||
return -EINVAL;
|
||||
|
||||
dev_dbg(dev, "%s: %.2u/%.2u/%.4u %.2u:%.2u:%.2u (%.2u)\n",
|
||||
__func__, tm->tm_mon, tm->tm_mday, tm->tm_year,
|
||||
tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_wday);
|
||||
|
||||
rtc_args.proc = TIMEREMOTE_PROCEEDURE_SET_JULIAN;
|
||||
rtc_args.tm = tm;
|
||||
rc = msm_rpc_client_req(rtc_pdata->rpc_client,
|
||||
TIMEREMOTE_PROCEEDURE_SET_JULIAN,
|
||||
msmrtc_tod_proc_args, &rtc_args,
|
||||
NULL, NULL, -1);
|
||||
if (rc) {
|
||||
dev_err(dev, "%s: rtc time (TOD) could not be set\n", __func__);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
msmrtc_timeremote_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
int rc;
|
||||
struct rtc_tod_args rtc_args;
|
||||
struct msm_rtc *rtc_pdata = dev_get_drvdata(dev);
|
||||
|
||||
rtc_args.proc = TIMEREMOTE_PROCEEDURE_GET_JULIAN;
|
||||
rtc_args.tm = tm;
|
||||
|
||||
rc = msm_rpc_client_req(rtc_pdata->rpc_client,
|
||||
TIMEREMOTE_PROCEEDURE_GET_JULIAN,
|
||||
msmrtc_tod_proc_args, &rtc_args,
|
||||
msmrtc_tod_proc_result, &rtc_args, -1);
|
||||
|
||||
if (rc) {
|
||||
dev_err(dev, "%s: Error retrieving rtc (TOD) time\n", __func__);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
msmrtc_virtual_alarm_set(struct device *dev, struct rtc_wkalrm *a)
|
||||
{
|
||||
struct msm_rtc *rtc_pdata = dev_get_drvdata(dev);
|
||||
unsigned long now = get_seconds();
|
||||
|
||||
if (!a->enabled) {
|
||||
rtc_pdata->rtcalarm_time = 0;
|
||||
return 0;
|
||||
} else
|
||||
rtc_tm_to_time(&a->time, &(rtc_pdata->rtcalarm_time));
|
||||
|
||||
if (now > rtc_pdata->rtcalarm_time) {
|
||||
dev_err(dev, "%s: Attempt to set alarm in the past\n",
|
||||
__func__);
|
||||
rtc_pdata->rtcalarm_time = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct rtc_class_ops msm_rtc_ops = {
|
||||
.read_time = msmrtc_timeremote_read_time,
|
||||
.set_time = msmrtc_timeremote_set_time,
|
||||
.set_alarm = msmrtc_virtual_alarm_set,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_RTC_SECURE_TIME_SUPPORT
|
||||
static int
|
||||
msmrtc_timeremote_set_time_secure(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
int rc;
|
||||
struct rtc_tod_args rtc_args;
|
||||
struct msm_rtc *rtc_pdata = dev_get_drvdata(dev);
|
||||
|
||||
if (tm->tm_year < 1900)
|
||||
tm->tm_year += 1900;
|
||||
|
||||
if (tm->tm_year < 1970)
|
||||
return -EINVAL;
|
||||
|
||||
dev_dbg(dev, "%s: %.2u/%.2u/%.4u %.2u:%.2u:%.2u (%.2u)\n",
|
||||
__func__, tm->tm_mon, tm->tm_mday, tm->tm_year,
|
||||
tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_wday);
|
||||
|
||||
rtc_args.proc = TIMEREMOTE_PROCEEDURE_SET_SECURE_JULIAN;
|
||||
rtc_args.tm = tm;
|
||||
|
||||
rc = msm_rpc_client_req(rtc_pdata->rpc_client,
|
||||
TIMEREMOTE_PROCEEDURE_SET_SECURE_JULIAN,
|
||||
msmrtc_tod_proc_args, &rtc_args,
|
||||
NULL, NULL, -1);
|
||||
if (rc) {
|
||||
dev_err(dev,
|
||||
"%s: rtc secure time could not be set\n", __func__);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
msmrtc_timeremote_read_time_secure(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
int rc;
|
||||
struct rtc_tod_args rtc_args;
|
||||
struct msm_rtc *rtc_pdata = dev_get_drvdata(dev);
|
||||
rtc_args.proc = TIMEREMOTE_PROCEEDURE_GET_SECURE_JULIAN;
|
||||
rtc_args.tm = tm;
|
||||
|
||||
rc = msm_rpc_client_req(rtc_pdata->rpc_client,
|
||||
TIMEREMOTE_PROCEEDURE_GET_SECURE_JULIAN, msmrtc_tod_proc_args,
|
||||
&rtc_args, msmrtc_tod_proc_result, &rtc_args, -1);
|
||||
|
||||
if (rc) {
|
||||
dev_err(dev,
|
||||
"%s: Error retrieving secure rtc time\n", __func__);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct rtc_class_ops msm_rtc_ops_secure = {
|
||||
.read_time = msmrtc_timeremote_read_time_secure,
|
||||
.set_time = msmrtc_timeremote_set_time_secure,
|
||||
};
|
||||
#endif
|
||||
|
||||
static void process_cb_request(void *buffer)
|
||||
{
|
||||
struct rtc_cb_recv *rtc_cb = buffer;
|
||||
struct timespec ts, tv;
|
||||
|
||||
rtc_cb->client_cb_id = be32_to_cpu(rtc_cb->client_cb_id);
|
||||
rtc_cb->event = be32_to_cpu(rtc_cb->event);
|
||||
rtc_cb->cb_info_ptr = be32_to_cpu(rtc_cb->cb_info_ptr);
|
||||
|
||||
if (rtc_cb->event == EVENT_TOD_CHANGE) {
|
||||
/* A TOD update has been received from the Modem */
|
||||
rtc_cb->cb_info_data.tod_update.tick =
|
||||
be32_to_cpu(rtc_cb->cb_info_data.tod_update.tick);
|
||||
rtc_cb->cb_info_data.tod_update.stamp =
|
||||
be64_to_cpu(rtc_cb->cb_info_data.tod_update.stamp);
|
||||
rtc_cb->cb_info_data.tod_update.freq =
|
||||
be32_to_cpu(rtc_cb->cb_info_data.tod_update.freq);
|
||||
pr_info("RPC CALL -- TOD TIME UPDATE: ttick = %d\n"
|
||||
"stamp=%lld, freq = %d\n",
|
||||
rtc_cb->cb_info_data.tod_update.tick,
|
||||
rtc_cb->cb_info_data.tod_update.stamp,
|
||||
rtc_cb->cb_info_data.tod_update.freq);
|
||||
|
||||
getnstimeofday(&ts);
|
||||
msmrtc_updateatsuspend(&ts);
|
||||
rtc_hctosys();
|
||||
getnstimeofday(&tv);
|
||||
/* Update the alarm information with the new time info. */
|
||||
alarm_update_timedelta(ts, tv);
|
||||
|
||||
} else
|
||||
pr_err("%s: Unknown event EVENT=%x\n",
|
||||
__func__, rtc_cb->event);
|
||||
}
|
||||
|
||||
static int msmrtc_cb_func(struct msm_rpc_client *client, void *buffer, int size)
|
||||
{
|
||||
int rc = -1;
|
||||
struct rpc_request_hdr *recv = buffer;
|
||||
|
||||
recv->xid = be32_to_cpu(recv->xid);
|
||||
recv->type = be32_to_cpu(recv->type);
|
||||
recv->rpc_vers = be32_to_cpu(recv->rpc_vers);
|
||||
recv->prog = be32_to_cpu(recv->prog);
|
||||
recv->vers = be32_to_cpu(recv->vers);
|
||||
recv->procedure = be32_to_cpu(recv->procedure);
|
||||
|
||||
if (recv->procedure == RTC_EVENT_CB_PROC)
|
||||
process_cb_request((void *) (recv + 1));
|
||||
|
||||
msm_rpc_start_accepted_reply(client, recv->xid,
|
||||
RPC_ACCEPTSTAT_SUCCESS);
|
||||
|
||||
rc = msm_rpc_send_accepted_reply(client, 0);
|
||||
if (rc) {
|
||||
pr_debug("%s: sending reply failed: %d\n", __func__, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msmrtc_rpc_proc_args(struct msm_rpc_client *client, void *buff,
|
||||
void *data)
|
||||
{
|
||||
struct msm_rtc *rtc_pdata = data;
|
||||
|
||||
if (rtc_pdata->proc == RTC_CLIENT_INIT_PROC) {
|
||||
/* arguments passed to the client_init function */
|
||||
struct rtc_client_init_req {
|
||||
enum client_type client;
|
||||
uint32_t client_id_ptr;
|
||||
u8 client_id;
|
||||
enum processor_type processor;
|
||||
};
|
||||
struct rtc_client_init_req *req_1 = buff;
|
||||
|
||||
req_1->client = cpu_to_be32(CLIENT_TYPE_INTERNAL);
|
||||
req_1->client_id_ptr = cpu_to_be32(0x1);
|
||||
req_1->client_id = (u8) cpu_to_be32(0x1);
|
||||
req_1->processor = cpu_to_be32(CLIENT_PROCESSOR_APP1);
|
||||
|
||||
return sizeof(*req_1);
|
||||
|
||||
} else if (rtc_pdata->proc == RTC_REQUEST_CB_PROC) {
|
||||
/* arguments passed to the request_cb function */
|
||||
struct rtc_event_req {
|
||||
u8 client_id;
|
||||
uint32_t rtc_cb_id;
|
||||
};
|
||||
struct rtc_event_req *req_2 = buff;
|
||||
|
||||
req_2->client_id = (u8) cpu_to_be32(rtc_pdata->client_id);
|
||||
req_2->rtc_cb_id = cpu_to_be32(RTC_CB_ID);
|
||||
|
||||
return sizeof(*req_2);
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msmrtc_rpc_proc_result(struct msm_rpc_client *client, void *buff,
|
||||
void *data)
|
||||
{
|
||||
uint32_t result = -EINVAL;
|
||||
struct msm_rtc *rtc_pdata = data;
|
||||
|
||||
if (rtc_pdata->proc == RTC_CLIENT_INIT_PROC) {
|
||||
/* process reply received from client_init function */
|
||||
uint32_t client_id_ptr;
|
||||
result = be32_to_cpu(*(uint32_t *)buff);
|
||||
buff += sizeof(uint32_t);
|
||||
client_id_ptr = be32_to_cpu(*(uint32_t *)(buff));
|
||||
buff += sizeof(uint32_t);
|
||||
if (client_id_ptr == 1)
|
||||
rtc_pdata->client_id = (u8)
|
||||
be32_to_cpu(*(uint32_t *)(buff));
|
||||
else {
|
||||
pr_debug("%s: Client-id not received from Modem\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (rtc_pdata->proc == RTC_REQUEST_CB_PROC) {
|
||||
/* process reply received from request_cb function */
|
||||
result = be32_to_cpu(*(uint32_t *)buff);
|
||||
}
|
||||
|
||||
if (result == ERR_NONE) {
|
||||
pr_debug("%s: RPC client reply for PROC=%x success\n",
|
||||
__func__, rtc_pdata->proc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pr_debug("%s: RPC client registration failed ERROR=%x\n",
|
||||
__func__, result);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int msmrtc_setup_cb(struct msm_rtc *rtc_pdata)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* Register with the server with client specific info */
|
||||
rtc_pdata->proc = RTC_CLIENT_INIT_PROC;
|
||||
rc = msm_rpc_client_req(rtc_pdata->rpc_client, RTC_CLIENT_INIT_PROC,
|
||||
msmrtc_rpc_proc_args, rtc_pdata,
|
||||
msmrtc_rpc_proc_result, rtc_pdata, -1);
|
||||
if (rc) {
|
||||
pr_debug("%s: RPC client registration for PROC:%x failed\n",
|
||||
__func__, RTC_CLIENT_INIT_PROC);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Register with server for the callback event */
|
||||
rtc_pdata->proc = RTC_REQUEST_CB_PROC;
|
||||
rc = msm_rpc_client_req(rtc_pdata->rpc_client, RTC_REQUEST_CB_PROC,
|
||||
msmrtc_rpc_proc_args, rtc_pdata,
|
||||
msmrtc_rpc_proc_result, rtc_pdata, -1);
|
||||
if (rc) {
|
||||
pr_debug("%s: RPC client registration for PROC:%x failed\n",
|
||||
__func__, RTC_REQUEST_CB_PROC);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int __devinit
|
||||
msmrtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
int rc;
|
||||
struct msm_rtc *rtc_pdata = NULL;
|
||||
struct rpcsvr_platform_device *rdev =
|
||||
container_of(pdev, struct rpcsvr_platform_device, base);
|
||||
uint32_t prog_version;
|
||||
|
||||
|
||||
if (pdev->id == (TIMEREMOTE_PROG_VER_1 & RPC_VERSION_MAJOR_MASK))
|
||||
prog_version = TIMEREMOTE_PROG_VER_1;
|
||||
else if (pdev->id == (TIMEREMOTE_PROG_VER_2 &
|
||||
RPC_VERSION_MAJOR_MASK))
|
||||
prog_version = TIMEREMOTE_PROG_VER_2;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
rtc_pdata = kzalloc(sizeof(*rtc_pdata), GFP_KERNEL);
|
||||
if (rtc_pdata == NULL) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: Unable to allocate memory\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
rtc_pdata->rpc_client = msm_rpc_register_client("rtc", rdev->prog,
|
||||
prog_version, 1, msmrtc_cb_func);
|
||||
if (IS_ERR(rtc_pdata->rpc_client)) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: init RPC failed! VERS = %x\n", __func__,
|
||||
prog_version);
|
||||
rc = PTR_ERR(rtc_pdata->rpc_client);
|
||||
kfree(rtc_pdata);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up the callback client.
|
||||
* For older targets this initialization will fail
|
||||
*/
|
||||
rc = msmrtc_setup_cb(rtc_pdata);
|
||||
if (rc)
|
||||
dev_dbg(&pdev->dev, "%s: Could not initialize RPC callback\n",
|
||||
__func__);
|
||||
|
||||
rtc_pdata->rtcalarm_time = 0;
|
||||
platform_set_drvdata(pdev, rtc_pdata);
|
||||
|
||||
rtc_pdata->rtc = rtc_device_register("msm_rtc",
|
||||
&pdev->dev,
|
||||
&msm_rtc_ops,
|
||||
THIS_MODULE);
|
||||
if (IS_ERR(rtc_pdata->rtc)) {
|
||||
dev_err(&pdev->dev, "%s: Can't register RTC device (%ld)\n",
|
||||
pdev->name, PTR_ERR(rtc_pdata->rtc));
|
||||
rc = PTR_ERR(rtc_pdata->rtc);
|
||||
goto fail_cb_setup;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_RTC_SECURE_TIME_SUPPORT
|
||||
rtc_pdata->rtcsecure = rtc_device_register("msm_rtc_secure",
|
||||
&pdev->dev,
|
||||
&msm_rtc_ops_secure,
|
||||
THIS_MODULE);
|
||||
|
||||
if (IS_ERR(rtc_pdata->rtcsecure)) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: Can't register RTC Secure device (%ld)\n",
|
||||
pdev->name, PTR_ERR(rtc_pdata->rtcsecure));
|
||||
rtc_device_unregister(rtc_pdata->rtc);
|
||||
rc = PTR_ERR(rtc_pdata->rtcsecure);
|
||||
goto fail_cb_setup;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_RTC_ASYNC_MODEM_SUPPORT
|
||||
rtc_hctosys();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
fail_cb_setup:
|
||||
msm_rpc_unregister_client(rtc_pdata->rpc_client);
|
||||
kfree(rtc_pdata);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static void
|
||||
msmrtc_alarmtimer_expired(unsigned long _data,
|
||||
struct msm_rtc *rtc_pdata)
|
||||
{
|
||||
pr_debug("%s: Generating alarm event (src %lu)\n",
|
||||
rtc_pdata->rtc->name, _data);
|
||||
|
||||
rtc_update_irq(rtc_pdata->rtc, 1, RTC_IRQF | RTC_AF);
|
||||
rtc_pdata->rtcalarm_time = 0;
|
||||
}
|
||||
|
||||
static int
|
||||
msmrtc_suspend(struct platform_device *dev, pm_message_t state)
|
||||
{
|
||||
int rc, diff;
|
||||
struct rtc_time tm;
|
||||
unsigned long now;
|
||||
struct msm_rtc *rtc_pdata = platform_get_drvdata(dev);
|
||||
|
||||
suspend_state.tick_at_suspend = msm_timer_get_sclk_time(NULL);
|
||||
if (rtc_pdata->rtcalarm_time) {
|
||||
rc = msmrtc_timeremote_read_time(&dev->dev, &tm);
|
||||
if (rc) {
|
||||
dev_err(&dev->dev,
|
||||
"%s: Unable to read from RTC\n", __func__);
|
||||
return rc;
|
||||
}
|
||||
rtc_tm_to_time(&tm, &now);
|
||||
diff = rtc_pdata->rtcalarm_time - now;
|
||||
if (diff <= 0) {
|
||||
msmrtc_alarmtimer_expired(1 , rtc_pdata);
|
||||
msm_pm_set_max_sleep_time(0);
|
||||
atomic_inc(&suspend_state.state);
|
||||
return 0;
|
||||
}
|
||||
msm_pm_set_max_sleep_time((int64_t)
|
||||
((int64_t) diff * NSEC_PER_SEC));
|
||||
} else
|
||||
msm_pm_set_max_sleep_time(0);
|
||||
atomic_inc(&suspend_state.state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
msmrtc_resume(struct platform_device *dev)
|
||||
{
|
||||
int rc, diff;
|
||||
struct rtc_time tm;
|
||||
unsigned long now;
|
||||
struct msm_rtc *rtc_pdata = platform_get_drvdata(dev);
|
||||
|
||||
if (rtc_pdata->rtcalarm_time) {
|
||||
rc = msmrtc_timeremote_read_time(&dev->dev, &tm);
|
||||
if (rc) {
|
||||
dev_err(&dev->dev,
|
||||
"%s: Unable to read from RTC\n", __func__);
|
||||
return rc;
|
||||
}
|
||||
rtc_tm_to_time(&tm, &now);
|
||||
diff = rtc_pdata->rtcalarm_time - now;
|
||||
if (diff <= 0)
|
||||
msmrtc_alarmtimer_expired(2 , rtc_pdata);
|
||||
}
|
||||
suspend_state.tick_at_suspend = 0;
|
||||
atomic_dec(&suspend_state.state);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define msmrtc_suspend NULL
|
||||
#define msmrtc_resume NULL
|
||||
#endif
|
||||
|
||||
static int __devexit msmrtc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct msm_rtc *rtc_pdata = platform_get_drvdata(pdev);
|
||||
|
||||
rtc_device_unregister(rtc_pdata->rtc);
|
||||
#ifdef CONFIG_RTC_SECURE_TIME_SUPPORT
|
||||
rtc_device_unregister(rtc_pdata->rtcsecure);
|
||||
#endif
|
||||
msm_rpc_unregister_client(rtc_pdata->rpc_client);
|
||||
kfree(rtc_pdata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver msmrtc_driver = {
|
||||
.probe = msmrtc_probe,
|
||||
.suspend = msmrtc_suspend,
|
||||
.resume = msmrtc_resume,
|
||||
.remove = __devexit_p(msmrtc_remove),
|
||||
.driver = {
|
||||
.name = APP_TIMEREMOTE_PDEV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init msmrtc_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* For backward compatibility, register multiple platform
|
||||
* drivers with the RPC PROG_VERS to be supported.
|
||||
*
|
||||
* Explicit cast away of 'constness' for driver.name in order to
|
||||
* initialize it here.
|
||||
*/
|
||||
snprintf((char *)msmrtc_driver.driver.name,
|
||||
strlen(msmrtc_driver.driver.name)+1,
|
||||
"rs%08x", TIMEREMOTE_PROG_NUMBER);
|
||||
pr_debug("RTC Registering with %s\n", msmrtc_driver.driver.name);
|
||||
|
||||
rc = platform_driver_register(&msmrtc_driver);
|
||||
if (rc)
|
||||
pr_err("%s: platfrom_driver_register failed\n", __func__);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void __exit msmrtc_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&msmrtc_driver);
|
||||
}
|
||||
|
||||
module_init(msmrtc_init);
|
||||
module_exit(msmrtc_exit);
|
||||
|
||||
MODULE_DESCRIPTION("RTC driver for Qualcomm MSM7x00a chipsets");
|
||||
MODULE_AUTHOR("San Mehat <san@android.com>");
|
||||
MODULE_LICENSE("GPL");
|
32
include/linux/rtc-msm.h
Normal file
32
include/linux/rtc-msm.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __RTC_MSM_H__
|
||||
#define __RTC_MSM_H__
|
||||
|
||||
/*
|
||||
* This is the only function which updates the xtime structure. This
|
||||
* function is supposed to be called only once during kernel initialization.
|
||||
* But we need to call this function whenever we receive an RTC update
|
||||
* from MODEM.
|
||||
*/
|
||||
int rtc_hctosys(void);
|
||||
|
||||
extern void msm_pm_set_max_sleep_time(int64_t sleep_time_ns);
|
||||
void msmrtc_updateatsuspend(struct timespec *ts);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
int64_t msm_timer_get_sclk_time(int64_t *period);
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
#endif /* __RTC_MSM_H__ */
|
Loading…
Reference in a new issue