Linux第106步_Linux内核RTC驱动实验
1、了解rtc_device结构体
1)、打开“include/linux/rtc.h”
rtc_class_ops是需要用户根据所使用的RTC设备编写的,其结构体如下:
struct rtc_class_ops {
int (*ioctl)(struct device *, unsigned int, unsigned long);/*函数指针ioctl*/
int (*read_time)(struct device *, struct rtc_time *);/*函数指针read_time*/
int (*set_time)(struct device *, struct rtc_time *);/*函数指针set_time*/
int (*read_alarm)(struct device *, struct rtc_wkalrm *);
/*函数指针read_alarm*/
int (*set_alarm)(struct device *, struct rtc_wkalrm *);
/*函数指针set_alarm*/
int (*proc)(struct device *, struct seq_file *);/*函数指针proc*/
int (*alarm_irq_enable)(struct device *, unsigned int enabled);
/*函数指针alarm_irq_enable*/
int (*read_offset)(struct device *, long *offset);/*函数指针read_offset*/
int (*set_offset)(struct device *, long offset);/*函数指针set_offset*/
};
struct rtc_device {
struct device dev; /*设备*/
struct module *owner;
int id; /*设备ID号*/
const struct rtc_class_ops *ops;/*RTC设备最底层操作函数*/
struct mutex ops_lock;
struct cdev char_dev;/*字符设备*/
unsigned long flags;
unsigned long irq_data;
spinlock_t irq_lock;
wait_queue_head_t irq_queue;
struct fasync_struct *async_queue;
int irq_freq;
int max_user_freq;
struct timerqueue_head timerqueue;
struct rtc_timer aie_timer;
struct rtc_timer uie_rtctimer;
struct hrtimer pie_timer; /* sub second exp, so needs hrtimer */
int pie_enabled;
struct work_struct irqwork;
/* Some hardware can't support UIE mode */
int uie_unsupported;
/* Number of nsec it takes to set the RTC clock. This influences when
* the set ops are called. An offset:
* - of 0.5 s will call RTC set for wall clock time 10.0 s at 9.5 s
* - of 1.5 s will call RTC set for wall clock time 10.0 s at 8.5 s
* - of -0.5 s will call RTC set for wall clock time 10.0 s at 10.5 s
*/
long set_offset_nsec;
bool registered;
/* Old ABI support */
bool nvram_old_abi;
struct bin_attribute *nvram;
time64_t range_min;
timeu64_t range_max;
time64_t start_secs;
time64_t offset_secs;
bool set_start_time;
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
struct work_struct uie_task;
struct timer_list uie_timer;
/* Those fields are protected by rtc->irq_lock */
unsigned int oldsecs;
unsigned int uie_irq_active:1;
unsigned int stop_uie_polling:1;
unsigned int uie_task_active:1;
unsigned int uie_timer_active:1;
#endif
};
2)、打开“drivers/rtc/class.c”
/*注册RTC类设备
* devm_rtc_device_register - resource managed rtc_device_register()
* @dev: 要注册的设备,the device to register
* @name: 设备名字,the name of the device (unused)
* @ops: 底层驱动函数集,the rtc operations structure
* @owner: 驱动模块拥有者,the module owner
*返回值:注册成功的话就返回rtcdevice,4错误的话会返回一个负值
* @return a struct rtc on success, or an ERR_PTR on error
*
* Managed rtc_device_register(). The rtc_device returned from this function
* are automatically freed on driver detach.
* This function is deprecated, use devm_rtc_allocate_device and
* rtc_register_device instead
*/
struct rtc_device *devm_rtc_device_register(struct device *dev,
const char *name,
const struct rtc_class_ops *ops,
struct module *owner)
{
struct rtc_device *rtc;
int err;
rtc = devm_rtc_allocate_device(dev);
if (IS_ERR(rtc))
return rtc;
rtc->ops = ops;
err = __rtc_register_device(owner, rtc);
if (err)
return ERR_PTR(err);
return rtc;
}
struct rtc_device *rtc_device_register(const char *name, struct device *dev, const struct rtc_class_ops *ops, struct module *owner);//rtc_device_register()和devm_rtc_device_register()功能相同;
/**
*卸载RTC驱动时,删除先前注册的RTC类设备
* rtc_device_unregister - removes the previously registered RTC class device
*
* @rtc: the RTC class device to destroy
*/
static void rtc_device_unregister(struct rtc_device *rtc)
{
mutex_lock(&rtc->ops_lock);
/*
* Remove innards of this RTC, then disable it, before
* letting any rtc_class_open() users access it again
*/
rtc_proc_del_device(rtc);
cdev_device_del(&rtc->char_dev, &rtc->dev);
rtc->ops = NULL;
mutex_unlock(&rtc->ops_lock);
put_device(&rtc->dev);
}
int __rtc_register_device(struct module *owner, struct rtc_device *rtc)
{
struct rtc_wkalrm alrm;
int err;
if (!rtc->ops) {
dev_dbg(&rtc->dev, "no ops set\n");
return -EINVAL;
}
rtc->owner = owner;
rtc_device_get_offset(rtc);
/* Check to see if there is an ALARM already set in hw */
err = __rtc_read_alarm(rtc, &alrm);
if (!err && !rtc_valid_tm(&alrm.time))
rtc_initialize_alarm(rtc, &alrm);
rtc_dev_prepare(rtc);
err = cdev_device_add(&rtc->char_dev, &rtc->dev);
if (err)
dev_warn(rtc->dev.parent, "failed to add char device %d:%d\n",
MAJOR(rtc->dev.devt), rtc->id);
else
dev_dbg(rtc->dev.parent, "char device (%d:%d)\n",
MAJOR(rtc->dev.devt), rtc->id);
rtc_proc_add_device(rtc);
rtc->registered = true;
dev_info(rtc->dev.parent, "registered as %s\n",
dev_name(&rtc->dev));
return 0;
}
/**卸载RTC驱动时,删除先前注册的RTC类设备;
* rtc_device_unregister - removes the previously registered RTC class device
*
* @rtc: the RTC class device to destroy
*/
static void rtc_device_unregister(struct rtc_device *rtc)
{
mutex_lock(&rtc->ops_lock);/* 上锁 */
/*
* Remove innards of this RTC, then disable it, before
* letting any rtc_class_open() users access it again
*/
rtc_proc_del_device(rtc);
cdev_device_del(&rtc->char_dev, &rtc->dev);
rtc->ops = NULL;
mutex_unlock(&rtc->ops_lock);/* 解锁,释放互斥锁 */
put_device(&rtc->dev);
}
devm_rtc_device_unregister()和rtc_device_unregister(struct rtc_device *rtc)功能相同;
3)、打开“include/linux/rtc.h”
/*
* For these RTC methods the device parameter is the physical device
* on whatever bus holds the hardware (I2C, Platform, SPI, etc), which
* was passed to rtc_device_register(). Its driver_data normally holds
* device state, including the rtc_device pointer for the RTC.
*
* Most of these methods are called with rtc_device.ops_lock held,
* through the rtc_*(struct rtc_device *, ...) calls.
*
* The (current) exceptions are mostly filesystem hooks:
* - the proc() hook for procfs
* - non-ioctl() chardev hooks: open(), release()
*
* REVISIT those periodic irq calls *do* have ops_lock when they're
* issued through ioctl() ...
rtc_class_ops是需要用户根据所使用的RTC设备编写的,其结构体如下:
*/
struct rtc_class_ops {
int (*ioctl)(struct device *, unsigned int, unsigned long);/*函数指针ioctl*/
int (*read_time)(struct device *, struct rtc_time *);/*函数指针read_time*/
int (*set_time)(struct device *, struct rtc_time *);/*函数指针set_time*/
int (*read_alarm)(struct device *, struct rtc_wkalrm *);/*函数指针read_alarm*/
int (*set_alarm)(struct device *, struct rtc_wkalrm *);/*函数指针set_alarm*/
int (*proc)(struct device *, struct seq_file *);/*函数指针proc*/
int (*alarm_irq_enable)(struct device *, unsigned int enabled);/*函数指针alarm_irq_enable*/
int (*read_offset)(struct device *, long *offset);/*函数指针read_offset*/
int (*set_offset)(struct device *, long offset);/*函数指针set_offset*/
};
打开“/usr/include/linux/rtc.h”
struct rtc_time {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
};
4)、打开“drivers/rtc/dev.c”
/* 设备操作函数结构体 */
/*声明file_operations结构变量rtc_dev_fops,它是指向设备的操作函数集合变量*/
static const struct file_operations rtc_dev_fops = {
.owner = THIS_MODULE,
/*表示该文件的操作结构体所属的模块是当前的模块,即这个模块属于内核*/
.llseek = no_llseek,
.read = rtc_dev_read,
.poll = rtc_dev_poll,
.unlocked_ioctl = rtc_dev_ioctl,
.open = rtc_dev_open,
.release = rtc_dev_release,
.fasync = rtc_dev_fasync,
};
static long rtc_dev_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
int err = 0;
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))
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 < 0)
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 = 0;
alarm.pending = 0;
alarm.time.tm_wday = -1;
alarm.time.tm_yday = -1;
alarm.time.tm_isdst = -1;
/* 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 < 0)
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 < 0)
return err;
then = rtc_tm_to_time64(&alarm.time);
/* alarm may need to wrap into tomorrow */
if (then < now) {
rtc_time64_to_tm(now + 24 * 60 * 60, &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);
/*获取当前RTC时钟,rtc_read_time()会调用__rtc_read_time()*/
if (err < 0)
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)))
return -EFAULT;
return rtc_set_time(rtc, &tm);/*设置时间*/
case RTC_PIE_ON:
err = rtc_irq_set_state(rtc, 1);
break;
case RTC_PIE_OFF:
err = rtc_irq_set_state(rtc, 0);
break;
case RTC_AIE_ON:
mutex_unlock(&rtc->ops_lock);/* 解锁,释放互斥锁 */
return rtc_alarm_irq_enable(rtc, 1);
case RTC_AIE_OFF:
mutex_unlock(&rtc->ops_lock);/* 解锁,释放互斥锁 */
return rtc_alarm_irq_enable(rtc, 0);
case RTC_UIE_ON:
mutex_unlock(&rtc->ops_lock);/* 解锁,释放互斥锁 */
return rtc_update_irq_enable(rtc, 1);
case RTC_UIE_OFF:
mutex_unlock(&rtc->ops_lock);/* 解锁,释放互斥锁 */
return rtc_update_irq_enable(rtc, 0);
case RTC_IRQP_SET:
err = rtc_irq_set_freq(rtc, 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 < 0)
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;
}
5)、打开“/linux-5.4.31/drivers/rtc/interface.c”
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, 0, sizeof(struct rtc_time));
err = rtc->ops->read_time(rtc->dev.parent, tm);
if (err < 0) {
dev_dbg(&rtc->dev, "read_time: fail to read: %d\n",
err);
return err;
}
rtc_add_offset(rtc, tm);
err = rtc_valid_tm(tm);
if (err < 0)
dev_dbg(&rtc->dev, "read_time: rtc_time isn't valid\n");
}
return err;
}
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);
mutex_unlock(&rtc->ops_lock);/* 解锁,释放互斥锁 */
trace_rtc_read_time(rtc_tm_to_time64(tm), err);
return err;
}
6)、打开“/linux-5.4.31/drivers/rtc/rtc-stm32.c”
static const struct of_device_id stm32_rtc_of_match[] = {
{ .compatible = "st,stm32-rtc", .data = &stm32_rtc_data },
{ .compatible = "st,stm32h7-rtc", .data = &stm32h7_rtc_data },
{ .compatible = "st,stm32mp1-rtc", .data = &stm32mp1_data },
/*"st,stm32mp1-rtc"在设备树中*/
{ /*这是一个空元素,在编写of_device_id时最后一个元素一定要为空*/
/* Sentinel */
}
};
/*标准的platform驱动框架*/
static struct platform_driver stm32_rtc_driver = {
.probe = stm32_rtc_probe,
.remove = stm32_rtc_remove,
.driver = {
.name = DRIVER_NAME,
.pm = &stm32_rtc_pm_ops,
.of_match_table = stm32_rtc_of_match,
},
};
static const struct rtc_class_ops stm32_rtc_ops = {
.read_time = stm32_rtc_read_time,
.set_time = stm32_rtc_set_time,
.read_alarm = stm32_rtc_read_alarm,
.set_alarm = stm32_rtc_set_alarm,
.alarm_irq_enable = stm32_rtc_alarm_irq_enable,
};
2、修改设备树
1)、打开“stm32mp151.dtsi”找到“rtc”,内容如下:
rtc: rtc@5c004000 {
compatible = "st,stm32mp1-rtc";
reg = <0x5c004000 0x400>;
clocks = <&scmi0_clk CK_SCMI0_RTCAPB>,
<&scmi0_clk CK_SCMI0_RTC>;
clock-names = "pclk", "rtc_ck";
interrupts-extended = <&exti 19 IRQ_TYPE_LEVEL_HIGH>;
status = "disabled";
};
2)、打开“stm32mp157d-atk.dts”,添加内容如下(注意:不是在根节点“/”下添加):
&rtc {
status = "okay";
};
3、“drivers/rtc/rtc-stm32.c”程序如下:
#include <linux/bcd.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/errno.h>
#include <linux/iopoll.h>
#include <linux/ioport.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>//使能stm32_rtc_init()
#include <linux/of_device.h>
#include <linux/pm_wakeirq.h>
#include <linux/regmap.h>
#include <linux/rtc.h>
#include <dt-bindings/rtc/rtc-stm32.h>
#define DRIVER_NAME "stm32_rtc"
/* STM32_RTC_TR bit fields */
#define STM32_RTC_TR_SEC_SHIFT 0
#define STM32_RTC_TR_SEC GENMASK(6, 0)
#define STM32_RTC_TR_MIN_SHIFT 8
#define STM32_RTC_TR_MIN GENMASK(14, 8)
#define STM32_RTC_TR_HOUR_SHIFT 16
#define STM32_RTC_TR_HOUR GENMASK(21, 16)
/* STM32_RTC_DR bit fields */
#define STM32_RTC_DR_DATE_SHIFT 0
#define STM32_RTC_DR_DATE GENMASK(5, 0)
#define STM32_RTC_DR_MONTH_SHIFT 8
#define STM32_RTC_DR_MONTH GENMASK(12, 8)
#define STM32_RTC_DR_WDAY_SHIFT 13
#define STM32_RTC_DR_WDAY GENMASK(15, 13)
#define STM32_RTC_DR_YEAR_SHIFT 16
#define STM32_RTC_DR_YEAR GENMASK(23, 16)
/* STM32_RTC_CR bit fields */
#define STM32_RTC_CR_FMT BIT(6)
#define STM32_RTC_CR_ALRAE BIT(8)
#define STM32_RTC_CR_ALRAIE BIT(12)
#define STM32_RTC_CR_COSEL BIT(19)
#define STM32_RTC_CR_OSEL_SHIFT 21
#define STM32_RTC_CR_OSEL GENMASK(22, 21)
#define STM32_RTC_CR_COE BIT(23)
#define STM32_RTC_CR_TAMPOE BIT(26)
#define STM32_RTC_CR_OUT2EN BIT(31)
/* STM32_RTC_ISR/STM32_RTC_ICSR bit fields */
#define STM32_RTC_ISR_ALRAWF BIT(0)
#define STM32_RTC_ISR_INITS BIT(4)
#define STM32_RTC_ISR_RSF BIT(5)
#define STM32_RTC_ISR_INITF BIT(6)
#define STM32_RTC_ISR_INIT BIT(7)
#define STM32_RTC_ISR_ALRAF BIT(8)
/* STM32_RTC_PRER bit fields */
#define STM32_RTC_PRER_PRED_S_SHIFT 0
#define STM32_RTC_PRER_PRED_S GENMASK(14, 0)
#define STM32_RTC_PRER_PRED_A_SHIFT 16
#define STM32_RTC_PRER_PRED_A GENMASK(22, 16)
/* STM32_RTC_ALRMAR and STM32_RTC_ALRMBR bit fields */
#define STM32_RTC_ALRMXR_SEC_SHIFT 0
#define STM32_RTC_ALRMXR_SEC GENMASK(6, 0)
#define STM32_RTC_ALRMXR_SEC_MASK BIT(7)
#define STM32_RTC_ALRMXR_MIN_SHIFT 8
#define STM32_RTC_ALRMXR_MIN GENMASK(14, 8)
#define STM32_RTC_ALRMXR_MIN_MASK BIT(15)
#define STM32_RTC_ALRMXR_HOUR_SHIFT 16
#define STM32_RTC_ALRMXR_HOUR GENMASK(21, 16)
#define STM32_RTC_ALRMXR_PM BIT(22)
#define STM32_RTC_ALRMXR_HOUR_MASK BIT(23)
#define STM32_RTC_ALRMXR_DATE_SHIFT 24
#define STM32_RTC_ALRMXR_DATE GENMASK(29, 24)
#define STM32_RTC_ALRMXR_WDSEL BIT(30)
#define STM32_RTC_ALRMXR_WDAY_SHIFT 24
#define STM32_RTC_ALRMXR_WDAY GENMASK(27, 24)
#define STM32_RTC_ALRMXR_DATE_MASK BIT(31)
/* STM32_RTC_SR/_SCR bit fields */
#define STM32_RTC_SR_ALRA BIT(0)
/* STM32_RTC_CFGR bit fields */
#define STM32_RTC_CFGR_OUT2_RMP BIT(0)
#define STM32_RTC_CFGR_LSCOEN_OUT1 1
#define STM32_RTC_CFGR_LSCOEN_OUT2_RMP 2
/* STM32_RTC_VERR bit fields */
#define STM32_RTC_VERR_MINREV_SHIFT 0
#define STM32_RTC_VERR_MINREV GENMASK(3, 0)
#define STM32_RTC_VERR_MAJREV_SHIFT 4
#define STM32_RTC_VERR_MAJREV GENMASK(7, 4)
/* STM32_RTC_WPR key constants */
#define RTC_WPR_1ST_KEY 0xCA
#define RTC_WPR_2ND_KEY 0x53
#define RTC_WPR_WRONG_KEY 0xFF
/* Max STM32 RTC register offset is 0x3FC */
#define UNDEF_REG 0xFFFF
struct stm32_rtc;//声明stm32_rtc结构类型
struct stm32_rtc_registers {
u16 tr;
u16 dr;
u16 cr;
u16 isr;/*RTC_ICSR寄存器*/
u16 prer;
u16 alrmar;
u16 wpr;
u16 sr;
u16 scr;
u16 cfgr;
u16 verr;
};
struct stm32_rtc_events {
u32 alra;
};
struct stm32_rtc_data {
const struct stm32_rtc_registers regs;
const struct stm32_rtc_events events;
void (*clear_events)(struct stm32_rtc *rtc, unsigned int flags);
bool has_pclk;
bool need_dbp;
bool has_lsco;
};
struct stm32_rtc {
struct rtc_device *rtc_dev;
void __iomem *base;
struct regmap *dbp;
unsigned int dbp_reg;
unsigned int dbp_mask;
struct clk *pclk;
struct clk *rtc_ck;
const struct stm32_rtc_data *data;
int irq_alarm;
int lsco;
struct clk *clk_lsco;
};
/*
* -------------------------------------------------------------------------
* | TAMPOE | OSEL[1:0] | COE | OUT2EN | RTC_OUT1 | RTC_OUT2 |
* | | | | | | or RTC_OUT2_RMP |
* |-------------------------------------------------------------------------|
* | 0 | 00 | 0 | 0 or 1 | - | - |
* |--------|-----------|-----|--------|------------------|------------------|
* | 0 | 00 | 1 | 0 | CALIB | - |
* |--------|-----------|-----|--------|------------------|------------------|
* | 0 or 1 | !=00 | 0 | 0 | TAMPALRM | - |
* |--------|-----------|-----|--------|------------------|------------------|
* | 0 | 00 | 1 | 1 | - | CALIB |
* |--------|-----------|-----|--------|------------------|------------------|
* | 0 or 1 | !=00 | 0 | 1 | - | TAMPALRM |
* |--------|-----------|-----|--------|------------------|------------------|
* | 0 or 1 | !=00 | 1 | 1 | TAMPALRM | CALIB |
* -------------------------------------------------------------------------
*/
static int stm32_rtc_clk_lsco_check_availability(struct stm32_rtc *rtc)
{
struct stm32_rtc_registers regs = rtc->data->regs;
unsigned int cr = readl_relaxed(rtc->base + regs.cr);
/*读取STM32MP1的RTC_CR寄存器的值*/
unsigned int cfgr = readl_relaxed(rtc->base + regs.cfgr);
/*读取STM32MP1的RTC_CFGR寄存器的值*/
unsigned int calib = STM32_RTC_CR_COE;
unsigned int tampalrm = STM32_RTC_CR_TAMPOE | STM32_RTC_CR_OSEL;
switch (rtc->lsco) {
case RTC_OUT1:
if ((!(cr & STM32_RTC_CR_OUT2EN) &&
((cr & calib) || cr & tampalrm)) ||
((cr & calib) && (cr & tampalrm)))
return -EBUSY;
break;
case RTC_OUT2_RMP:
if ((cr & STM32_RTC_CR_OUT2EN) &&
(cfgr & STM32_RTC_CFGR_OUT2_RMP) &&
((cr & calib) || (cr & tampalrm)))
return -EBUSY;
break;
default:
return -EINVAL;
}
if (clk_get_rate(rtc->rtc_ck) != 32768)
/*获得rtc->rtc_ck时钟源的当前时钟频率(HZ)*/
return -ERANGE;
return 0;
}
static int stm32_rtc_clk_lsco_register(struct platform_device *pdev)
{
struct stm32_rtc *rtc = platform_get_drvdata(pdev);
struct stm32_rtc_registers regs = rtc->data->regs;
u8 lscoen;
int ret;
ret = stm32_rtc_clk_lsco_check_availability(rtc);
if (ret)
return ret;
lscoen = (rtc->lsco == RTC_OUT1) ? STM32_RTC_CFGR_LSCOEN_OUT1 :
STM32_RTC_CFGR_LSCOEN_OUT2_RMP;
rtc->clk_lsco = clk_register_gate(&pdev->dev, "rtc_lsco",
__clk_get_name(rtc->rtc_ck),
CLK_IGNORE_UNUSED | CLK_IS_CRITICAL,
rtc->base + regs.cfgr, lscoen,
0, NULL);
if (IS_ERR(rtc->clk_lsco))
return PTR_ERR(rtc->clk_lsco);
of_clk_add_provider(pdev->dev.of_node,
of_clk_src_simple_get, rtc->clk_lsco);
return 0;
}
/*“RTC寄存器“解锁,允许写入*/
static void stm32_rtc_wpr_unlock(struct stm32_rtc *rtc)
{
const struct stm32_rtc_registers *regs = &rtc->data->regs;
writel_relaxed(RTC_WPR_1ST_KEY, rtc->base + regs->wpr);
writel_relaxed(RTC_WPR_2ND_KEY, rtc->base + regs->wpr);
/*
解锁步骤:
1. Write 0xCA into the RTC_WPR register.
2. Write 0x53 into the RTC_WPR register.
*/
}
/*写错值,会重新激活“RTC寄存器“写保护,不允许写RTC寄存器;
Writing a wrong key reactivates the write protection.
*/
static void stm32_rtc_wpr_lock(struct stm32_rtc *rtc)
{
const struct stm32_rtc_registers *regs = &rtc->data->regs;
writel_relaxed(RTC_WPR_WRONG_KEY, rtc->base + regs->wpr);
/*将0xFF写入“RTC写保护寄存器RTC_WPR“*/
}
//函数功能:令RTC进入初始化模式
static int stm32_rtc_enter_init_mode(struct stm32_rtc *rtc)
{
const struct stm32_rtc_registers *regs = &rtc->data->regs;
unsigned int isr = readl_relaxed(rtc->base + regs->isr);
/*读“RTC_ICSR寄存器“*/
if (!(isr & STM32_RTC_ISR_INITF)) {
isr |= STM32_RTC_ISR_INIT;
/*准备将“RTC_ICSR寄存器“的bit7置1*/
/*Initialization mode used to program time and date register
(RTC_TR and RTC_DR), and prescaler register (RTC_PRER).*/
writel_relaxed(isr, rtc->base + regs->isr);
/*将isr的值写入“RTC_ICSR寄存器“*/
/*
* It takes around 2 rtc_ck clock cycles to enter in
* initialization phase mode (and have INITF flag set). As
* slowest rtc_ck frequency may be 32kHz and highest should be
* 1MHz, we poll every 10 us with a timeout of 100ms.
*/
return readl_relaxed_poll_timeout_atomic(
rtc->base + regs->isr,
isr, (isr & STM32_RTC_ISR_INITF),
10, 100000);
/*每10us轮询一次“RTC_ICSR寄存器“的bit7是否置1;
直到从“Free running mode”进入“Initialization mode“
*/
}
return 0;
}
//函数功能:令RTC进入“自由运行模式“
static void stm32_rtc_exit_init_mode(struct stm32_rtc *rtc)
{
const struct stm32_rtc_registers *regs = &rtc->data->regs;
unsigned int isr = readl_relaxed(rtc->base + regs->isr);
/*读“RTC_ICSR寄存器“*/
isr &= ~STM32_RTC_ISR_INIT;
/*准备将“RTC_ICSR寄存器“的bit7置0,
令RTC从“Initialization mode“进入“Free running mode”*/
writel_relaxed(isr, rtc->base + regs->isr);
/*将isr的值写入“RTC_ICSR寄存器“*/
}
//函数功能:令“日历影子寄存器“同步
static int stm32_rtc_wait_sync(struct stm32_rtc *rtc)
{
const struct stm32_rtc_registers *regs = &rtc->data->regs;
unsigned int isr = readl_relaxed(rtc->base + regs->isr);
/*读“RTC_ICSR寄存器“*/
isr &= ~STM32_RTC_ISR_RSF;
/*准备将“RTC_ICSR寄存器“的bit5置0,准备令“日历影子寄存器“不同步;
“RTC_ICSR寄存器“的bit5=1,表示“Calendar shadow registers synchronized“
*/
writel_relaxed(isr, rtc->base + regs->isr);
/*将isr的值写入“RTC_ICSR寄存器“*/
/*
* Wait for RSF to be set to ensure the calendar registers are
* synchronised, it takes around 2 rtc_ck clock cycles
*/
return readl_relaxed_poll_timeout_atomic(rtc->base + regs->isr,
isr,
(isr & STM32_RTC_ISR_RSF),
10, 100000);
/*每10us轮询一次“RTC_ICSR寄存器“的bit5是否置1;
直到“日历影子寄存器“同步
*/
}
/*将flags写入RTC_SCR寄存器(RTC status clear register)*/
static void stm32_rtc_clear_event_flags(struct stm32_rtc *rtc,
unsigned int flags)
{
rtc->data->clear_events(rtc, flags);
/*将flags写入RTC_SCR寄存器(RTC status clear register)*/
}
/*RTC的“Alarm A”中断服务函数*/
static irqreturn_t stm32_rtc_alarm_irq(int irq, void *dev_id)
{
struct stm32_rtc *rtc = (struct stm32_rtc *)dev_id;
const struct stm32_rtc_registers *regs = &rtc->data->regs;
const struct stm32_rtc_events *evts = &rtc->data->events;
unsigned int status, cr;
mutex_lock(&rtc->rtc_dev->ops_lock);/* 上锁 */
status = readl_relaxed(rtc->base + regs->sr);/*读“RTC_SR寄存器“*/
cr = readl_relaxed(rtc->base + regs->cr);/*读“RTC_CR寄存器“*/
if ((status & evts->alra) &&
(cr & STM32_RTC_CR_ALRAIE)) {
/* Alarm A flag - Alarm interrupt */
dev_dbg(&rtc->rtc_dev->dev, "Alarm occurred\n");
/*将事件传递给内核,Pass event to the kernel */
rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF);
/* Clear event flags, otherwise new events won't be received */
stm32_rtc_clear_event_flags(rtc, evts->alra);
/*将evts->alra写入RTC_SCR寄存器(RTC status clear register);
Clear alarm A flag;
*/
}
mutex_unlock(&rtc->rtc_dev->ops_lock);/* 解锁,释放互斥锁 */
return IRQ_HANDLED;
}
/* Convert rtc_time structure from bin to bcd format */
/*函数功能:将rtc_time结构转换为BCD格式*/
static void tm2bcd(struct rtc_time *tm)
{
tm->tm_sec = bin2bcd(tm->tm_sec);
tm->tm_min = bin2bcd(tm->tm_min);
tm->tm_hour = bin2bcd(tm->tm_hour);
tm->tm_mday = bin2bcd(tm->tm_mday);
tm->tm_mon = bin2bcd(tm->tm_mon + 1);
tm->tm_year = bin2bcd(tm->tm_year - 100);
/*
* Number of days since Sunday
* - on kernel side, 0=Sunday...6=Saturday
* - on rtc side, 0=invalid,1=Monday...7=Sunday
*/
tm->tm_wday = (!tm->tm_wday) ? 7 : tm->tm_wday;
}
/* Convert rtc_time structure from bcd to bin format */
/*函数功能:将BCD格式转换为rtc_time结构*/
static void bcd2tm(struct rtc_time *tm)
{
tm->tm_sec = bcd2bin(tm->tm_sec);
tm->tm_min = bcd2bin(tm->tm_min);
tm->tm_hour = bcd2bin(tm->tm_hour);
tm->tm_mday = bcd2bin(tm->tm_mday);
tm->tm_mon = bcd2bin(tm->tm_mon) - 1;
tm->tm_year = bcd2bin(tm->tm_year) + 100;
/*
* Number of days since Sunday
* - on kernel side, 0=Sunday...6=Saturday
* - on rtc side, 0=invalid,1=Monday...7=Sunday
*/
tm->tm_wday %= 7;
}
//函数功能:读取RTC时间和日期,保存到tm中
static int stm32_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct stm32_rtc *rtc = dev_get_drvdata(dev);
const struct stm32_rtc_registers *regs = &rtc->data->regs;
unsigned int tr, dr;
/* Time and Date in BCD format */
tr = readl_relaxed(rtc->base + regs->tr);/*读取STM32MP1的RTC_TR时间寄存器的值*/
dr = readl_relaxed(rtc->base + regs->dr);/*读取STM32MP1的RTC_DR日期寄存器的值*/
tm->tm_sec = (tr & STM32_RTC_TR_SEC) >> STM32_RTC_TR_SEC_SHIFT;
tm->tm_min = (tr & STM32_RTC_TR_MIN) >> STM32_RTC_TR_MIN_SHIFT;
tm->tm_hour = (tr & STM32_RTC_TR_HOUR) >> STM32_RTC_TR_HOUR_SHIFT;
tm->tm_mday = (dr & STM32_RTC_DR_DATE) >> STM32_RTC_DR_DATE_SHIFT;
tm->tm_mon = (dr & STM32_RTC_DR_MONTH) >> STM32_RTC_DR_MONTH_SHIFT;
tm->tm_year = (dr & STM32_RTC_DR_YEAR) >> STM32_RTC_DR_YEAR_SHIFT;
tm->tm_wday = (dr & STM32_RTC_DR_WDAY) >> STM32_RTC_DR_WDAY_SHIFT;
/* We don't report tm_yday and tm_isdst */
bcd2tm(tm);/*将BCD格式转换为rtc_time格式*/
return 0;
}
//函数功能:将tm中的时间和日期写入RTC_TR时间寄存器和RTC_DR日期寄存器
static int stm32_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct stm32_rtc *rtc = dev_get_drvdata(dev);
const struct stm32_rtc_registers *regs = &rtc->data->regs;
unsigned int tr, dr;
int ret = 0;
tm2bcd(tm);/*将rtc_time结构转换为BCD格式*/
/* Time in BCD format */
tr = ((tm->tm_sec << STM32_RTC_TR_SEC_SHIFT) & STM32_RTC_TR_SEC) |
((tm->tm_min << STM32_RTC_TR_MIN_SHIFT) & STM32_RTC_TR_MIN) |
((tm->tm_hour << STM32_RTC_TR_HOUR_SHIFT) & STM32_RTC_TR_HOUR);
/* Date in BCD format */
dr = ((tm->tm_mday << STM32_RTC_DR_DATE_SHIFT) & STM32_RTC_DR_DATE) |
((tm->tm_mon << STM32_RTC_DR_MONTH_SHIFT) & STM32_RTC_DR_MONTH) |
((tm->tm_year << STM32_RTC_DR_YEAR_SHIFT) & STM32_RTC_DR_YEAR) |
((tm->tm_wday << STM32_RTC_DR_WDAY_SHIFT) & STM32_RTC_DR_WDAY);
stm32_rtc_wpr_unlock(rtc);/*“RTC寄存器“解锁,允许写入*/
ret = stm32_rtc_enter_init_mode(rtc);/*令RTC进入初始化模式*/
if (ret) {
dev_err(dev, "Can't enter in init mode. Set time aborted.\n");
goto end;
}
writel_relaxed(tr, rtc->base + regs->tr);/*tr写入RTC_TR时间寄存器*/
writel_relaxed(dr, rtc->base + regs->dr);/*dr写入RTC_DR日期寄存器*/
stm32_rtc_exit_init_mode(rtc);/*令RTC进入“自由运行模式“*/
ret = stm32_rtc_wait_sync(rtc);/*令“日历影子寄存器“同步*/
end:
stm32_rtc_wpr_lock(rtc);/*写错值,会重新激活“RTC寄存器“写保护,不允许写RTC寄存器*/
return ret;
}
//函数功能:读RTC报警时间,保存到alrm结构中
static int stm32_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct stm32_rtc *rtc = dev_get_drvdata(dev);
const struct stm32_rtc_registers *regs = &rtc->data->regs;
const struct stm32_rtc_events *evts = &rtc->data->events;
struct rtc_time *tm = &alrm->time;
unsigned int alrmar, cr, status;
alrmar = readl_relaxed(rtc->base + regs->alrmar);
/*读取STM32MP1的RTC_ALRMAR寄存器的值*/
cr = readl_relaxed(rtc->base + regs->cr);
/*读取STM32MP1的RTC_CR寄存器的值*/
status = readl_relaxed(rtc->base + regs->sr);
/*读取STM32MP1的RTC_CS寄存器的值*/
if (alrmar & STM32_RTC_ALRMXR_DATE_MASK) {
/*日期/天不用匹配,即每天触发一次报警
* Date/day doesn't matter in Alarm comparison so alarm
* triggers every day
*/
tm->tm_mday = -1;
tm->tm_wday = -1;
} else {//日期/天匹配,才会报警
if (alrmar & STM32_RTC_ALRMXR_WDSEL) {
/*在星期几触发一次报警;
Alarm is set to a day of week */
tm->tm_mday = -1;
tm->tm_wday = (alrmar & STM32_RTC_ALRMXR_WDAY) >>
STM32_RTC_ALRMXR_WDAY_SHIFT;
/*将RTC_ALRMAR中的DU[3:0]保存到tm->tm_wday*/
tm->tm_wday %= 7;/*得到星期几*/
} else {
/* 在某天触发报警
Alarm is set to a day of month */
tm->tm_wday = -1;
tm->tm_mday = (alrmar & STM32_RTC_ALRMXR_DATE) >>
STM32_RTC_ALRMXR_DATE_SHIFT;
/*将RTC_ALRMAR中的DU[3:0]保存到tm->tm_wday*/
}
}
if (alrmar & STM32_RTC_ALRMXR_HOUR_MASK) {
/* 不要求小时报警
Hours don't matter in Alarm comparison */
tm->tm_hour = -1;
} else {/*要求小时报警*/
tm->tm_hour = (alrmar & STM32_RTC_ALRMXR_HOUR) >>
STM32_RTC_ALRMXR_HOUR_SHIFT;
/*将RTC_ALRMAR中的HU[3:0]保存到tm->tm_hour*/
if (alrmar & STM32_RTC_ALRMXR_PM)
tm->tm_hour += 12;
}
if (alrmar & STM32_RTC_ALRMXR_MIN_MASK) {
/* 不要求分钟报警
Minutes don't matter in Alarm comparison */
tm->tm_min = -1;
} else {/*要求分钟报警*/
tm->tm_min = (alrmar & STM32_RTC_ALRMXR_MIN) >>
STM32_RTC_ALRMXR_MIN_SHIFT;
/*将RTC_ALRMAR中的MNT[2:0]和MNU[3:0]保存到tm->tm_min*/
}
if (alrmar & STM32_RTC_ALRMXR_SEC_MASK) {
/* 不要求妙报警
Seconds don't matter in Alarm comparison */
tm->tm_sec = -1;
} else {/*要求妙报警*/
tm->tm_sec = (alrmar & STM32_RTC_ALRMXR_SEC) >>
STM32_RTC_ALRMXR_SEC_SHIFT;
/*将RTC_ALRMAR中的ST[2:0]和SU[3:0]保存到tm->tm_sec*/
}
bcd2tm(tm);
alrm->enabled = (cr & STM32_RTC_CR_ALRAE) ? 1 : 0;
/*如果“Alarm A enable”,则返回1;
如果“Alarm A disabled”,则返回0;
*/
alrm->pending = (status & evts->alra) ? 1 : 0;
/*如果“Alarm A标志“建立,则返回1;
如果“Alarm A标志“没有建立,则返回0;
*/
return 0;
}
/*函数功能:
enabled=1,设置使能“ALARM A“和使能“ALARM A“报警时能产生中断;
enabled=0,设置不使能“ALARM A“和不使能“ALARM A“报警时能产生中断;
*/
static int stm32_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
struct stm32_rtc *rtc = dev_get_drvdata(dev);
const struct stm32_rtc_registers *regs = &rtc->data->regs;
const struct stm32_rtc_events *evts = &rtc->data->events;
unsigned int cr;
cr = readl_relaxed(rtc->base + regs->cr);
/*读取STM32MP1的RTC_CR寄存器的值*/
stm32_rtc_wpr_unlock(rtc);/*“RTC寄存器“解锁,允许写入*/
/* We expose Alarm A to the kernel */
if (enabled)
cr |= (STM32_RTC_CR_ALRAIE | STM32_RTC_CR_ALRAE);
/*准备将RTC_CR寄存器的bit12置1,使能ALARM A报警中断*/
/*准备将RTC_CR寄存器的bit8置1,使能ALARM A*/
else
cr &= ~(STM32_RTC_CR_ALRAIE | STM32_RTC_CR_ALRAE);
/*准备将RTC_CR寄存器的bit12置0,不使能ALARM A报警中断*/
/*准备将RTC_CR寄存器的bit8置0,不使能ALARM A*/
writel_relaxed(cr, rtc->base + regs->cr);/*cr写入RTC_CR寄存器*/
/* Clear event flags, otherwise new events won't be received */
stm32_rtc_clear_event_flags(rtc, evts->alra);
/*将evts->alra写入RTC_SCR寄存器(RTC status clear register)*/
stm32_rtc_wpr_lock(rtc);/*写错值,会重新激活“RTC寄存器“写保护,不允许写RTC寄存器*/
return 0;
}
//函数功能:返回0,表示设置的ALARM报警时间tm是有效的
static int stm32_rtc_valid_alrm(struct stm32_rtc *rtc, struct rtc_time *tm)
{
const struct stm32_rtc_registers *regs = &rtc->data->regs;
int cur_day, cur_mon, cur_year, cur_hour, cur_min, cur_sec;
unsigned int dr = readl_relaxed(rtc->base + regs->dr);
/*读取STM32MP1的RTC_DR日期寄存器的值*/
unsigned int tr = readl_relaxed(rtc->base + regs->tr);
/*读取STM32MP1的RTC_TR时间寄存器的值*/
cur_day = (dr & STM32_RTC_DR_DATE) >> STM32_RTC_DR_DATE_SHIFT;
cur_mon = (dr & STM32_RTC_DR_MONTH) >> STM32_RTC_DR_MONTH_SHIFT;
cur_year = (dr & STM32_RTC_DR_YEAR) >> STM32_RTC_DR_YEAR_SHIFT;
cur_sec = (tr & STM32_RTC_TR_SEC) >> STM32_RTC_TR_SEC_SHIFT;
cur_min = (tr & STM32_RTC_TR_MIN) >> STM32_RTC_TR_MIN_SHIFT;
cur_hour = (tr & STM32_RTC_TR_HOUR) >> STM32_RTC_TR_HOUR_SHIFT;
/*
* Assuming current date is M-D-Y H:M:S.
* RTC alarm can't be set on a specific month and year.
* So the valid alarm range is:
* M-D-Y H:M:S < alarm <= (M+1)-D-Y H:M:S
* with a specific case for December...
*/
if ((((tm->tm_year > cur_year) &&
(tm->tm_mon == 0x1) && (cur_mon == 0x12)) ||
((tm->tm_year == cur_year) &&
(tm->tm_mon <= cur_mon + 1))) &&
((tm->tm_mday > cur_day) ||
((tm->tm_mday == cur_day) &&
((tm->tm_hour > cur_hour) ||
((tm->tm_hour == cur_hour) && (tm->tm_min > cur_min)) ||
((tm->tm_hour == cur_hour) && (tm->tm_min == cur_min) &&
(tm->tm_sec >= cur_sec))))))
return 0;
return -EINVAL;
}
//函数功能:设置“ALARM A报警时间“,报警时间保存在alrm中
static int stm32_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct stm32_rtc *rtc = dev_get_drvdata(dev);
const struct stm32_rtc_registers *regs = &rtc->data->regs;
struct rtc_time *tm = &alrm->time;
unsigned int cr, isr, alrmar;
int ret = 0;
tm2bcd(tm);/*将rtc_time结构转换为BCD格式*/
/*
* RTC alarm can't be set on a specific date, unless this date is
* up to the same day of month next month.
*/
if (stm32_rtc_valid_alrm(rtc, tm) < 0) {
/*stm32_rtc_valid_alrm()返回0,表示设置的ALARM报警时间tm是有效的*/
dev_err(dev, "Alarm can be set only on upcoming month.\n");
return -EINVAL;
}
alrmar = 0;
/* tm_year and tm_mon are not used because not supported by RTC */
alrmar |= (tm->tm_mday << STM32_RTC_ALRMXR_DATE_SHIFT) &
STM32_RTC_ALRMXR_DATE;
/* 24-hour format */
alrmar &= ~STM32_RTC_ALRMXR_PM;
alrmar |= (tm->tm_hour << STM32_RTC_ALRMXR_HOUR_SHIFT) &
STM32_RTC_ALRMXR_HOUR;
alrmar |= (tm->tm_min << STM32_RTC_ALRMXR_MIN_SHIFT) &
STM32_RTC_ALRMXR_MIN;
alrmar |= (tm->tm_sec << STM32_RTC_ALRMXR_SEC_SHIFT) &
STM32_RTC_ALRMXR_SEC;
stm32_rtc_wpr_unlock(rtc);/*“RTC寄存器“解锁,允许写入*/
/* Disable Alarm */
cr = readl_relaxed(rtc->base + regs->cr);/*读取STM32MP1的RTC_CR寄存器的值*/
cr &= ~STM32_RTC_CR_ALRAE;/*准备将RTC_CR寄存器的bit8置0,不使能ALARM A*/
writel_relaxed(cr, rtc->base + regs->cr);/*cr写入RTC_CR寄存器*/
/*
* Poll Alarm write flag to be sure that Alarm update is allowed: it
* takes around 2 rtc_ck clock cycles
*/
ret = readl_relaxed_poll_timeout_atomic(rtc->base + regs->isr,
isr,
(isr & STM32_RTC_ISR_ALRAWF),
10, 100000);
/*每10us轮询一次“RTC_ICSR寄存器“的bit0是否置1;
直到建立“Alarm A写标志“,才可以设置报警时间;
*/
if (ret) {
dev_err(dev, "Alarm update not allowed\n");
goto end;
}
/* Write to Alarm register */
writel_relaxed(alrmar, rtc->base + regs->alrmar);
/*将alrmar的值写入STM32MP1的RTC_ALRMAR寄存器*/
stm32_rtc_alarm_irq_enable(dev, alrm->enabled);
/*
enabled=1,设置使能“ALARM A“和使能“ALARM A“报警时能产生中断;
enabled=0,设置不使能“ALARM A“和不使能“ALARM A“报警时能产生中断;
*/
end:
stm32_rtc_wpr_lock(rtc);/*写错值,会重新激活“RTC寄存器“写保护,不允许写RTC寄存器*/
return ret;
}
static const struct rtc_class_ops stm32_rtc_ops = {
.read_time = stm32_rtc_read_time,
.set_time = stm32_rtc_set_time,
.read_alarm = stm32_rtc_read_alarm,
/*给函数指针赋值,使用stm32_rtc_read_alarm()读RTC报警时间*/
.set_alarm = stm32_rtc_set_alarm,
/*给函数指针赋值,使用stm32_rtc_set_alarm()设置“ALARM A报警时间“*/
.alarm_irq_enable = stm32_rtc_alarm_irq_enable,
/*给函数指针赋值,使用stm32_rtc_alarm_irq_enable(enabled),
enabled=1,设置使能“ALARM A“和使能“ALARM A“报警时能产生中断;
enabled=0,设置不使能“ALARM A“和不使能“ALARM A“报警时能产生中断;
*/
};
/*清除“RTC initialization control and status register“中的事件标志位*/
/*
static void stm32_rtc_clear_events(struct stm32_rtc *rtc,
unsigned int flags)
{
const struct stm32_rtc_registers *regs = &rtc->data->regs;
//Flags are cleared by writing 0 in RTC_ISR
writel_relaxed(readl_relaxed(rtc->base + regs->isr) & ~flags,
rtc->base + regs->isr);
//readl_relaxed()读取STM32MP1的RTC_ICSR寄存器的值
//writel_relaxed()写STM32MP1的RTC_ICSR寄存器
//采用先读后写,清除“RTC initialization control and status register“中的事件标志位
}
*/
/*根据设备树,这个结构没用*/
/*
static const struct stm32_rtc_data stm32_rtc_data = {
.has_pclk = false,
.need_dbp = true,
.has_lsco = false,
.regs = {
.tr = 0x00,
.dr = 0x04,
.cr = 0x08,
.isr = 0x0C,
.prer = 0x10,
.alrmar = 0x1C,
.wpr = 0x24,
.sr = 0x0C, //set to ISR offset to ease alarm management
.scr = UNDEF_REG,
.cfgr = UNDEF_REG,
.verr = UNDEF_REG,
},
.events = {
.alra = STM32_RTC_ISR_ALRAF,
},
.clear_events = stm32_rtc_clear_events,
};
*/
/*根据设备树,这个结构没用*/
/*
static const struct stm32_rtc_data stm32h7_rtc_data = {
.has_pclk = true,
.need_dbp = true,
.has_lsco = false,
.regs = {
.tr = 0x00,
.dr = 0x04,
.cr = 0x08,
.isr = 0x0C,
.prer = 0x10,
.alrmar = 0x1C,
.wpr = 0x24,
.sr = 0x0C, //set to ISR offset to ease alarm management
.scr = UNDEF_REG,
.cfgr = UNDEF_REG,
.verr = UNDEF_REG,
},
.events = {
.alra = STM32_RTC_ISR_ALRAF,
},
.clear_events = stm32_rtc_clear_events,
};
*/
/*将flags写入RTC_SCR寄存器(RTC status clear register)*/
static void stm32mp1_rtc_clear_events(struct stm32_rtc *rtc,unsigned int flags)
{
struct stm32_rtc_registers regs = rtc->data->regs;
/* Flags are cleared by writing 1 in RTC_SCR */
writel_relaxed(flags, rtc->base + regs.scr);
/*将flags写入RTC_SCR寄存器*/
}
/*STM32MP157的RTC寄存器*/
static const struct stm32_rtc_data stm32mp1_data = {
.has_pclk = true,
.need_dbp = false,
.has_lsco = true,
.regs = {
.tr = 0x00,/*RTC_TR寄存器偏移地址*/
.dr = 0x04,/*RTC_DR寄存器偏移地址*/
.cr = 0x18,/*RTC_CR寄存器偏移地址*/
.isr = 0x0C, /*RTC_ICSR寄存器偏移地址,named RTC_ICSR on stm32mp1 */
.prer = 0x10,/*RTC_PRER寄存器偏移地址*/
.alrmar = 0x40,/*RTC_ALRMAR寄存器偏移地址*/
.wpr = 0x24,/*RTC_WPR寄存器偏移地址*/
.sr = 0x50,/*RTC_SR寄存器偏移地址*/
.scr = 0x5C,/*RTC_SCR寄存器偏移地址*/
.cfgr = 0x60,/*RTC_CFGR寄存器偏移地址*/
.verr = 0x3F4,/*RTC_VERR寄存器偏移地址*/
},
.events = {
.alra = STM32_RTC_SR_ALRA,/*将events.alra初始化为0*/
},
.clear_events = stm32mp1_rtc_clear_events,
/*将flags写入RTC_SCR寄存器(RTC status clear register)*/
};
/* 匹配列表 */
//驱动中的compatible属性要和和设备树中的compatible属性相匹配。
static const struct of_device_id stm32_rtc_of_match[] = {
/*{ .compatible = "st,stm32-rtc", .data = &stm32_rtc_data },
{ .compatible = "st,stm32h7-rtc", .data = &stm32h7_rtc_data },*/
{ .compatible = "st,stm32mp1-rtc", .data = &stm32mp1_data },
/*"st,stm32mp1-rtc"在设备树中*/
{ /*这是一个空元素,在编写of_device_id时最后一个元素一定要为空*/
/* Sentinel */
}
};
MODULE_DEVICE_TABLE(of, stm32_rtc_of_match);
/**初始化STM32MP1的rtc寄存器*/
static int stm32_rtc_init(struct platform_device *pdev,
struct stm32_rtc *rtc)
{
const struct stm32_rtc_registers *regs = &rtc->data->regs;
unsigned int prer, pred_a, pred_s, pred_a_max, pred_s_max, cr;
unsigned int rate;
int ret = 0;
rate = clk_get_rate(rtc->rtc_ck);/*获得rtc->rtc_ck时钟源的当前时钟频率(HZ)*/
/* Find prediv_a and prediv_s to obtain the 1Hz calendar clock */
pred_a_max = STM32_RTC_PRER_PRED_A >> STM32_RTC_PRER_PRED_A_SHIFT;
pred_s_max = STM32_RTC_PRER_PRED_S >> STM32_RTC_PRER_PRED_S_SHIFT;
for (pred_a = pred_a_max; pred_a + 1 > 0; pred_a--) {
pred_s = (rate / (pred_a + 1)) - 1;
if (((pred_s + 1) * (pred_a + 1)) == rate)
break;
}
/*
* Can't find a 1Hz, so give priority to RTC power consumption
* by choosing the higher possible value for prediv_a
*/
if ((pred_s > pred_s_max) || (pred_a > pred_a_max)) {
pred_a = pred_a_max;
pred_s = (rate / (pred_a + 1)) - 1;
dev_warn(&pdev->dev, "rtc_ck is %s\n",
(rate < ((pred_a + 1) * (pred_s + 1))) ?
"fast" : "slow");
}
stm32_rtc_wpr_unlock(rtc);/*RTC写保护寄存器RTC_WPR*/
ret = stm32_rtc_enter_init_mode(rtc);/*令RTC进入初始化模式*/
if (ret) {
dev_err(&pdev->dev,
"Can't enter in init mode. Prescaler config failed.\n");
goto end;
}
prer = (pred_s << STM32_RTC_PRER_PRED_S_SHIFT) & STM32_RTC_PRER_PRED_S;
writel_relaxed(prer, rtc->base + regs->prer);
prer |= (pred_a << STM32_RTC_PRER_PRED_A_SHIFT) & STM32_RTC_PRER_PRED_A;
writel_relaxed(prer, rtc->base + regs->prer);
/* Force 24h time format */
cr = readl_relaxed(rtc->base + regs->cr);
cr &= ~STM32_RTC_CR_FMT;
writel_relaxed(cr, rtc->base + regs->cr);
stm32_rtc_exit_init_mode(rtc);/*令RTC进入“自由运行模式“*/
ret = stm32_rtc_wait_sync(rtc);/*令“日历影子寄存器“同步*/
end:
stm32_rtc_wpr_lock(rtc);/*写错值,会重新激活“RTC寄存器“写保护,不允许写RTC寄存器*/
return ret;
}
/*platform的probe函数为stm32_rtc_probe()*/
static int stm32_rtc_probe(struct platform_device *pdev)
{
struct stm32_rtc *rtc;
const struct stm32_rtc_registers *regs;
struct resource *res;
int ret;
rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
/*向内核申请一块内存,当设备驱动程序被卸载时,内存会被自动释放*/
if (!rtc)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
/*platform_get_resource()函数从设备树中获取到RTC外设寄存器基地址*/
rtc->base = devm_ioremap_resource(&pdev->dev, res);
/*devm_ioremap_resource()函数完成内存映射,得到RTC外设寄存器物理基地址对应的虚拟地址*/
if (IS_ERR(rtc->base))
return PTR_ERR(rtc->base);
rtc->data = (struct stm32_rtc_data *)
of_device_get_match_data(&pdev->dev);
regs = &rtc->data->regs;
if (rtc->data->need_dbp) {
rtc->dbp = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
"st,syscfg");
if (IS_ERR(rtc->dbp)) {
dev_err(&pdev->dev, "no st,syscfg\n");
return PTR_ERR(rtc->dbp);
}
ret = of_property_read_u32_index(pdev->dev.of_node, "st,syscfg",
1, &rtc->dbp_reg);
if (ret) {
dev_err(&pdev->dev, "can't read DBP register offset\n");
return ret;
}
ret = of_property_read_u32_index(pdev->dev.of_node, "st,syscfg",
2, &rtc->dbp_mask);
if (ret) {
dev_err(&pdev->dev, "can't read DBP register mask\n");
return ret;
}
}
if (!rtc->data->has_pclk) {
rtc->pclk = NULL;
rtc->rtc_ck = devm_clk_get(&pdev->dev, NULL);
} else {
rtc->pclk = devm_clk_get(&pdev->dev, "pclk");
if (IS_ERR(rtc->pclk)) {
if (PTR_ERR(rtc->pclk) != -EPROBE_DEFER)
dev_err(&pdev->dev, "no pclk clock");
return PTR_ERR(rtc->pclk);
}
rtc->rtc_ck = devm_clk_get(&pdev->dev, "rtc_ck");
}
if (IS_ERR(rtc->rtc_ck)) {
if (PTR_ERR(rtc->pclk) != -EPROBE_DEFER)
dev_err(&pdev->dev, "no rtc_ck clock");
return PTR_ERR(rtc->rtc_ck);
}
if (rtc->data->has_pclk) {
ret = clk_prepare_enable(rtc->pclk);
if (ret)
return ret;
}
ret = clk_prepare_enable(rtc->rtc_ck);/*使能预分频器时钟*/
if (ret)
goto err;
if (rtc->data->need_dbp)
regmap_update_bits(rtc->dbp, rtc->dbp_reg,
rtc->dbp_mask, rtc->dbp_mask);
/*
* After a system reset, RTC_ISR.INITS flag can be read to check if
* the calendar has been initialized or not. INITS flag is reset by a
* power-on reset (no vbat, no power-supply). It is not reset if
* rtc_ck parent clock has changed (so RTC prescalers need to be
* changed). That's why we cannot rely on this flag to know if RTC
* init has to be done.
*/
ret = stm32_rtc_init(pdev, rtc);
/**初始化STM32MP1的rtc寄存器*/
if (ret)
goto err;
rtc->irq_alarm = platform_get_irq(pdev, 0);
/*获取设备树的中断号*/
if (rtc->irq_alarm <= 0) {
ret = rtc->irq_alarm;
goto err;
}
ret = device_init_wakeup(&pdev->dev, true);
if (ret)
goto err;
ret = dev_pm_set_wake_irq(&pdev->dev, rtc->irq_alarm);
if (ret)
goto err;
platform_set_drvdata(pdev, rtc);
rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name,&stm32_rtc_ops, THIS_MODULE);
/*注册RTC类设备*/
if (IS_ERR(rtc->rtc_dev)) {
ret = PTR_ERR(rtc->rtc_dev);
dev_err(&pdev->dev, "rtc device registration failed, err=%d\n",
ret);
goto err;
}
/* Handle RTC alarm interrupts */
ret = devm_request_threaded_irq(&pdev->dev, rtc->irq_alarm, NULL,
stm32_rtc_alarm_irq, IRQF_ONESHOT,
pdev->name, rtc);
/*RTC的“Alarm A”中断服务函数为stm32_rtc_alarm_irq()*/
if (ret) {
dev_err(&pdev->dev, "IRQ%d (alarm interrupt) already claimed\n",
rtc->irq_alarm);
goto err;
}
if (rtc->data->has_lsco) {
ret = of_property_read_s32(pdev->dev.of_node,
"st,lsco", &rtc->lsco);
if (!ret) {
ret = stm32_rtc_clk_lsco_register(pdev);
if (ret)
dev_warn(&pdev->dev,
"LSCO clock registration failed: %d\n",
ret);
} else {
rtc->lsco = ret;
dev_dbg(&pdev->dev, "No LSCO clock: %d\n", ret);
}
}
/*
* If INITS flag is reset (calendar year field set to 0x00), calendar
* must be initialized
*/
if (!(readl_relaxed(rtc->base + regs->isr) & STM32_RTC_ISR_INITS))
dev_warn(&pdev->dev, "Date/Time must be initialized\n");
if (regs->verr != UNDEF_REG) {
u32 ver = readl_relaxed(rtc->base + regs->verr);
dev_info(&pdev->dev, "registered rev:%d.%d\n",
(ver >> STM32_RTC_VERR_MAJREV_SHIFT) & 0xF,
(ver >> STM32_RTC_VERR_MINREV_SHIFT) & 0xF);
}
return 0;
err:
if (rtc->data->has_pclk)
clk_disable_unprepare(rtc->pclk);
clk_disable_unprepare(rtc->rtc_ck);
if (rtc->data->need_dbp)
regmap_update_bits(rtc->dbp, rtc->dbp_reg, rtc->dbp_mask, 0);
dev_pm_clear_wake_irq(&pdev->dev);
device_init_wakeup(&pdev->dev, false);
return ret;
}
/*platform的remove函数为stm32_rtc_remove()*/
static int stm32_rtc_remove(struct platform_device *pdev)
{
struct stm32_rtc *rtc = platform_get_drvdata(pdev);
const struct stm32_rtc_registers *regs = &rtc->data->regs;
unsigned int cr;
if (!IS_ERR_OR_NULL(rtc->clk_lsco))
clk_unregister_gate(rtc->clk_lsco);
/* Disable interrupts */
stm32_rtc_wpr_unlock(rtc);/*“RTC寄存器“解锁,允许写入*/
cr = readl_relaxed(rtc->base + regs->cr);
cr &= ~STM32_RTC_CR_ALRAIE;
writel_relaxed(cr, rtc->base + regs->cr);
stm32_rtc_wpr_lock(rtc);/*写错值,会重新激活“RTC寄存器“写保护,不允许写RTC寄存器*/
clk_disable_unprepare(rtc->rtc_ck);
if (rtc->data->has_pclk)
clk_disable_unprepare(rtc->pclk);
/* Enable backup domain write protection if needed */
if (rtc->data->need_dbp)
regmap_update_bits(rtc->dbp, rtc->dbp_reg, rtc->dbp_mask, 0);
dev_pm_clear_wake_irq(&pdev->dev);
device_init_wakeup(&pdev->dev, false);
return 0;
}
#ifdef CONFIG_PM_SLEEP
/*RTC暂停工作*/
static int stm32_rtc_suspend(struct device *dev)
{
struct stm32_rtc *rtc = dev_get_drvdata(dev);
if (rtc->data->has_pclk)
clk_disable_unprepare(rtc->pclk);
return 0;
}
/*RTC恢复工作*/
static int stm32_rtc_resume(struct device *dev)
{
struct stm32_rtc *rtc = dev_get_drvdata(dev);
int ret = 0;
if (rtc->data->has_pclk) {
ret = clk_prepare_enable(rtc->pclk);
if (ret)
return ret;
}
ret = stm32_rtc_wait_sync(rtc);/*令“日历影子寄存器“同步*/
if (ret < 0)
return ret;
return ret;
}
#endif
static const struct dev_pm_ops stm32_rtc_pm_ops = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(stm32_rtc_suspend, stm32_rtc_resume)
};
/*标准的platform驱动框架*/
static struct platform_driver stm32_rtc_driver = {
.probe = stm32_rtc_probe,/*platform的probe函数为stm32_rtc_probe()*/
.remove = stm32_rtc_remove,/*platform的remove函数为stm32_rtc_remove()*/
.driver = {
.name = DRIVER_NAME,/* 驱动名字,用于和设备匹配 */
.pm = &stm32_rtc_pm_ops,
.of_match_table = stm32_rtc_of_match,/*设备树匹配表*/
},
};
module_platform_driver(stm32_rtc_driver);
//stm32_rtc_driver为platform_driver结构
//用来向linux内核注册platform驱动
MODULE_ALIAS("platform:" DRIVER_NAME);
MODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>");//添加作者名字
MODULE_DESCRIPTION("STMicroelectronics STM32 Real Time Clock driver");//模块介绍
MODULE_LICENSE("GPL v2");//LICENSE采用“GPL协议”
4、编译设备树
①打开VSCode中的终端,输入“make uImage dtbs LOADADDR=0XC2000040 -j8回车”,执行编译“Image”和“dtbs”,并指定装载的起始地址为0XC2000040,j8表示指定采用8线程执行。“make dtbs”,用来指定编译设备树。见下图:
②输入“ls arch/arm/boot/uImage -l”
查看是否生成了新的“uImage”文件
③输入“ls arch/arm/boot/dts/stm32mp157d-atk.dtb -l”
查看是否生成了新的“stm32mp157d-atk.dtb”文件
4)、拷贝输出的文件:
①输入“cp arch/arm/boot/uImage /home/zgq/linux/atk-mp1/linux/bootfs/ -f回车”,执行文件拷贝,准备烧录到EMMC;
②输入“cp arch/arm/boot/dts/stm32mp157d-atk.dtb /home/zgq/linux/atk-mp1/linux/bootfs/ -f回车”,执行文件拷贝,准备烧录到EMMC
③输入“cp arch/arm/boot/uImage /home/zgq/linux/tftpboot/ -f回车”,执行文件拷贝,准备从tftp下载;
④输入“cp arch/arm/boot/dts/stm32mp157d-atk.dtb /home/zgq/linux/tftpboot/ -f回车”,执行文件拷贝,准备从tftp下载;
⑤输入“ls -l /home/zgq/linux/atk-mp1/linux/bootfs/回车”,查看“/home/zgq/linux/atk-mp1/linux/bootfs/”目录下的所有文件和文件夹
⑥输入“ls -l /home/zgq/linux/tftpboot/回车”,查看“/home/zgq/linux/tftpboot/”目录下的所有文件和文件夹
⑦输入“chmod 777 /home/zgq/linux/tftpboot/stm32mp157d-atk.dtb回车”
给“stm32mp157d-atk.dtb”文件赋予可执行权限
⑧输入“chmod 777 /home/zgq/linux/tftpboot/uImage回车” ,给“uImage”文件赋予可执行权限
⑨输入“ls /home/zgq/linux/tftpboot/ -l回车”,查看“/home/zgq/linux/tftpboot/”目录下的所有文件和文件夹
5、测试
①给开发板上电,启动开发板,从网络下载程序
②输入“root”
③输入“date回车”,查看时间。
④输入“date --help回车”,查看date命令如何设置系统时间。
⑤输入“date -s "2025-02-09 13:16:00"回车”,修改当前时间,但还没有写入到STM32MP1内部RTC里面或其他的RTC芯片里面,因此,系统重启以后时间又会丢失。
⑥输入“date回车”,查看时间。
⑦输入“hwclock -w回车”,将当前的系统时间写入到RTC里。
⑧重启开发板
相关文章:
Linux第106步_Linux内核RTC驱动实验
1、了解rtc_device结构体 1)、打开“include/linux/rtc.h” rtc_class_ops是需要用户根据所使用的RTC设备编写的,其结构体如下: struct rtc_class_ops { int (*ioctl)(struct device *, unsigned int, unsigned long);/*函数指针ioctl*/ int (*read_time)(struct device *,…...
《XSS跨站脚本攻击》
一、XSS简介 XSS全称(Cross Site Scripting)跨站脚本攻击,为了避免和CSS层叠样式表名称冲突,所以改为了XSS,是最常见的Web应用程序安全漏洞之一,位于OWASP top 10 2013/2017年度分别为第三名和第七名&…...
康谋方案 | BEV感知技术:多相机数据采集与高精度时间同步方案
随着自动驾驶技术的快速发展,车辆准确感知周围环境的能力变得至关重要。BEV(Birds-Eye-View,鸟瞰图)感知技术,以其独特的视角和强大的数据处理能力,正成为自动驾驶领域的一大研究热点。 一、BEV感知技术概…...
堆详解及C语言实现
堆结构与最大堆详解:从原理到C语言实现 1. 堆结构概述 1.1 定义与性质 堆(Heap)是一种特殊的完全二叉树数据结构,满足以下性质: 结构性:完全二叉树结构有序性:每个结点的值都≥(…...
基于STM32的智能鱼缸水质净化系统设计
🤞🤞大家好,这里是5132单片机毕设设计项目分享,今天给大家分享的是智能鱼缸水质净化系统。 目录 1、设计要求 2、系统功能 3、演示视频和实物 4、系统设计框图 5、软件设计流程图 6、原理图 7、主程序 8、总结 1、设计要求…...
python:csv文件批量导入mysql
1.导入sql文件到数据库中 mysql -u username -p要先创建一个空的数据库 CREATE DATABASE your_database_name;USE your_database_name;导入sql文件 source /path/to/your/file.sql;查看某个表格的结构,为后续数据插入做准备 DESCRIBE table_name;2.插入假数据到对应…...
第三十二周:Informer学习笔记
目录 摘要Abstract1 Informer1.1 预备知识1.2 模型框架1.3 实验分析 总结 摘要 本周学习的主要内容是Informer模型,Informer是一种专为长序列时间序列预测(LSTF) 设计的Transformer模型。相较于传统的Transformer,Informer采用Pr…...
计算机视觉核心任务
1. 计算机视频重要分类 计算机视觉的重要任务可以大致分为以下几类: 1. 图像分类(Image Classification) 识别图像属于哪个类别,例如猫、狗、汽车等。 应用场景:物品识别、人脸识别、医疗影像分类。代表模型&#…...
【python】matplotlib(animation)
文章目录 1、matplotlib.animation1.1、FuncAnimation1.2、修改 matplotlib 背景 2、matplotlib imageio2.1、折线图2.2、条形图2.3、散点图 3、参考 1、matplotlib.animation 1.1、FuncAnimation matplotlib.animation.FuncAnimation 是 Matplotlib 库中用于创建动画的一个…...
【Linux网络编程】之守护进程
【Linux网络编程】之守护进程 进程组进程组的概念组长进程 会话会话的概念会话ID 控制终端控制终端的概念控制终端的作用会话、终端、bash三者的关系 前台进程与后台进程概念特点查看当前终端的后台进程前台进程与后台进程的切换 作业控制相关概念作业状态(一般指后…...
Vue.js如何根据访问路径切换页面
Vue Router 在前端工程中,路由指的是,根据不同的访问路径,展示不同组件的内容。 Vue Router是Vue.js的官方路由。 Vue Router介绍。 要使用vue Router,得先安装 npm install vue-router4这里的4,指的是第4个版本 在s…...
Vue与Konva:解锁Canvas绘图的无限可能
前言 在现代Web开发中,动态、交互式的图形界面已成为提升用户体验的关键要素。Vue.js,作为一款轻量级且高效的前端框架,凭借其响应式数据绑定和组件化开发模式,赢得了众多开发者的青睐。而当Vue.js邂逅Konva.js,两者结…...
collabora online+nextcloud+mariadb在线文档协助
1、环境 龙蜥os 8.9 docker 2、安装docker dnf -y install dnf-plugins-core dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sed -i shttps://download.docker.comhttps://mirrors.tuna.tsinghua.edu.cn/docker-ce /etc/yum.repos.…...
linux基础命令1
1、linux目录结构——树型结构 根目录:/ 用户主目录(家目录):~或者 /home/edu 根目录下常见的文件夹: 2、常见的命令 1、pwd 查看当前目录 cd 切换目录 cd ~ 切换到家目录 2、ls 查看当前目录的文件信息 语法:ls [选项] [参…...
[LVGL] 在VC_MFC中移植LVGL
前言: 0. 在MFC中开发LVGL的优点是可以用多个Window界面做辅助扩展【类似GUIguider】 1.本文基于VC2022-MFC单文档框架移植lvgl8 2. gitee上下载lvgl8.3 源码,并将其文件夹改名为lvgl lvgl: LVGL 是一个开源图形库,提供您创建具有易于使用…...
Spring Boot整合MQTT
MQTT是基于代理的轻量级的消息发布订阅传输协议。 1、下载安装代理 进入mosquitto下载地址:Download | Eclipse Mosquitto,进行下载,以win版本为例 下载完成后,在本地文件夹找到下载的代理安装文件 使用管理员身份打开安装 安装…...
elasticsearch实战三 elasticsearch与mysql数据实时同步
一 介绍 elasticsearch数据不是一直不变的,需要与mysql、oracle等数据库的数据做同步。 本博客里涉及到的项目地址:https://www.aliyundrive.com/s/7bRWpTYsxWV 方案一: 同步调用,即操作mysql数据后,接着操作elastic…...
netcore openTelemetry+prometheus+grafana
一、netcore项目 二、openTelemetry 三、prometheus 四、grafana添加Dashborad aspire/src/Grafana/dashboards at main dotnet/aspire GitHub 导入:aspnetcore.json和aspnetcore-endpoint.json 效果:...
StochSync:可在任意空间中生成360°全景图和3D网格纹理
StochSync方法可以用于在任意空间中生成图像,尤其是360全景图和3D网格纹理。该方法利用了预训练的图像扩散模型,以实现零-shot生成,消除了对新数据收集和单独训练生成模型的需求。StochSync 结合了 Diffusion Synchronization(DS&…...
MybatisPlus较全常用复杂查询引例(limit、orderby、groupby、having、like...)
MyBatis-Plus 是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。以下是 MyBatis-Plus 中常用复杂查询(如 LIMIT、ORDER BY、GROUP BY、HAVING、LIKE 等)的引例: 1. 环境准备…...
大数据项目2:基于hadoop的电影推荐和分析系统设计和实现
前言 大数据项目源码资料说明: 大数据项目资料来自我多年工作中的开发积累与沉淀。 我分享的每个项目都有完整代码、数据、文档、效果图、部署文档及讲解视频。 可用于毕设、课设、学习、工作或者二次开发等,极大提升效率! 1、项目目标 本…...
win10的Unet项目导入阿里云训练
win10配置文件 annotated-types0.7.0 certifi2024.12.14 charset-normalizer3.4.1 click8.1.8 colorama0.4.6 contourpy1.1.1 cycler0.12.1 docker-pycreds0.4.0 eval_type_backport0.2.2 filelock3.16.1 fonttools4.55.3 fsspec2024.12.0 gitdb4.0.12 GitPython3.1.44 idna3.…...
Linux(20)——调度作业
目录 一、调度延迟的用户作业: 1、延迟的用户作业: 2、查看延迟的用户作业: 3、从计划中删除作业: 二、调度周期性用户作业: 1、周期性用户作业: 2、调度周期性用户作业: 3、用户作业格…...
DeepSeek赋能Vue:打造超丝滑进度条开发指南
前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏关注哦 💕 目录 Deep…...
在CT107D单片机综合训练平台上,8个数码管分别单独依次显示0~9的值,然后所有数码管一起同时显示0~F的值,如此往复。
题目:在CT107D单片机综合训练平台上,8个数码管分别单独依次显示0~9的值,然后所有数码管一起同时显示0~F的值,如此往复。 延时函数分析LED首先实现8个数码管单独依次显示0~9的数字所有数码管一起同时显示0~F的值,如此往…...
清除el-table选中状态 clearSelection
如何在Vue应用中使用Element UI的el-table组件,通过this.$refs.multipleTable.clearSelection()方法来清除所有选中行的状态。适合前端开发者了解表格组件的交互操作。 // el-table绑定ref<el-table selection-change"selsChange" ref"multipl…...
【算法】动态规划专题⑥ —— 完全背包问题 python
目录 前置知识进入正题模板 前置知识 【算法】动态规划专题⑤ —— 0-1背包问题 滚动数组优化 完全背包问题是动态规划中的一种经典问题,它与0-1背包问题相似,但有一个关键的区别:在完全背包问题中,每种物品都有无限的数量可用。…...
论文笔记-COLING2025-LLMTreeRec
论文笔记-COLING2025-LLMTreeRec: Unleashing the Power of Large Language Models for Cold-Start Recommendations LLMTreeRec: 释放大语言模型在冷启动推荐中的力量摘要1.引言2.框架2.1项目树构建2.2以LLM为中心的基于树的推荐2.2.1推荐链策略2.2.2检索策略 3.实验3.1实验设…...
c++ haru生成pdf输出饼图
#define PI 3.14159265358979323846 // 绘制饼图的函数 void draw_pie_chart(HPDF_Doc pdf, HPDF_Page page, float *data, int data_count, float x, float y, float radius) { float total 0; int i; // 计算数据总和 for (i 0; i < data_count; i) { tot…...
【Unity】Unity中物体的static属性作用
Unity中物体的static属性主要用于优化游戏性能和简化渲染过程。 Unity中物体的static属性的作用 优化渲染性能:当物体被标记为static时,Unity会在游戏运行时将其视为静止的物体,这意味着这些物体的渲染信息不会随着每一帧的更新而变化…...
Rust 测试指南:从入门到进阶
1. 测试基础:#[test] 属性 Rust 测试的基本单位是函数。只要在一个函数前面标注 #[test] 属性,那么在运行 cargo test 时,Rust 会自动识别并执行它。例如,新建一个库工程 adder,cargo new adder --lib,在 …...
Elasticsearch 生产集群部署终极方案
Elasticsearch 集群部署 1.集群部署1.1 新增用户1.2 优化操作系统1.3 JDK1.4 elasticsearch1.5 开机自启动 2.安全认证功能2.1 生成CA证书2.2 生成密钥2.3 上传至其他节点2.4 修改属主、属组2.5 配置文件添加参数2.6 各节点添加密钥库密码2.7 设置用户密码 1.集群部署 1.1 新增…...
电路笔记(元器件):AD 5263数字电位计(暂记)
AD5263 是四通道、15 V、256位数字电位计,可通过SPI/I2C配置具体电平值。 配置模式: W引脚作为电位器的抽头,可在A-B之间调整任意位置的电阻值。也可将W与A(或B)引脚短接,A-W间的电阻总是0欧姆,通过数字接口调整电位器…...
如何在电脑后台定时进行自动截图?自动截图后如何快捷保存?如何远程查看?
7-2 有时候需要对电脑的屏幕进行在后台连续性的截图保存,并且要可以远程查看,无界面,以达到对电脑的使用过程进行完全了解的目的,一般用于对小孩使用电脑的掌握,如果父母在外地,不方便就近管理,…...
解决react中函数式组件usestate异步更新
问题:在点击modal组件确认后 调用后端接口,使用setstateone(false)使modal组件关闭,但是设置后关闭不了,在设置setstateone(false)前后打印出了对应的stateone都为true,但…...
skia-macos源码编译
1、下载git-hub 源码 2、下载依赖库 3、编译,注意选项 bin/gn gen out/release --args"is_official_buildfalse skia_use_system_expatfalse skia_use_system_icufalse skia_use_libjpeg_turbofalse skia_use_system_libpngfalse skia_use_system_libwebpfal…...
本地部署DeepSeek(Mac版本,带图形化操作界面)
一、下载安装:Ollama 官网下载:Download Ollama on macOS 二、安装Ollama 1、直接解压zip压缩包,解压出来就是应用程序 2、直接将Ollama拖到应用程序中即可 3、启动终端命令验证 # 输入 ollama 代表已经安装成功。 4、下载模型 点击模型…...
离线统信系统的python第三方库批量安装流程
一、关于UOS本机 操作系统:UOS(基于Debian的Linux发行版) CPU:海光x86 二、具体步骤 1、在联网的电脑上用控制台的pip命令批量下载指定版本的第三方库 方法A cd <目标位置的绝对路径> pip download -d . --platform many…...
群晖安装Gitea
安装Docker Docker运行Gitea 上传gitea包,下载地址:https://download.csdn.net/download/hmxm6/90360455 打开docker 点击印象,点击新增,从文件添加 点击启动 可根据情况,进行高级设置,没有就下一步 点击应…...
jmeter逻辑控制器9
1,简单控制器2,录制控制器3,循环控制器4,随机控制器5,随机顺序控制器6,if控制器7,模块控制器8,Include控制器9,事物控制器本文永久更新地址: 1,简单控制器 不…...
Spring统一修改RequestBody
我们编写RestController时,有可能多个接口使用了相同的RequestBody,在一些场景下需求修改传入的RequestBody的值,如果是每个controller中都去修改,代码会比较繁琐,最好的方式是在一个地方统一修改,比如将he…...
自动化xpath定位元素(附几款浏览器xpath插件)
在 Web 自动化测试、数据采集、前端调试中,XPath 仍然是不可或缺的技能。虽然 CSS 选择器越来越强大,但面对复杂 DOM 结构时,XPath 仍然更具灵活性。因此,掌握 XPath,不仅能提高自动化测试的稳定性,还能在爬…...
go-elasticsearch创建ik索引并进行查询操作
es-go client引入gomod go get github.com/elastic/go-elasticsearch/v8latest连接es服务器(不经过安全校验) cfg : elasticsearch.Config{Addresses: []string{"http://localhost:9200",}, } es, err : elasticsearch.NewClient(cfg) if err ! nil {pa…...
【东莞常平】戴尔R710服务器不开机维修分享
1:2025-02-06一位老客户的朋友刚开工公司ERP服务器一台戴尔老服务器故障无法开机,于是经老客户介绍找到我们。 2:服务器型号是DELL PowerEdge R710 这个服务器至少也有15年以上的使用年限了。 3:客户反馈的故障问题为:…...
rebase和merge
rebase 和merge区别: rebase变基,改变基底:rebase会抹去提交记录。 git pull 默认merge,git pull --rebase 变基 rebase C、D提交属于feature分支,是基于master分支,在B提交额外拉出来的,当…...
SSA-TCN麻雀算法优化时间卷积神经网络时间序列预测未来Matlab实现
SSA-TCN麻雀算法优化时间卷积神经网络时间序列预测未来Matlab实现 目录 SSA-TCN麻雀算法优化时间卷积神经网络时间序列预测未来Matlab实现预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现SSA-TCN麻雀算法优化时间卷积神经网络时间序列预测未来(优…...
2025牛客寒假算法基础集训营5(补题)
C 小L的位运算 显然,如果两次反置的价格小于等于交换的价格,那么直接全部反置就好了。 反之,由于交换一定低于两次反置,我们尽可能用交换来消去不正确的位置。不正确的位置类型只有00,01,10,11&…...
Kotlin Android 环境搭建
Kotlin Android 环境搭建 引言 随着移动应用的日益普及,Android 开发成为了一个热门的技术领域。Kotlin 作为一种现代的编程语言,因其简洁、安全、互操作性强等特点,被越来越多的开发者所喜爱。本文将详细介绍 Kotlin Android 环境搭建的步骤,帮助您快速上手 Kotlin Andr…...
DeepSeek图解10页PDF
以前一直在关注国内外的一些AI工具,包括文本型、图像类的一些AI实践,最近DeepSeek突然爆火,从互联网收集一些资料与大家一起分享学习。 本章节分享的文件为网上流传的DeepSeek图解10页PDF,免费附件链接给出。 1 本地 1 本地部…...
Kafka中的KRaft算法
我们之前的Kafka值依赖于Zookeeper注册中心来启动的,往里面注册我们节点信息 Kafka是什么时候不依赖Zookeeper节点了 在Kafka2.8.0开始就可以不依赖Zookeeper了 可以用KRaft模式代替Zookeeper管理Kafka集群 KRaft Controller和KRaft Leader的关系 两者关系 Lea…...