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里。

⑧重启开发板

相关推荐
云梦谭6 分钟前
ubuntu server环境下使用mitmproxy代理
linux·mitmproxy
yqcoder7 分钟前
centos 和 ubuntu 区别
linux·ubuntu·centos
易水寒陈27 分钟前
FreeRTOS的事件组
stm32·单片机
鹏展-penggeon8 小时前
STM32学习笔记【大学生电子设计竞赛】【嵌入式】【标准库学习】【HAL库学习】
stm32
年*D-清仁9 小时前
STM32+Proteus+DS18B20数码管仿真实验
stm32·单片机·proteus
想要成为糕手。9 小时前
stm32-wifi模块
stm32·嵌入式硬件·php
Hi-Dison10 小时前
VMware 虚拟机中 Ubuntu 20 网络不通问题解决总结
linux·网络·ubuntu
kongba00711 小时前
c语言样式主题 清爽风格 代码色彩 keil风格 适合单片机开发GD32 STM32等 cursor或者vscode 的settings.json文件
c语言·vscode·stm32·单片机
小镇敲码人11 小时前
【Linux网络编程】之守护进程
linux·运维·网络