linux内核提供一系列devm_*开头的函数,在已有的函数基础增加管理,可以在驱动卸载时自动释放资源,无需再手动释放,关于devm的相关接口可以详见Linux/Documentation/driver-api/driver-model/devres.rst。
//函数原型
#define alloc_candev(sizeof_priv, echo_skb_max) \
alloc_candev_mqs(sizeof_priv, echo_skb_max, 1, 1)
struct net_device *alloc_candev_mqs(int sizeof_priv, unsigned int echo_skb_max,
unsigned int txqs, unsigned int rxqs);
//参数
sizeof_priv: 私有数据长度
echo_skb_max: socket buffer数组个数
txqs: 发送队列个数
rxqs: 接收队列个数
返回: 成功获取can设备管理结构,失败返回NULL指针
//例程
struct net_device *dev;
dev = alloc_candev(sizeof(struct flexcan_priv), 1);
//函数原型
struct clk *of_clk_get(struct device_node *np, int index)
//参数
np: 指向设备device_node的指针
index: 在dts中clks属性中的索引
返回: 成功获取时钟资源,失败返回对应错误码
//例程
clk[i].clk = of_clk_get(np, i);
//函数原型
struct clk *devm_clk_get(struct device *dev, const char *id)
{
return __devm_clk_get(dev, id, clk_get, NULL, NULL);
}
struct clk *clk_get(struct device *dev, const char *con_id)
//参数
dev: 总线匹配的设备
id: 需要获取的clocks内属性的别名,一般由clock-names定义,没有clock-names属性clocks内只有一个,此时对应NULL。
返回: 成功获取的时钟资源,可用于模块时钟管理(使能,关闭),失败返回对应错误码指针ERR_PTR(-err)
//例程
//时钟在设备树的说明
i2c1: i2c@21a0000 {
//......
clocks = <&clks IMX6UL_CLK_I2C1>;
}
i2c_imx->clk = devm_clk_get(&pdev->dev, NULL);
//函数原型
struct clk *devm_clk_get_enabled(struct device *dev, const char *id)
{
return __devm_clk_get(dev, id, clk_get,
clk_prepare_enable, clk_disable_unprepare);
}
//参数
dev: 总线匹配的设备
id: 需要获取的clocks内属性的别名,一般由clock-names定义,没有clock-names属性clocks内只有一个,此时对应NULL。
返回: 成功则获取时钟资源结构,失败返回NULL指针
//例程
pvt->clk = devm_clk_get_enabled(dev, NULL);
//函数原型
struct clk *devm_clk_get_optional(struct device *dev, const char *id)
{
return __devm_clk_get(dev, id, clk_get_optional, NULL, NULL);
}
clk *clk_get_optional(struct device *dev, const char *id);
//参数
dev: 总线匹配的设备
id: 需要获取的clocks内属性的别名,一般由clock-names定义,没有clock-names属性clocks内只有一个,此时对应NULL。
返回: 成功则获取时钟资源结构,失败返回NULL指针
//例程
//时钟在设备树的说明
i2c1: i2c@21a0000 {
//......
clocks = <&clks IMX6UL_CLK_I2C1>;
}
i2c_imx->clk = devm_clk_get_optional(&pdev->dev, NULL);
//函数原型
void devm_clk_put(struct device *dev, struct clk *clk)
//参数
dev: 总线匹配的设备
clk: 获取的clocks时钟对象(对应设备树中的clocks定义)
//例程
devm_clk_put(&pdev->dev, clk);
//函数原型
int __must_check devm_clk_bulk_get(struct device *dev, int num_clks,
struct clk_bulk_data *clks)
{
return __devm_clk_bulk_get(dev, num_clks, clks, false);
}
//参数
dev: 管理资源的设备,一般为总线匹配的设备
num_clks: 获取时钟句柄的数目
clks: clk参数列表,分别对应时钟id和时钟句柄
struct clk_bulk_data {
const char *id;
struct clk *clk;
};
返回: 成功返回0,其它返回错误码
//例程
ret = devm_clk_bulk_get(&pdev->dev, msm_host->num_bus_clks, msm_host->bus_clks);
//函数原型
int __must_check devm_clk_bulk_get_all(struct device *dev, struct clk_bulk_data **clks)
//参数
dev: 管理资源的设备,一般为总线匹配的设备
clks: 指向时钟资源指针的指针
struct clk_bulk_data {
const char *id;
struct clk *clk;
};
返回: 成功返回0,其它返回错误码
//例程
rc = devm_clk_bulk_get_all(dev, &hpriv->clks);
//函数原型
int __must_check devm_clk_bulk_get_optional(struct device *dev, int num_clks,
struct clk_bulk_data *clks)
{
return __devm_clk_bulk_get(dev, num_clks, clks, true);
}
//参数
dev: 管理资源的设备,一般为总线匹配的设备
num_clks: 获取时钟句柄的数目
clks: clk参数列表,分别对应时钟id和时钟句柄
struct clk_bulk_data {
const char *id;
struct clk *clk;
};
返回: 成功返回0,其它返回错误码
//例程
ret = devm_clk_bulk_get_optional(&pdev->dev, num_clocks, bulk);
//函数原型
struct clk *devm_get_clk_from_child(struct device *dev,
struct device_node *np, const char *con_id)
//参数
dev: 管理资源的设备,一般为总线匹配的设备
np: 指向device_node的指针
con_id: 子节点时钟信息别名
返回: 成功返回时钟句柄,失败返回错误码
//例程
qphy->pipe_clk = devm_get_clk_from_child(dev, np, NULL);
//函数原型
int clk_set_parent(struct clk *clk, struct clk *parent)
//参数
clk: 准备操作的时钟句柄
parent: 父时钟的时钟句柄
返回: 0表示成功,其它表示设置失败
//例程
clk_set_parent(pll1_bypass_clk, pll1_bypass_src_clk);
//函数原型
struct clk *clk_get_parent(struct clk *clk)
//参数
clk: 准备操作的时钟句柄
返回: 成功返回父时钟的时钟句柄,失败返回NULL
//例程
parent = clk_get_parent(clk);
//函数原型
int clk_prepare(struct clk *clk)
//参数
clk: 准备操作的时钟句柄
返回: 成功返回0,失败返回对应时钟句柄
//例程
ret = clk_prepare(clk);
//备注:clk_prepare(该函数可能睡眠)和可以在原子上下文调用的clk_enable,而clk_prepare_enable则同时完成prepare和enable的工作,只能在可能睡眠的上调用API
//函数原型
int clk_enable(struct clk *clk)
//参数
clk: 准备操作的时钟句柄
返回: 成功返回0,失败返回对应时钟句柄
//例程
ret = clk_enable(clk);
//函数原型
static inline int clk_prepare_enable(struct clk *clk)
//参数
clk:指向准备操作的时钟句柄。这个时钟对象通常是在设备树(Device Tree)中定义的,或者在设备驱动程序初始化时通过其他方式获得的
返回: 0表示启用成功,失败返回负数的错误码
//例程
i2c1: i2c@21a0000 {
clocks = <&clks IMX6UL_CLK_I2C1>;
};
i2c_imx->clk = devm_clk_get(&pdev->dev, NULL);
ret = clk_prepare_enable(i2c_imx->clk);
//函数原型
void clk_disable(struct clk *clk)
//参数
clk:指向准备操作的时钟句柄。
//例程
clk_disable(i2c_imx->clk);
//函数原型
void clk_unprepare(struct clk *clk)
//参数
clk:指向准备操作的时钟句柄。
//例程
clk_unprepare(i2c_imx->clk);
//函数原型
void clk_disable_unprepare(struct clk *clk)
//参数
clk:指向准备操作的时钟句柄。
//例程
clk_disable_unprepare(i2c_imx->clk);
//函数原型
unsigned long clk_get_rate(struct clk *clk)
//参数
clk:准备操作的时钟句柄
返回:获取的时钟频率
//例程
//时钟在设备树的说明
ckil: clock-cli {
//......
#clock-cells = <0>;
clock-frequency = <32768>;
clock-output-names = "ckil";
};
clks: clock-controller@20c4000 {
//......
#clock-cells = <1>;
clocks = <&ckil>, <&osc>, <&ipp_di0>, <&ipp_di1>;
clock-names = "ckil", "osc", "ipp_di0", "ipp_di1";
};
clk = devm_clk_get(&pdev->dev, "ckil");
rate = clk_get_rate(clk); //获取clock-frequency属性
//函数原型
int clk_set_rate(struct clk *clk, unsigned long rate)
//参数
clk:准备操作的时钟句柄
rate: 希望设备的时钟频率
返回:成功返回0,失败返回对应错误码
//例程
clk_set_rate(ahb_clk, LPAPM_CLK);
关于gpio的接口可以查看文档: Documentation/driver-api/gpio/consumer.rst
//函数原型
struct gpio_desc *__must_check devm_gpiod_get(struct device *dev,
const char *con_id,
enum gpiod_flags flags)
{
return devm_gpiod_get_index(dev, con_id, 0, flags);
}
//参数
dev: 管理资源的设备,一般为总线匹配的设备
con_id: gpio属性id名称,"led"表示led-gpios属性,如果为NULL则表示gpios属性
flags: 设置GPIO的状态
GPIOD_ASIS 默认状态
GPIOD_IN 输入状态
GPIOD_OUT_LOW 输出状态,默认输出为0
GPIOD_OUT_HIGH 输出状态,默认输出为1
GPIOD_OUT_LOW_OPEN_DRAIN 输出状态,开漏模式,默认输出0
GPIOD_OUT_HIGH_OPEN_DRAIN 输出状态,开漏模式,默认输出1
返回:成功返回获取的引脚句柄,失败返回对应错误码
//例程
beep {
beep-gpios = <&gpio5 1 GPIO_ACTIVE_LOW>;
};
static gpio_desc *desc = devm_gpiod_get(&pdev-dev, "beep", GPIOD_OUT_LOW);
//函数原型
struct gpio_descs *__must_check devm_gpiod_get_array(struct device *dev,
const char *con_id,
enum gpiod_flags flags)
//参数
dev: 管理资源的设备,一般为总线匹配的设备
con_id: gpio属性id名称,"led"表示led-gpios属性,如果为NULL则表示gpios属性
flags: 设置GPIO的状态
返回:成功返回引脚句柄数组,失败返回对应错误码
//例程
usr_loopled {
//...
leds-gpios = <&gpio4 21 GPIO_ACTIVE_HIGH>,
<&gpio4 23 GPIO_ACTIVE_HIGH>,
<&gpio4 25 GPIO_ACTIVE_HIGH>;
};
static gpio_descs *descs = devm_gpiod_get_array(&pdev-dev, "leds", GPIOD_OUT_LOW);
//函数原型
struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev,
const char *con_id,
unsigned int idx,
enum gpiod_flags flags)
//参数
dev: 管理资源的设备,一般为总线匹配的设备
con_id: gpio属性id名称,如led-gpios属性,则输入led,必须时xx-gpios属性
idx: gpio资源中的标签
flags: 详细见devm_gpiod_get
返回:成功返回引脚句柄,失败返回对应错误码
//例程
leds-gpios = <&gpio4 21 GPIO_ACTIVE_HIGH>,
<&gpio4 23 GPIO_ACTIVE_HIGH>,
<&gpio4 25 GPIO_ACTIVE_HIGH>;
chip->desc[0] = devm_gpiod_get_index(&pdev-dev, "leds", 0, GPIOD_OUT_LOW);
chip->desc[1] = devm_gpiod_get_index(&pdev-dev, "leds", 1, GPIOD_OUT_LOW);
//函数原型
void devm_gpiod_put(struct device *dev, struct gpio_desc *desc)
//参数
dev: 管理资源的设备,一般为总线匹配的设备
desc: 申请的引脚句柄资源
//例程
devm_gpiod_put(&pdev->dev, desc);
//函数原型
void devm_gpiod_unhinge(struct device *dev, struct gpio_desc *desc)
//参数
dev: 管理资源的设备,一般为总线匹配的设备
desc: 申请的引脚描述符资源
//例程
devm_gpiod_unhinge(&pdev->dev, desc);
//函数原型
int devm_gpio_request(struct device *dev, unsigned gpio, const char *label)
//参数
dev: 管理资源的设备,一般为总线匹配的设备
gpio:gpio对应的标签值,通过of_get_named_gpio可以获取
label: 申请的gpio别名
返回: 0表示申请成功,其它则失败
//例程
ret = devm_gpio_request(&pdev->dev, chip->gpio, "beep");
//函数原型
int devm_gpio_request_one(struct device *dev, unsigned gpio,
unsigned long flags, const char *label)
//参数
dev: 管理资源的设备,一般为总线匹配的设备
gpio:gpio对应的标签值,通过of_get_named_gpio可以获取
flags:GPIOF_*开头定义的gpio配置
* GPIOF_DIR_IN - to configure direction as input
* GPIOF_DIR_OUT - to configure direction as output
* GPIOF_INIT_LOW - as output, set initial level to LOW
* GPIOF_INIT_HIGH - as output, set initial level to HIGH
* GPIOF_OPEN_DRAIN - gpio pin is open drain type.
* GPIOF_OPEN_SOURCE - gpio pin is open source type.
* GPIOF_EXPORT_DIR_FIXED - export gpio to sysfs, keep direction
* GPIOF_EXPORT_DIR_CHANGEABLE - also export, allow changing direction
* GPIOF_IN - configure as input
* GPIOF_OUT_INIT_LOW - configured as output, initial level LOW
* GPIOF_OUT_INIT_HIGH - configured as output, initial level HIGH
label: 申请的gpio别名
返回: 0表示申请成功,其它则失败
//例程
chip->key_gpio = of_get_named_gpio(nd, "key-gpios", 0);
devm_gpio_request(&pdev->dev, chip->gpio, "beep");
//函数原型
#define devm_request_mem_region(dev,start,n,name) \
__devm_request_region(dev, &iomem_resource, (start), (n), (name))
struct resource *__devm_request_region(struct device *dev, struct resource *parent,
resource_size_t start, resource_size_t n, const char *name)
//参数
dev: 管理资源的设备,一般为总线匹配的设备
type: 资源指针,这里特指MEM资源
start: 资源的起始地址
n: 资源的总长度
name: 使用资源的用户名称,用于内核标记
返回: NULL表示获取失败,其它返回resoure对应的指针地址
//例程
if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), DRIVER_NAME)) {
dev_warn(&pdev->dev, "Failed to get memory region resource\n");
return -ENOENT;
}
//函数原型
#define devm_request_region(dev,start,n,name) \
__devm_request_region(dev, &ioport_resource, (start), (n), (name))
struct resource *__devm_request_region(struct device *dev, struct resource *parent,
resource_size_t start, resource_size_t n, const char *name)
//参数
dev: 管理资源的设备,一般为总线匹配的设备
type: 资源指针,这里特指I/O资源
start: 资源的起始地址
n: 资源的总长度
name: 使用资源的用户名称,用于内核标记
返回: NULL表示获取失败,其它返回resoure对应的指针地址
//例程
struct resource *res_gpi;
if (!devm_request_region(&pdev->dev, res_gpi->start, resource_size(res_gpi), MODULE_NAME "_gpi")) {
dev_warn(&pdev->dev, "GPI I/O resource busy, probably claimed by ACPI\n");
}
//函数原型
struct resource *platform_get_resource_byname(struct platform_device *dev, unsigned int type, const char *name)
//参数
dev: 管理资源的设备,一般为总线匹配的设备
type: 资源的类型, IORESOURCE_IO, IORESOURCE_MEM, IORESOURCE_REG, IORESOURCE_IRQ, IORESOURCE_DMA, IORESOURCE_BUS
name: 资源的名称
返回: NULL表示获取失败,其它返回resoure对应的指针地址
//例程
//interrupt-name指定中断的name,通过platform_get_resource_byname访问
interrupts = <0 8 IRQ_TYPE_LEVEL_HIGH>,
<0 9 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "edma-tx", "edma-err";
fsl_edma->errirq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "edma-err")
//函数原型
void devm_free_irq(struct device *dev, unsigned int irq, void *dev_id)
//参数
dev: 管理资源的设备,一般为总线匹配的设备
irp: 被申请得中断线号
dev_id: 释放设备的标识指针
//例程
devm_free_irq(dev, priv->irq, priv);
//函数原型
int devm_request_any_context_irq(struct device *dev, unsigned int irq,
irq_handler_t handler, unsigned long irqflags,
const char *devname, void *dev_id)
//参数
dev: 管理资源的设备,一般为总线匹配的设备
irp: 被申请得中断线号
handler: irq发生时执行的中断回调函数
irqflags: 申请的中断触发的标志位,其中边沿和电平要和设备树一致,如果为0,则使用设备树中的中断标志
devname: 设备名称
dev_id: 传递给回调函数得参数
//例程
static irqreturn_t max77650_charger_check_status(int irq, void *data)
{
}
rv = devm_request_any_context_irq(dev, chg_irq,
max77650_charger_check_status,
IRQF_ONESHOT, "chg", chg);
//函数原型
static inline int __must_check
devm_request_irq(struct device *dev, unsigned int irq, irq_handler_t handler,
unsigned long irqflags, const char *devname, void *dev_id)
{
return devm_request_threaded_irq(dev, irq, handler, NULL, irqflags, devname, dev_id);
}
//参数
dev: 管理资源的设备,一般为总线匹配的设备
irp: 被申请得中断线号
handler: irq发生时执行得回调函数
irqflags: 申请的中断触发的标志位,其中边沿和电平要和设备树一致,如果为0,则使用设备树中的中断标志
devname: 设备名称
dev_id: 传递给回调函数得参数
//例程
//priv为传递的数据rngc
static irqreturn_t imx_rngc_irq(int irq, void *priv)
{
//中断回调函数
}
ret = devm_request_irq(&pdev->dev,
irq, imx_rngc_irq, 0, pdev->name, (void *)rngc);
//函数原型
int devm_request_threaded_irq(struct device *dev, unsigned int irq,
irq_handler_t handler, irq_handler_t thread_fn,
unsigned long irqflags, const char *devname,
void *dev_id)
//参数
dev: 管理资源的设备,一般为总线匹配的设备
irp: 被申请得中断线号
handler: irq发生时执行得回调函数(中断上半部)
thead_fn:线程化调用的中断函数,下半部,NULL则中断
irqflags: 申请的中断触发的标志位,其中边沿和电平要和设备树一致,如果为0,则使用设备树中的中断标志
devname: 设备名称
dev_id: 传递给回调函数得参数
//例程
//tegra为传递参数
static irqreturn_t actmon_thread_isr(int irq, void *data)
{
}
err = devm_request_threaded_irq(&pdev->dev, tegra->irq, NULL,
actmon_thread_isr, IRQF_ONESHOT,
"tegra-devfreq", tegra);
//函数原型
unsigned int irq_of_parse_and_map(struct device_node *dev, int index)
//参数
dev: 驱动匹配的设备节点
index: 在interrupt属性中的编号(interrupt属性支持多组)
返回: 在内核中所在控制器中的对应的中断编号
//例程
//设备树
key {
interrupt-parent = <&gpio1>;
interrupts = <18 IRQ_TYPE_EDGE_BOTH>;
};
chip->irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
等同于devm_led_classdev_register_ext, 格式如下所示。
static inline int devm_led_classdev_register(struct device *parent,
struct led_classdev *led_cdev)
{
return devm_led_classdev_register_ext(parent, led_cdev, NULL);
}
//函数原型
int devm_led_classdev_register_ext(struct device *parent,
struct led_classdev *led_cdev,
struct led_init_data *init_data)
//参数
parent: LED设备的父设备总线
led_cdev: led控制类结构
led_init_data: led类初始化数据
返回: 0表示注册成功,负值表示注册失败
//例程
//设备树
led_dat->cdev.brightness_set = gpio_led_set; //对应brightness
led_dat->cdev.blink_set = gpio_blink_set;
if (template->name) {
led_dat->cdev.name = template->name;
ret = devm_led_classdev_register(parent, &led_dat->cdev);
} else {
init_data.fwnode = fwnode;
ret = devm_led_classdev_register_ext(parent, &led_dat->cdev,
&init_data);
}
//函数原型
void *devm_kzalloc(struct device *dev, size_t size, gfp_t gfp)
//参数
dev: 管理资源的设备,一般为总线匹配的设备
size: 申请数据的长度
gfp: 申请数据的类型
/*
GFP_ATOMIC: GFP_ATOMIC标记是用在中断处理函数中的,
GFP_KERNEL: 内存不够,会等待内核释放内存,因此会发生堵塞,不能用在中断处理中
...
参考:https://blog.csdn.net/21cnbao/article/details/112210900
*/
返回: 获取的内存指针首地址
static struct led_data *chip;
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
//函数原型
struct pinctrl *devm_pinctrl_get(struct device *dev)
//参数
dev: 管理资源的设备,一般为总线匹配的设备
返回: 管理引脚资源的句柄
struct pinctrl {
struct list_head node; /* 管理资源的列表 */
struct device *dev; /* 使用此资源的设备 */
struct list_head states; /* 设备状态的列表 */
struct pinctrl_state *state; /* 资源的状态 */
struct list_head dt_maps; /* 从设备树种解析的映射表块 */
struct kref users; /* 资源被引用的次数 */
};
//例程
usr_led {
//...
pinctrl-names = "default", "improve";
pinctrl-0 = <&pinctrl_gpio_led>;
pinctrl-1 = <&pinctrl_led_improve>;
}
pinctrl_gpio_led: gpio-leds {
fsl,pins = <
MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x17059
>;
};
pinctrl_led_improve: led-improve {
fsl,pins = <
MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x40017059
>;
};
chip->led_pinctrl = devm_pinctrl_get(&pdev->dev);
if(IS_ERR_OR_NULL(chip->led_pinctrl))
{
dev_info(&pdev->dev, "devm_pinctrl_get failed\n");
return -EIO;
}
//函数原型
void devm_pinctrl_put(struct pinctrl *p)
//参数
p: 申请的管理引脚资源的句柄
//例程
devm_pinctrl_put(chip->led_pinctrl);
//函数原型
struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p,
const char *name)
//参数
p: 申请的管理引脚资源的句柄
name: pinctrl-names中的名称,与<pinctrl-num>相对应
返回: 返回name对应的pinctrl状态
//例程
struct pinctrl_state *pState = pinctrl_lookup_state(chip->led_pinctrl, "improve");
//函数原型
int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state)
//参数
p: 申请的管理引脚资源的句柄
state: 获取的引脚状态的状态配置
返回: 0表示选择成功,其它则失败
//例程
int ret = pinctrl_select_state(chip->led_pinctrl, pState);
//函数原型
static inline struct pinctrl * __must_check devm_pinctrl_get_select(struct device *dev, const char *name)
//参数
dev: 管理资源的设备,一般为总线匹配的设备
name: pinctrl-names属性中的名称
返回: 管理引脚资源的句柄
//例程
chip->led_pinctrl = devm_pinctrl_get(&pdev->dev, "default");
//函数原型
struct rtc_device *devm_rtc_device_register(struct device *dev,
const char *name,
const struct rtc_class_ops *ops,
struct module *owner)
//参数
dev: 管理资源的设备,一般为总线匹配的设备
name: rtc设备名称
ops: rtc设备用于上层访问调用的接口
struct rtc_class_ops {
int (*ioctl)(struct device *, unsigned int, unsigned long);
int (*read_time)(struct device *, struct rtc_time *); //读取当前时间
int (*set_time)(struct device *, struct rtc_time *); //写入当前时间
int (*read_alarm)(struct device *, struct rtc_wkalrm *); //读取当前定时时间
int (*set_alarm)(struct device *, struct rtc_wkalrm *); //写入当前定时时间
int (*proc)(struct device *, struct seq_file *);
int (*alarm_irq_enable)(struct device *, unsigned int enabled);
int (*read_offset)(struct device *, long *offset);
int (*set_offset)(struct device *, long offset);
int (*param_get)(struct device *, struct rtc_param *param);
int (*param_set)(struct device *, struct rtc_param *param);
};
module: 设备归属用户,一般为THIS_MODULE
//例程
static const struct rtc_class_ops pcf8563_ops = {
.ioctl = pcf8563_rtc_ioctl,
.read_time = pcf8563_get_time,
.set_time = pcf8563_set_time,
.read_alarm = pcf8563_read_alarm,
.set_alarm = pcf8563_set_alarm,
.alarm_irq_enable = pcf8563_alarm_irq_enable,
};
priv->rtc = devm_rtc_device_register(&pdev->dev, "pcf8563", &pcf8563_ops, THIS_MODULE);
//函数原型
struct rtc_device *devm_rtc_allocate_device(struct device *dev)
//参数
dev: 管理rtc设备的设备(一般为platform总线设备或i2c设备)
//例程
chip->rtc = devm_rtc_allocate_device(&client->dev);
if (IS_ERR(chip->rtc)){
dev_err(&client->dev, "rtc alloc device failed!\n");
return PTR_ERR(chip->rtc);
}
//函数原型
#define devm_rtc_register_device(device) \
__devm_rtc_register_device(THIS_MODULE, device)
//参数
device: 指向rtc设备的结构指针
//例程
int err;
chip->rtc->owner = THIS_MODULE;
chip->rtc->ops = &pcf8563_ops; //设置rtc支持的接口
set_bit(RTC_FEATURE_ALARM_RES_MINUTE, chip->rtc->features);
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, chip->rtc->features);
chip->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
chip->rtc->range_max = RTC_TIMESTAMP_END_2099;
chip->rtc->set_start_time = true;
//5. register the rtc device.
err = devm_rtc_register_device(chip->rtc);
//函数原型
static inline int devm_add_action_or_reset(struct device *dev,
void (*action)(void *), void *data)
//参数
dev:指向要添加回调函数的设备的指针。
action:指向要添加的回调函数的指针。当设备被释放或发生某些错误时,这个回调函数将被调用。
data:传递给回调函数的参数。
返回: 0表示添加成功,其它则表示添加失败
//例程
ret = devm_add_action_or_reset(dev, imx2_wdt_action, wdev->clk);
直接开始下一节说明: 内核扩展资源管理接口