相关文件list:

pm8998.dtsi ---RTC dts配置
qpnp-rtc.c  ---qcom RTC驱动
class.c    ---RTC相关class
interface.c ---相关RTC功能的接口定义
hctosys.c ---一开机通过RTC设置系统时间
rtc-dev.c   ---RTC device fops接口:open、close、ioctl、poll等

简述:

所谓RTC(Real Time Clock),用于关机时继续计算系统日期和时间。是基于硬件的功能。也可以RTC做Alarm来设置power on/off。

驱动分析:

首先在dts的Document中看到两个配置项:

- qcom,qpnp-rtc-write: This property enables/disables rtc write
0 = Disable rtc writes.
1 = Enable rtc writes.
- qcom,qpnp-rtc-alarm-pwrup: This property enables/disables
feature of powering up phone (from
power down state) through alarm
interrupt.
If not mentioned rtc driver will
disable feature of powring-up
phone through alarm.
0 = Disable powering up of phone
through alarm interrupt.
1 = Enable powering up of phone
through alarm interrupt.

一个是是否使能写RTC时间的功能。另一个是是否支持RTC alarm开机的功能。

接下来,再看RTC的驱动部分,从qpnp-rtc.c:

其中根据.compatible = "qcom,qpnp-rtc"匹配成功后走到probe,probe中获取了dts中上面两条配置参数,从而进行初始化。获取RTC/Alarm相关的寄存器和资源,并通过操作寄存器enable RTC相关功能。注册了RTC中断并配置了RTC相关操作的api。

通过dts的配置,使用不同的ops,从而实现支持write RTC与否。

//这个是不支持write RTC的ops
static const struct rtc_class_ops qpnp_rtc_ro_ops = {
.read_time = qpnp_rtc_read_time,
.set_alarm = qpnp_rtc_set_alarm,
.read_alarm = qpnp_rtc_read_alarm,
.alarm_irq_enable = qpnp_rtc_alarm_irq_enable,
}; //这个是支持读写 RTC的ops,就是多了个write的接口
static const struct rtc_class_ops qpnp_rtc_rw_ops = {
.read_time = qpnp_rtc_read_time,
.set_alarm = qpnp_rtc_set_alarm,
.read_alarm = qpnp_rtc_read_alarm,
.alarm_irq_enable = qpnp_rtc_alarm_irq_enable,
.set_time = qpnp_rtc_set_time,
};
static int qpnp_rtc_probe(struct platform_device *pdev)
{
const struct rtc_class_ops *rtc_ops = &qpnp_rtc_ro_ops; //默认使用不支持RTC write的ops
int rc;
u8 subtype;
struct qpnp_rtc *rtc_dd;
unsigned int base;
struct device_node *child; rtc_dd = devm_kzalloc(&pdev->dev, sizeof(*rtc_dd), GFP_KERNEL);
if (rtc_dd == NULL)
return -ENOMEM; rtc_dd->regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!rtc_dd->regmap) {
dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
return -EINVAL;
} /* Get the rtc write property */
rc = of_property_read_u32(pdev->dev.of_node, "qcom,qpnp-rtc-write",
&rtc_dd->rtc_write_enable); //读取dts中的RTC write配置
if (rc && rc != -EINVAL) {
dev_err(&pdev->dev,
"Error reading rtc_write_enable property %d\n", rc);
return rc;
} rc = of_property_read_u32(pdev->dev.of_node,
"qcom,qpnp-rtc-alarm-pwrup",
&rtc_dd->rtc_alarm_powerup); //读取dts中RTC alarm powerup的配置
if (rc && rc != -EINVAL) {
dev_err(&pdev->dev,
"Error reading rtc_alarm_powerup property %d\n", rc);
return rc;
} /* Initialise spinlock to protect RTC control register */
spin_lock_init(&rtc_dd->alarm_ctrl_lock); rtc_dd->rtc_dev = &(pdev->dev);
rtc_dd->pdev = pdev; if (of_get_available_child_count(pdev->dev.of_node) == ) {
pr_err("no child nodes\n");
rc = -ENXIO;
goto fail_rtc_enable;
} /* Get RTC/ALARM resources */
for_each_available_child_of_node(pdev->dev.of_node, child) {
rc = of_property_read_u32(child, "reg", &base);
if (rc < ) {
dev_err(&pdev->dev,
"Couldn't find reg in node = %s rc = %d\n",
child->full_name, rc);
goto fail_rtc_enable;
} rc = qpnp_read_wrapper(rtc_dd, &subtype,
base + REG_OFFSET_PERP_SUBTYPE, );
if (rc) {
dev_err(&pdev->dev,
"Peripheral subtype read failed\n");
goto fail_rtc_enable;
} switch (subtype) {
case RTC_PERPH_SUBTYPE:
rtc_dd->rtc_base = base;
break;
case ALARM_PERPH_SUBTYPE:
rtc_dd->alarm_base = base;
rtc_dd->rtc_alarm_irq = of_irq_get(child, );
if (rtc_dd->rtc_alarm_irq < ) {
dev_err(&pdev->dev, "ALARM IRQ absent\n");
rc = -ENXIO;
goto fail_rtc_enable;
}
break;
default:
dev_err(&pdev->dev, "Invalid peripheral subtype\n");
rc = -EINVAL;
goto fail_rtc_enable;
}
} rc = qpnp_read_wrapper(rtc_dd, &rtc_dd->rtc_ctrl_reg,
rtc_dd->rtc_base + REG_OFFSET_RTC_CTRL, );
if (rc) {
dev_err(&pdev->dev, "Read from RTC control reg failed\n");
goto fail_rtc_enable;
} if (!(rtc_dd->rtc_ctrl_reg & BIT_RTC_ENABLE)) {
dev_err(&pdev->dev, "RTC h/w disabled, rtc not registered\n");
goto fail_rtc_enable;
} rc = qpnp_read_wrapper(rtc_dd, &rtc_dd->alarm_ctrl_reg1,
rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, );
if (rc) {
dev_err(&pdev->dev, "Read from Alarm control reg failed\n");
goto fail_rtc_enable;
}
/* Enable abort enable feature */
rtc_dd->alarm_ctrl_reg1 |= BIT_RTC_ABORT_ENABLE;
rc = qpnp_write_wrapper(rtc_dd, &rtc_dd->alarm_ctrl_reg1,
rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, );
if (rc) {
dev_err(&pdev->dev, "SPMI write failed!\n");
goto fail_rtc_enable;
} if (rtc_dd->rtc_write_enable == true) //判断dts中配置如果要支持RTC write功能,那就重新赋值,使用将RTC RW都支持的ops接口
rtc_ops = &qpnp_rtc_rw_ops; dev_set_drvdata(&pdev->dev, rtc_dd); /* Register the RTC device */
rtc_dd->rtc = rtc_device_register("qpnp_rtc", &pdev->dev,
rtc_ops, THIS_MODULE);
if (IS_ERR(rtc_dd->rtc)) {
dev_err(&pdev->dev, "%s: RTC registration failed (%ld)\n",
__func__, PTR_ERR(rtc_dd->rtc));
rc = PTR_ERR(rtc_dd->rtc);
goto fail_rtc_enable;
} /* Request the alarm IRQ */
rc = request_any_context_irq(rtc_dd->rtc_alarm_irq,
qpnp_alarm_trigger, IRQF_TRIGGER_RISING,
"qpnp_rtc_alarm", rtc_dd);
if (rc) {
dev_err(&pdev->dev, "Request IRQ failed (%d)\n", rc);
goto fail_req_irq;
} device_init_wakeup(&pdev->dev, );
enable_irq_wake(rtc_dd->rtc_alarm_irq); dev_dbg(&pdev->dev, "Probe success !!\n"); return ; fail_req_irq:
rtc_device_unregister(rtc_dd->rtc);
fail_rtc_enable:
dev_set_drvdata(&pdev->dev, NULL); return rc;
}

而在RTC shutdown时,会根据是否要支持RTC alarm开机,进行中断和寄存器的配置。

static void qpnp_rtc_shutdown(struct platform_device *pdev)
{
u8 value[] = {};
u8 reg;
int rc;
unsigned long irq_flags;
struct qpnp_rtc *rtc_dd;
bool rtc_alarm_powerup; if (!pdev) {
pr_err("qpnp-rtc: spmi device not found\n");
return;
}
rtc_dd = dev_get_drvdata(&pdev->dev);
if (!rtc_dd) {
pr_err("qpnp-rtc: rtc driver data not found\n");
return;
}
rtc_alarm_powerup = rtc_dd->rtc_alarm_powerup;
if (!rtc_alarm_powerup && !poweron_alarm) { //根据flag设置是否disbale RTC alarm的中断,以及通过设置reg,控制是否disable RTC alarm
spin_lock_irqsave(&rtc_dd->alarm_ctrl_lock, irq_flags);
dev_dbg(&pdev->dev, "Disabling alarm interrupts\n"); /* Disable RTC alarms */
reg = rtc_dd->alarm_ctrl_reg1;
reg &= ~BIT_RTC_ALARM_ENABLE;
rc = qpnp_write_wrapper(rtc_dd, &reg,
rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, );
if (rc) {
dev_err(rtc_dd->rtc_dev, "SPMI write failed\n");
goto fail_alarm_disable;
} /* Clear Alarm register */
rc = qpnp_write_wrapper(rtc_dd, value,
rtc_dd->alarm_base + REG_OFFSET_ALARM_RW,
NUM_8_BIT_RTC_REGS);
if (rc)
dev_err(rtc_dd->rtc_dev, "SPMI write failed\n"); fail_alarm_disable:
spin_unlock_irqrestore(&rtc_dd->alarm_ctrl_lock, irq_flags);
}
}

内核的开机时间首先是从RTC读取的时间作为基准,之后会通过QCOM time daemon进行corection。QCOM time daemon的代码非open source,所以暂不分析。这里我们仅分析RTC的部分。

而RTC的时间,我们知道第一次开机,这里指的是RTC断电后的第一次开机,RTC时间是1970-01-01 00:00:00(初始时间),这个值就是从RTC中读取出来的。而在使用一段时间之后,再重启手机,这时RTC的时间为=初始时间+使用的时间。也就是说,这个时间会随着使用而不断累计,除非RTC掉电,重置为初始时间。RTC也支持将同步后的时间再次写入RTC,用于校准当前的正确日期和时间。

Case 1 开机系统内核时间设置 from RTC

这里主要分析RTC驱动部分的RTC时间读取,并设置内核系统时间。

log:

[    0.897142] qcom,qpnp-rtc c440000.qcom,spmi:qcom,pm8998@:qcom,pm8998_rtc: rtc core: registered qpnp_rtc as rtc0
[ 1.808755] hctosys: [ljj]open rtc device (rtc0)
[ 1.808819] qcom,qpnp-rtc c440000.qcom,spmi:qcom,pm8998@:qcom,pm8998_rtc: setting system clock to -- :: UTC ()

上面的代码,可以看到kernel log的最前面是距离开机的时间累计(CLOCK_MONOTONIC),后面的则是RTC读取的UTC时间(CLOCK_REALTIME)。可以看到一开机,就会从RTC中读取UTC时间。

对应代码在hcttosys.c中:

static int __init rtc_hctosys(void)
{
int err = -ENODEV;
struct rtc_time tm;
struct timespec64 tv64 = {
.tv_nsec = NSEC_PER_SEC >> ,
};
struct rtc_device *rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
pr_info("[ljj]open rtc device (%s)\n",
CONFIG_RTC_HCTOSYS_DEVICE);
if (rtc == NULL) {
pr_info("unable to open rtc device (%s)\n",
CONFIG_RTC_HCTOSYS_DEVICE);
goto err_open;
}
//api 调用
err = rtc_read_time(rtc, &tm);
if (err) {
dev_err(rtc->dev.parent,
"hctosys: unable to read the hardware clock\n");
goto err_read; } tv64.tv_sec = rtc_tm_to_time64(&tm); #if BITS_PER_LONG == 32
if (tv64.tv_sec > INT_MAX)
goto err_read;
#endif
//设置内核系统时间
err = do_settimeofday64(&tv64); dev_info(rtc->dev.parent,
"setting system clock to "
"%d-%02d-%02d %02d:%02d:%02d UTC (%lld)\n",
tm.tm_year + , tm.tm_mon + , tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec,
(long long) tv64.tv_sec); err_read:
rtc_class_close(rtc); err_open:
rtc_hctosys_ret = err; return err;
} late_initcall(rtc_hctosys);
int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
{
int err; err = mutex_lock_interruptible(&rtc->ops_lock);
if (err)
return err; err = __rtc_read_time(rtc, tm); //调用内部__rtc_read_time
mutex_unlock(&rtc->ops_lock);
return err;
}
EXPORT_SYMBOL_GPL(rtc_read_time);
static int __rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
{
int err;
if (!rtc->ops)
err = -ENODEV;
else if (!rtc->ops->read_time)
err = -EINVAL;
else {
memset(tm, , sizeof(struct rtc_time));
err = rtc->ops->read_time(rtc->dev.parent, tm); //调用了qpnp-rtc.c中的read_time ops,最后就是通过读取寄存器,来获取RTC硬件计算的时间,具体代码就不贴了
if (err < ) {
dev_dbg(&rtc->dev, "read_time: fail to read: %d\n",
err);
return err;
} err = rtc_valid_tm(tm);
if (err < )
dev_dbg(&rtc->dev, "read_time: rtc_time isn't valid\n");
}
return err;
}

Case 2 kernel RTC时间与system时间进行同步

实际是因为RTC中的时间并不一定准,所以会用android system中的时间与RTC时间进行对比同步和更新。也就是上面可能会牵扯到QCOM time Daemon的部分,所以避开这块闭源代码。可以参考如下链接,了解开源代码中内核时间与system时间同步。

kernel log中时间同步的流程,参考自:http://blog.chinaunix.net/uid-23141914-id-5715368.html

Case 3 更新并设置RTC时间

当时间与网络、或者setting同步后,可能需要将当前正确的时间再写入RTC中,以保证RTC中的时间准确。这块我们往上看多一点,从jni开始分析。如下:

路径:frameworks/base/services/core/jni/com_android_server_AlarmManagerService.cpp

static const JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"init", "()J", (void*)android_server_AlarmManagerService_init},
{"close", "(J)V", (void*)android_server_AlarmManagerService_close},
{"set", "(JIJJ)I", (void*)android_server_AlarmManagerService_set},
{"waitForAlarm", "(J)I", (void*)android_server_AlarmManagerService_waitForAlarm},
{"setKernelTime", "(JJ)I", (void*)android_server_AlarmManagerService_setKernelTime}, //设置时间的JNI
{"setKernelTimezone", "(JI)I", (void*)android_server_AlarmManagerService_setKernelTimezone},
}; static jint android_server_AlarmManagerService_setKernelTime(JNIEnv*, jobject, jlong nativeData, jlong millis)
{
AlarmImpl *impl = reinterpret_cast<AlarmImpl *>(nativeData);
struct timeval tv;
int ret; if (millis <= || millis / 1000LL >= INT_MAX) {
return -;
} tv.tv_sec = (time_t) (millis / 1000LL);
tv.tv_usec = (suseconds_t) ((millis % 1000LL) * 1000LL); ALOGD("Setting time of day to sec=%d\n", (int) tv.tv_sec); ret = impl->setTime(&tv); //调用setTime if(ret < ) {
ALOGW("Unable to set rtc to %ld: %s\n", tv.tv_sec, strerror(errno));
ret = -;
}
return ret;
} int AlarmImpl::setTime(struct timeval *tv)
{
struct rtc_time rtc;
struct tm tm, *gmtime_res;
int fd;
int res; res = settimeofday(tv, NULL);
if (res < ) {
ALOGV("settimeofday() failed: %s\n", strerror(errno));
return -;
} if (rtc_id < ) {
ALOGV("Not setting RTC because wall clock RTC was not found");
errno = ENODEV;
return -;
} android::String8 rtc_dev = String8::format("/dev/rtc%d", rtc_id);
fd = open(rtc_dev.string(), O_RDWR); //打开了RTC设备
if (fd < ) {
ALOGV("Unable to open %s: %s\n", rtc_dev.string(), strerror(errno));
return res;
} gmtime_res = gmtime_r(&tv->tv_sec, &tm);
if (!gmtime_res) {
ALOGV("gmtime_r() failed: %s\n", strerror(errno));
res = -;
goto done;
} memset(&rtc, , sizeof(rtc));
rtc.tm_sec = tm.tm_sec;
rtc.tm_min = tm.tm_min;
rtc.tm_hour = tm.tm_hour;
rtc.tm_mday = tm.tm_mday;
rtc.tm_mon = tm.tm_mon;
rtc.tm_year = tm.tm_year;
rtc.tm_wday = tm.tm_wday;
rtc.tm_yday = tm.tm_yday;
rtc.tm_isdst = tm.tm_isdst;
res = ioctl(fd, RTC_SET_TIME, &rtc); //通过ioctl设置RTC时间
if (res < )
ALOGV("RTC_SET_TIME ioctl failed: %s\n", strerror(errno));
done:
close(fd);
return res;
}

接下来就是驱动部分,rtc-dev.c文件中的ioctl接口,最后会调用至qpnp-rtc.c中的set接口,从而将准确的时间写入对应寄存器:

static long rtc_dev_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
int err = ;
struct rtc_device *rtc = file->private_data;
const struct rtc_class_ops *ops = rtc->ops;
struct rtc_time tm;
struct rtc_wkalrm alarm;
void __user *uarg = (void __user *) arg; err = mutex_lock_interruptible(&rtc->ops_lock);
if (err)
return err; /* check that the calling task has appropriate permissions
* for certain ioctls. doing this check here is useful
* to avoid duplicate code in each driver.
*/
switch (cmd) {
case RTC_EPOCH_SET:
case RTC_SET_TIME:
if (!capable(CAP_SYS_TIME)) //如上面注释所说,设置时间的api必须要有CAP_SYS_TIME的权限
err = -EACCES;
break; case RTC_IRQP_SET:
if (arg > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE))
err = -EACCES;
break; case RTC_PIE_ON:
if (rtc->irq_freq > rtc->max_user_freq &&
!capable(CAP_SYS_RESOURCE))
err = -EACCES;
break;
} if (err)
goto done; /*
* Drivers *SHOULD NOT* provide ioctl implementations
* for these requests. Instead, provide methods to
* support the following code, so that the RTC's main
* features are accessible without using ioctls.
*
* RTC and alarm times will be in UTC, by preference,
* but dual-booting with MS-Windows implies RTCs must
* use the local wall clock time.
*/ switch (cmd) {
case RTC_ALM_READ:
mutex_unlock(&rtc->ops_lock); err = rtc_read_alarm(rtc, &alarm);
if (err < )
return err; if (copy_to_user(uarg, &alarm.time, sizeof(tm)))
err = -EFAULT;
return err; case RTC_ALM_SET:
mutex_unlock(&rtc->ops_lock); if (copy_from_user(&alarm.time, uarg, sizeof(tm)))
return -EFAULT; alarm.enabled = ;
alarm.pending = ;
alarm.time.tm_wday = -;
alarm.time.tm_yday = -;
alarm.time.tm_isdst = -; /* RTC_ALM_SET alarms may be up to 24 hours in the future.
* Rather than expecting every RTC to implement "don't care"
* for day/month/year fields, just force the alarm to have
* the right values for those fields.
*
* RTC_WKALM_SET should be used instead. Not only does it
* eliminate the need for a separate RTC_AIE_ON call, it
* doesn't have the "alarm 23:59:59 in the future" race.
*
* NOTE: some legacy code may have used invalid fields as
* wildcards, exposing hardware "periodic alarm" capabilities.
* Not supported here.
*/
{
time64_t now, then; err = rtc_read_time(rtc, &tm);
if (err < )
return err;
now = rtc_tm_to_time64(&tm); alarm.time.tm_mday = tm.tm_mday;
alarm.time.tm_mon = tm.tm_mon;
alarm.time.tm_year = tm.tm_year;
err = rtc_valid_tm(&alarm.time);
if (err < )
return err;
then = rtc_tm_to_time64(&alarm.time); /* alarm may need to wrap into tomorrow */
if (then < now) {
rtc_time64_to_tm(now + * * , &tm);
alarm.time.tm_mday = tm.tm_mday;
alarm.time.tm_mon = tm.tm_mon;
alarm.time.tm_year = tm.tm_year;
}
} return rtc_set_alarm(rtc, &alarm); case RTC_RD_TIME:
mutex_unlock(&rtc->ops_lock); err = rtc_read_time(rtc, &tm);
if (err < )
return err; if (copy_to_user(uarg, &tm, sizeof(tm)))
err = -EFAULT;
return err; case RTC_SET_TIME:
mutex_unlock(&rtc->ops_lock); if (copy_from_user(&tm, uarg, sizeof(tm))) //获取上层传下来的tm结构
return -EFAULT; return rtc_set_time(rtc, &tm); //通过interface.c的接口,从而调用qpnp-rtc.c的设置RTC寄存器函数,完成RTC时间写入。 case RTC_PIE_ON:
err = rtc_irq_set_state(rtc, NULL, );
break; case RTC_PIE_OFF:
err = rtc_irq_set_state(rtc, NULL, );
break; case RTC_AIE_ON:
mutex_unlock(&rtc->ops_lock);
return rtc_alarm_irq_enable(rtc, ); case RTC_AIE_OFF:
mutex_unlock(&rtc->ops_lock);
return rtc_alarm_irq_enable(rtc, ); case RTC_UIE_ON:
mutex_unlock(&rtc->ops_lock);
return rtc_update_irq_enable(rtc, ); case RTC_UIE_OFF:
mutex_unlock(&rtc->ops_lock);
return rtc_update_irq_enable(rtc, ); case RTC_IRQP_SET:
err = rtc_irq_set_freq(rtc, NULL, arg);
break; case RTC_IRQP_READ:
err = put_user(rtc->irq_freq, (unsigned long __user *)uarg);
break; case RTC_WKALM_SET:
mutex_unlock(&rtc->ops_lock);
if (copy_from_user(&alarm, uarg, sizeof(alarm)))
return -EFAULT; return rtc_set_alarm(rtc, &alarm); case RTC_WKALM_RD:
mutex_unlock(&rtc->ops_lock);
err = rtc_read_alarm(rtc, &alarm);
if (err < )
return err; if (copy_to_user(uarg, &alarm, sizeof(alarm)))
err = -EFAULT;
return err; default:
/* Finally try the driver's ioctl interface */
if (ops->ioctl) {
err = ops->ioctl(rtc->dev.parent, cmd, arg);
if (err == -ENOIOCTLCMD)
err = -ENOTTY;
} else
err = -ENOTTY;
break;
} done:
mutex_unlock(&rtc->ops_lock);
return err;
}

Qcom平台RTC驱动分析的更多相关文章

  1. IMX6Q RTC驱动分析

    对于在工作中学习驱动的,讲究的是先使用,再理解.好吧,我们来看看板子里是如何注册的? 在板文件里,它的注册函数是这样的: imx6q_add_imx_snvs_rtc() 好吧,让我们追踪下去: 1 ...

  2. 高通 android平台LCD驱动分析

    目前手机芯片厂家提供的源码里包含整个LCD驱动框架,一般厂家会定义一个xxx_fb.c的源文件,注册一个平台设备和平台驱动,在驱动的probe函数中来调用register_framebuffer(), ...

  3. linux RTC 驱动模型分析【转】

    转自:http://blog.csdn.net/yaozhenguo2006/article/details/6824970 RTC(real time clock)实时时钟,主要作用是给Linux系 ...

  4. Linux RTC驱动模型分析之rtc-sysfs.c【转】

    转自:https://blog.csdn.net/longwang155069/article/details/52353408 版权声明:本文为博主原创文章,未经博主允许不得转载. https:// ...

  5. 字符设备驱动、平台设备驱动、设备驱动模型、sysfs的比较和关联

    转载自:http://www.kancloud.cn/yueqian_scut/emlinux/106829 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动.设备驱动模型和sy ...

  6. 电源管理之pmu驱动分析

    电源管理芯片可以为多设备供电,且这些设备电压电流有所不同.为这些设备提供的稳压器代码模型即为regulator. 说白了regulator就是稳压器,它提供电源供给.简单的可以gpio操作,高电平开电 ...

  7. [kernel]字符设备驱动、平台设备驱动、设备驱动模型、sysfs几者之间的比较和关联

    转自:http://www.2cto.com/kf/201510/444943.html Linux驱动开发经验总结,绝对干货! 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动 ...

  8. 二十一、RTC驱动

    一.RTC设备驱动分析 内核的rtc驱动位于内核drivers/rtc目录下,里面包含各个平台的RTC驱动.读者可在此目录下任意选择一个单板驱动文件进行分析,我选择的是rtc-davinci.c文件. ...

  9. linux kernel 平台总线实例分析

    linux 平台总线的实现有三大块  , platform bus , platform device , platform drvice 平台类型结构体: /** * struct bus_type ...

随机推荐

  1. 学习RxJava+Retrofit+OkHttp+MVP的网络请求使用

    公司的大佬用的是这一套,那我这个菜鸟肯定要学习使用了. 我在网上找了很多文章,写的都很详细,比如 https://www.jianshu.com/u/5fd2523645da https://www. ...

  2. jQuery中操作属性的方法attr与prop的区别

    attr 与 prop 都可以对某个属性进行获取和设置的操作,二者的用法相同: <script src = 'jQuery.js'></script> <script&g ...

  3. Jekyll 解决Jekyll server本地预览文章not found的问题

    layout: post tags: [Jekyll] comments: true 执行Jekyll本地浏览器预览指令 bundle exec jekyll serve 进入浏览器输入127.0.0 ...

  4. 栈溢出(Stack Overflow)

    调用栈(Call Stack)描述的时函数之间的调用关系.它由多个栈帧(Stack Frame)组成,每个栈帧对应着一个未运行完的函数.栈帧中保存了该函数的返回地址和局部变量,因而不能再执行完毕后找到 ...

  5. jquery-----ajax的几种方法

    $.get(url,function(data,status){ if(status == 'success'){ layer.msg(data,{shift:1,time:2000},functio ...

  6. 5-JVM常用的命令

    JVM常用的命令 jps :基础工具 查看JAVA进程PID. jps 命令用来查看所有 Java 进程,每一行就是一个 Java 进程信息. jps 仅查找当前用户的 Java 进程,而不是当前系统 ...

  7. 我的.vimrc配置

    termux平台下,通过pkg update && pkg upgrade && pkg install vim来获得vim.可是,当我配置.vimrc时发现如果我每在 ...

  8. 微信小程序-视频弹幕的项目

    1.视频播放器 2.选择弹幕颜色 3.弹幕来了... 一般微信小程序需要配置.wxml.wxss.js.json文件,所有接下来也是要配置这几个文件,请看下图: 第一:  index.wxml < ...

  9. DFS序--一般都要转化为顶点到每个点

    There is a rooted tree with n nodes, number from 1-n. Root’s number is 1.Each node has a value ai. I ...

  10. UVA11038

    题目大意:见刘汝佳<算法竞赛入门经典——训练指南>P173 解题思路: 求出 0 到 m-1 这些数字中 0 出现的次数(此处要特判 m = 0 的情况),再求出 0 到 n 这些数字中 ...