RK3568 PWM驱动学习总结

一、pwm介绍

二、RK3568的pwm接口

三、Pwm子系统介绍

四、RK3568的PWM5引脚信息

五、RK3568 MIPI屏幕背光控制使用的pwm5驱动分析

 

一、pwm介绍

pwm是脉冲宽度调制技术,由一个矩形波组成,pwm信号可以由周期和占空比描述;

周期:一个完整高低电平循环所需的时间;

占空比:高电平时间与周期的比例;

pwm信号的应用场景:

基于面积等效原理,通过占空比来控制电压的幅度;

控制电机转速和转矩;

控制LED灯亮度;

用于无线电通讯中的调制;

二、RK3568pwm接口

RK3568pwm接口:

芯片内置16pwm通道,也就是16pwm控制器,可以独立控制和输出pwm波形;

pwm通道采用可编程的预分频器,可以先对总线时钟进行分频,再进行二分频;

pwm通道内置了32位的定时/计数器功能;

pwm通道支持捕获模式,可以捕获外部输入信号的脉冲宽度信息;

pwm通道支持连续输出模式和一次性输出模式;

pwm通道提供参考模式,可以输出各种占空比的波形;

Pwm3pwm7pwm11pwm15经过优化,特别适用于红外应用;

16路都是硬件pwm,也就是软件写寄存器就可以控制pwm

优点:

cpu占用低,硬件自动完成pwm波形输出,无需cpu介入;

pwm输出频率和分辨率高,可以达到MHz级别的频率和ns级分辨率;

输出波形稳定,不易受cpu负载的影响;

缺点:

硬件成本高,需要专门的pwm模块;

pwm输出引脚受限,只能在限定的引脚输出;

比如:

Pwm5:

使用引脚是GPIO0_C4,用于mipi屏幕背光灯;

三、Pwm子系统介绍

pwm子系统分为用户空间、内核空间和硬件层;内核空间包括设备驱动层、PWM核心层和pwm适配器驱动层;pwm核心层提供了sysfs接口,用户空间可以直接通过这些sysfs接口/sys/class/pwm控制pwm

Pwm设备驱动层包含如下几个部分:

Pwm_device,代表一个连接在pwm总线上的从设备,包含从设备的pwm通道号、所属的pwm控制器等信息;

Pwm_driver,实现具体的Pwm设备驱动程序,负责pwm设备的初始化、配置、启停等动作;通过pwm_chippwm硬件设备进行交互;

向上提供标准的pwm设备访问接口;

/dev/xxx设备节点,提供给用户空间的交互接口,比如打开、读写、控制设备节点等;

pwm核心层:

核心层是linux内核中负责管理和控制pwm设备的重要组件;

通过struct pwm_device 统一描述PWM 设备的信息,如设备名称、所属控制器等。为上层应用程序提供标准化的PWM 设备访问API

提供pwm_config 接口,用于配置PWM 设备的工作参数,如周期、占空比、极性等。将上层配置参数转换为底层硬件可以理解的形式,并通过适配器驱动进行设置

提供pwm_enable pwm_disable 接口,用于控制PWM 输出的开启和关闭。负责调用适配器驱动程序的相应函数来执行实际的输出控制

集成了电源管理等高级特性;

PWM 核心层还提供了一组基于sysfs 的接口,用于上层应用程序对PWM 设备进行配置和控制。这些sysfs 接口包括:

/sys/class/pwm/pwmX/period: 设置PWM 周期

/sys/class/pwm/pwmX/duty_cycle: 设置PWM 占空比

/sys/class/pwm/pwmX/polarity: 设置PWM 极性

/sys/class/pwm/pwmX/enable: 控制PWM 输出的开启和关闭

PWM 适配器驱动层

PWM 适配器驱动层是连接PWM 硬件和上层PWM 核心层的关键组件,也就是控制器驱动,它负责对底层PWM硬件进行抽象化封装,并适配不同类型的PWM 设备,为上层提供标准化的硬件控制接口,同时还需要处理一些硬件相关的初始化、状态管理和电源管理等任务

硬件抽象化PWM 适配器驱动层负责对底层PWM 硬件设备进行抽象化封装。它定义了一组标准化的pwm_chip 接口,包括设备初始化、参数配置、输出控制等功能;

硬件适配PWM 适配器驱动程序需要实现pwm_chip 接口中定义的各个函数,以适配底层PWM 硬件的具体工作方式。这些适配函数将上层PWM 核心层的标准化请求,转换为底层硬件可以理解的操作指令。通过这种适配层,PWM 核心层可以无缝地支持不同类型的PWM 硬件设备;

硬件初始化PWM 适配器驱动程序负责完成PWM 硬件设备的初始化工作,包括GPIO、时钟、电源等相关资源的配置和初始化。在PWM 设备注册时,适配器驱动会执行初始化操作,确保硬件设备处于可用状态;

状态管理,适配器驱动程序需要维护PWM 硬件设备的当前状态,如设备是否已经被使能、当前的工作参数等。这些状态信息会反馈给上层PWM 核心层,以确保核心层能够正确地控制和管理PWM设备。

pwm子系统

四、RK3568PWM5引脚信息

用于MIPI屏幕的背光控制:

通过底板原理图查询到MIPI1背光控制使用PWM5

mipi1使用pwm5

通过核心板查询到PWM5对应的引脚是GPIO0_C4,并且列出了复用关系:

所以要配置设备树,确保引脚被配置成PWM5功能,

引脚复用图

GPIO0_C4的第一个复用功能就是PWM5,所以设备树中PWM5pinctrl配置如下:

pwm5设备树节点

<0 RK_PC4 1 &pcfg_pull_none>;  --- 表示将GPIO0_C4配置成功能1

GPIO0_C4的功能2spi0cs1引脚,所以spi0cs1引脚的pinctrl按如下配置:

<0 RK_PC4 2 &pcfg_pull_none>;

所以设备树中需要确保spi0cs1引脚没有使用;

设备树的引脚复用

GPIO0_C4的第3个功能是uart0rts引脚,所以uart0rts引脚按如下配置:

<0 RK_PC4 3 &pcfg_pull_none>;

所以设备树中需要确保uart0rts引脚没有使用,控制台使用的是串口2seirial2

uart0

五、RK3568 MIPI屏幕背光控制使用的pwm5驱动分析

设备树介绍:

mipi屏幕的设备树节点是dsi1,定义在设备树文件topeet_rk3568_lcds.dtsi

mipi屏幕的背光是通过pwm5控制的,背光是一个pwm设备驱动,对应pwm子系统的pwm设备驱动层;对应的设备树节点是backlightmipi屏幕的设备树节点dsi1中引用backlight节点,

&dsi1 {
        status = "disabled";
        //rockchip,lane-rate = <1000>;
        dsi1_panel: panel@0 {
                status = "disabled";
                compatible = "simple-panel-dsi";
                reg = <0>;
                backlight = <&backlight>;

backlight设备树节点定义如下,定义在设备树文件rk3568-evb.dtsi

backlight: backlight {
        compatible = "pwm-backlight";
        pwms = <&pwm4 0 25000 0>;
        brightness-levels = <
              0  20  20  21  21  22  22  23
             23  24  24  25  25  26  26  27
             27  28  28  29  29  30  30  31
             31  32  32  33  33  34  34  35
             35  36  36  37  37  38  38  39
             40  41  42  43  44  45  46  47
             48  49  50  51  52  53  54  55
             56  57  58  59  60  61  62  63
             64  65  66  67  68  69  70  71
             72  73  74  75  76  77  78  79
             80  81  82  83  84  85  86  87
             88  89  90  91  92  93  94  95
             96  97  98  99 100 101 102 103
            104 105 106 107 108 109 110 111
            112 113 114 115 116 117 118 119
            120 121 122 123 124 125 126 127
            128 129 130 131 132 133 134 135
            136 137 138 139 140 141 142 143
            144 145 146 147 148 149 150 151
            152 153 154 155 156 157 158 159
            160 161 162 163 164 165 166 167
            168 169 170 171 172 173 174 175
            176 177 178 179 180 181 182 183
            184 185 186 187 188 189 190 191
            192 193 194 195 196 197 198 199
            200 201 202 203 204 205 206 207
            208 209 210 211 212 213 214 215
            216 217 218 219 220 221 222 223
            224 225 226 227 228 229 230 231
            232 233 234 235 236 237 238 239
            240 241 242 243 244 245 246 247
            248 249 250 251 252 253 254 255
        >;
        default-brightness-level = <200>;
    };

topeet_rk3568_lcds.dtsi文件中对backlight节点进行使能,并修改pwms属性为引用pwm5

&backlight{
    pwms = <&pwm5 0 25000 0>;
    status = "okay";
};

Pwm5设备树节点定义在kernel/arch/arm64/boot/dts/rockchip/rk3568.dtsi,节点内容如下:

pwm5: pwm@fe6e0010 {
                compatible = "rockchip,rk3568-pwm", "rockchip,rk3328-pwm";
                reg = <0x0 0xfe6e0010 0x0 0x10>;
                #pwm-cells = <3>;
                pinctrl-names = "active";
                pinctrl-0 = <&pwm5_pins>;
                clocks = <&cru CLK_PWM1>, <&cru PCLK_PWM1>;
                clock-names = "pwm", "pclk";
                status = "disabled";
        };

rk3568-evb.dtsi文件中使能pwm5节点

&pwm5 {
        status = "okay";
};

 

backlight驱动分析:

设备树节点属性解析:

compatible = "pwm-backlight";   --- 用于和驱动匹配

pwms = <&pwm5 0 25000 0>;  ---  使用pwm5控制器,通道0,周期设置为25000ns,极性索引是0

brightness-levels  ---  背光亮度表,总共256

default-brightness-level = <200>;  ---  默认的背光亮度

backlight作为一个背光pwm设备驱动,主要作用是向上对接背光子系统(backlight_device_register这个函数把驱动注册到背光子系统);向下通过pwm核心层接口设置pwm5控制器,比如:devm_pwm_getpwm_adjust_configpwm_get_argspwm_configpwm_backlight_power_on

驱动的probe函数内容如下:

/*
 * backlight驱动代码详细分析
 * 对应文件:kernel/drivers/video/backlight/pwm_bl.c
 * 设备树和驱动匹配后进入probe函数
 * struct platform_pwm_backlight_data结构体定义在头文件kernel/include/linux/pwm_backlight.h
 * 从设备树获取信息
 * 从设备树获取pwm设备struct pwm_device *
 * 把背光设备注册到linux背光子系统
 * 调用背光子系统的标准接口,根据当前 props(brightness 和 power)实际配置硬件
 * 该函数会调用pwm控制器驱动的pwm_backlight_update_status
*/
static int pwm_backlight_probe(struct platform_device *pdev)
    struct platform_pwm_backlight_data defdata;
    /* 没有使用设备树的情况,data从这个接口获取 */
    struct platform_pwm_backlight_data *data = dev_get_platdata(&pdev->dev);
    int ret;
    /*  
     * 解析设备树节点,获取设备树内容
     * 从设备树读取属性:"brightness-levels"、"default-brightness-level"
    */
    ret = pwm_backlight_parse_dt(&pdev->dev, &defdata);
        /* 获取设备树节点的指针 */
        struct device_node *node = dev->of_node;
        memset(data, 0, sizeof(*data));
        /* 从设备树节点读取两个属性的值:"post-pwm-on-delay-ms"、"pwm-off-delay-ms" */
        of_property_read_u32(node, "post-pwm-on-delay-ms", &data->post_pwm_on_delay);
        of_property_read_u32(node, "pwm-off-delay-ms", &data->pwm_off_delay);
        /* 使能引脚 */
        data->enable_gpio = -EINVAL;
        /* 读取属性"brightness-levels"的字节长度,一个单元4字节,共256单元,所以占1024字节 */
        struct property *prop;
        prop = of_find_property(node, "brightness-levels", &length);
        /* 计算数组元素个数 */
        data->max_brightness = length / sizeof(u32);
        if (data->max_brightness > 0)
            /* size = 4 * 1024 */
            size_t size = sizeof(*data->levels) * data->max_brightness;
            /* 申请内存 */
            data->levels = devm_kzalloc(dev, size, GFP_KERNEL);
            /* 读取数组的全部256个元素,保存到分配的内存 */
            ret = of_property_read_u32_array(node, "brightness-levels", data->levels, data->max_brightness);
            /* 读取默认的亮度级别 */
            ret = of_property_read_u32(node, "default-brightness-level", &value);
            data->dft_brightness = value;
            /* 读取插值属性"num-interpolated-steps",设备树未定义 */
            of_property_read_u32(node, "num-interpolated-steps", &num_steps);
    data = &defdata;
    /* 申请结构体内存 */
    struct pwm_bl_data *pb;
    pb = devm_kzalloc(&pdev->dev, sizeof(*pb), GFP_KERNEL);
    /* 从设备树获取enable引脚的描述符struct gpio_desc,设备树没定义,所以获取不到 */
    pb->enable_gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_ASIS);
    /* 获取电源供应器,设备树没定义,所以获取不到 */
    pb->power_supply = devm_regulator_get(&pdev->dev, "power");
    /* 
     * 从设备树获取pwm设备struct pwm_device *
     * 对应属性:pwms = <&pwm5 0 25000 0>;  --- //通道索引0、周期25000ns、极性0
    */
    pb->pwm = devm_pwm_get(&pdev->dev, NULL);
    for (i = 0; i <= data->max_brightness; i++) {
        /* 把亮度表最大的值保存到pb->scale */
        if (data->levels[i] > pb->scale)
            pb->scale = data->levels[i];

        /* 把亮度表赋值给pb->levels */
        pb->levels = data->levels;
    }
    /* 配置pwm参数,周期、占空比、极性等 */
    pwm_adjust_config(pb->pwm);
    /* 获取周期和极性参数 */
    struct pwm_args pargs;
    pwm_get_args(pb->pwm, &pargs);
    /* 把背光设备注册到linux背光子系统 */
    struct backlight_properties props;
    memset(&props, 0, sizeof(struct backlight_properties));
    props.type = BACKLIGHT_RAW;
    props.max_brightness = data->max_brightness;
    /*
        static const struct backlight_ops pwm_backlight_ops = {
            .update_status    = pwm_backlight_update_status,
            .check_fb    = pwm_backlight_check_fb,
        };
    */
    bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, pb, &pwm_backlight_ops, &props);
    /* 背光设备的属性设置成默认亮度 */
    bl->props.brightness = data->dft_brightness;
    /*
     * 根据当前硬件状态(如使能 GPIO 电平、regulator 是否使能、PWM 是否已开启)确定背光的初始电源状态,并将结果赋值给 bl->props.power
    */
    bl->props.power = pwm_backlight_initial_power_state(pb);
    /*
     * 调用背光子系统的标准接口,根据当前 props(brightness 和 power)实际配置硬件
     * 该函数会调用pwm_backlight_update_status
    */
    backlight_update_status(bl);
        /*
         * static int pwm_backlight_update_status(struct backlight_device *bl)
        */
        pwm_backlight_update_status
            /* 取出私有结构体 */
            struct pwm_bl_data *pb = bl_get_data(bl);
            /* 这个是前面设置的默认亮度 */
            int brightness = bl->props.brightness;
            /*
             * 检查是否需要将亮度设置为0
            */
            if (bl->props.power != FB_BLANK_UNBLANK ||
                bl->props.fb_blank != FB_BLANK_UNBLANK ||
                bl->props.state & BL_CORE_FBBLANK)
                brightness = 0;
            if (brightness > 0)
                /* 将亮度值转换为pwm的占空比 */
                duty_cycle = compute_duty_cycle(pb, brightness);
                    unsigned int lth = pb->lth_brightness;
                    /* 通过亮度表索引转换 */
                    duty_cycle = pb->levels[brightness];
                    duty_cycle *= pb->period - lth;
                    do_div(duty_cycle, pb->scale);
                    return duty_cycle + lth;
                /* 调用 pwm_config 更新 PWM 的占空比和周期(周期为 pb->period)*/
                pwm_config(pb->pwm, duty_cycle, pb->period);
                /* 使能pwm */
                pwm_backlight_power_on(pb, brightness);
    /* 设置私有数据 */
    platform_set_drvdata(pdev, bl);

Pwm5控制器驱动:

设备树节点分析:

pwm5: pwm@fe6e0010 {

                compatible = "rockchip,rk3568-pwm", "rockchip,rk3328-pwm";

                reg = <0x0 0xfe6e0010 0x0 0x10>;

                #pwm-cells = <3>;

                pinctrl-names = "active";

                pinctrl-0 = <&pwm5_pins>;

                clocks = <&cru CLK_PWM1>, <&cru PCLK_PWM1>;

                clock-names = "pwm", "pclk";

                status = "disabled";

        };

compatible = "rockchip,rk3568-pwm", "rockchip,rk3328-pwm"; --- 用于和驱动匹配的字符串

reg = <0x0 0xfe6e0010 0x0 0x10>;  --- 控制器的寄存器

#pwm-cells = <3>; ---  pwm设备驱动的参数是3个,pwms = <&pwm5 0 25000 0>;

pinctrl-names = "active";  ---  引脚复用

pinctrl-0 = <&pwm5_pins>;  ---  引脚复用是pwm5_pins

clocks = <&cru CLK_PWM1>, <&cru PCLK_PWM1>;  --- 两个时钟,一个是pwm设备时钟,另一个是总线时钟

clock-names = "pwm", "pclk";  --- 两个时钟对应的名称

Pwm5控制器驱动的主要内容是向上提供接口给pwm核心层控制Pwm设备,向下读写寄存器控制pwm硬件,驱动主要内容如下:

/*
 * pwm5控制器驱动详细分析
 * 对应的文件:kernel/drivers/pwm/pwm-rockchip.c
*/
//对应的寄存器信息
static const struct rockchip_pwm_data pwm_data_v3 = {
    .regs = {   //各寄存器偏移(duty, period, cntr, ctrl)
        .duty = 0x08,
        .period = 0x04,
        .cntr = 0x00,
        .ctrl = 0x0c,
    },
    .prescaler = 1,  //时钟分频系数(v3 为 1,即不分频;v1 为 2)
    .supports_polarity = true,  //是否支持输出极性配置
    .supports_lock = true,   //是否支持锁存机制(v3 支持,可同时更新周期、占空比和极性)
    .vop_pwm = false,  //
    .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE |
               PWM_CONTINUOUS,   //使能 PWM 输出所需的寄存器位组合
    .enable_conf_mask = GENMASK(2, 0) | BIT(5) | BIT(8),  //使能/禁用时需要修改的位掩码
};
{ .compatible = "rockchip,rk3328-pwm", .data = &pwm_data_v3},
/* 
 * probe函数内容 
 * 解析设备树获取寄存器地址、引脚信息、时钟信息
 * 将pwm控制器struct pwm_chip注册到内核子系统
 * 
*/
static int rockchip_pwm_probe(struct platform_device *pdev)
    /* 获取pwm_data_v3内容 */
    const struct of_device_id *id;
    id = of_match_device(rockchip_pwm_dt_ids, &pdev->dev);
    /* 申请rockchip_pwm_chip结构体内存 */
    struct rockchip_pwm_chip *pc;
    pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
    /*
     * reg = <0x0 0xfe6e0010 0x0 0x10>;寄存器物理地址,映射到虚拟地址
    */
    struct resource *r;
    r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    pc->base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
    /*
     * 从设备树节点获取pwm时钟
     * clocks = <&cru CLK_PWM1>, <&cru PCLK_PWM1>;
     * clock-names = "pwm", "pclk";
     * pwm时钟对应<&cru CLK_PWM1>,PWM 模块用于产生波形的基准时钟
     * pclk时钟对应<&cru PCLK_PWM1>,APB 总线时钟,用于访问寄存器
    */
    pc->clk = devm_clk_get(&pdev->dev, "pwm");
    /* 获取设备树节点定义了几个时钟,这里是2个:<&cru CLK_PWM1>, <&cru PCLK_PWM1> */
    count = of_count_phandle_with_args(pdev->dev.of_node, "clocks", "#clock-cells");
    /* 获取总线时钟 */
    pc->pclk = devm_clk_get(&pdev->dev, "pclk");
    /* 使能pwm模块的时钟,让时钟控制器开始提供有效的时钟信号给硬件模块,使硬件能够正常工作 */
    ret = clk_prepare_enable(pc->clk);
    /* 总线时钟准备,但是不使能 */
    ret = clk_prepare(pc->pclk);
    /* 从设备树获取引脚的pinctrl信息 */
    pc->pinctrl = devm_pinctrl_get(&pdev->dev);
    pc->active_state = pinctrl_lookup_state(pc->pinctrl, "active");
    /* 将pdev的私有数据设置成pc */
    platform_set_drvdata(pdev, pc);
    /* pwm_data_v3数据内容赋值到pc->dat */
    pc->data = id->data;
    /*
     * 填充pwm的操作函数
     static const struct pwm_ops rockchip_pwm_ops = {
        .get_state = rockchip_pwm_get_state,
        .apply = rockchip_pwm_apply,
        .owner = THIS_MODULE,
    };
    */
    pc->chip.ops = &rockchip_pwm_ops;
    /* 获取pwm工作时钟的频率 */
    pc->clk_rate = clk_get_rate(pc->clk);
    /* 配置 PWM 控制器(pwm_chip)的设备树解析方式,使其能够正确解析设备树中 pwms 属性携带的极性标志 */
    if (pc->data->supports_polarity)
        /* 
         * 设置 PWM 核心在解析设备树中 pwms 属性时使用的回调函数 
         * PWM 子系统提供的标准解析函数,用于将设备树中的 3 个参数(通道索引、周期、标志)转换为内核 struct pwm_args 结构体(包含 period 和 polarity)
        */
        pc->chip.of_xlate = of_pwm_xlate_with_flags;
        /* 声明该 PWM 控制器期望的 pwms 属性中每个 PWM 条目包含的参数个数pwms = <&pwm5 0 25000 0>; --- 0表示通道号、25000表示周期、0表示极性 */
        pc->chip.of_pwm_n_cells = 3;
    /*
     * 向内核 PWM 框架注册一个 PWM 控制器
     * 将pwm控制器struct pwm_chip注册到内核子系统
     * 使其可供系统中其他设备驱动(如背光驱动)通过标准 PWM API 使用
     * PWM 核心将该控制器加入内部链表,其他驱动可以通过设备树 phandle 或查找表(pwm_lookup)找到该控制器及其通道
     * 设备树节点中的 #pwm-cells = <3> 与驱动设置的 of_pwm_n_cells = 3 匹配,使得消费者(如 backlight1)的 pwms 属性能被正确解析
     * 注册后,上层驱动可调用 pwm_get()、pwm_config()、pwm_enable() 等 API,最终会通过 pc->chip.ops 中的回调(如 rockchip_pwm_apply)操作硬件
    */
    ret = pwmchip_add(&pc->chip);
    /* 判断pwm是否使能状态,如果没使能,需要关闭pwm的工作时钟 */
    if (!pwm_is_enabled(pc->chip.pwms))
        clk_disable(pc->clk);
    
/*
 * 提供给pwm核心层的2个函数
 * rockchip_pwm_get_state
 * rockchip_pwm_apply
*/
/*
 * rockchip_pwm_get_state
 * 通过读寄存器获取当前pwm的状态
*/
static void rockchip_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *state)
    struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
    u32 enable_conf = pc->data->enable_conf;
    
    /* 使能总线时钟以访问pwm控制器的寄存器 */
    ret = clk_enable(pc->pclk);
    /* 获取周期信息 */
    tmp = readl_relaxed(pc->base + pc->data->regs.period);
    tmp *= pc->data->prescaler * NSEC_PER_SEC;
    state->period = DIV_ROUND_CLOSEST_ULL(tmp, pc->clk_rate);
    /* 获取占空比信息 */
    tmp = readl_relaxed(pc->base + pc->data->regs.duty);
    tmp *= pc->data->prescaler * NSEC_PER_SEC;
    state->duty_cycle =  DIV_ROUND_CLOSEST_ULL(tmp, pc->clk_rate);
    val = readl_relaxed(pc->base + pc->data->regs.ctrl);
    state->enabled = ((val & enable_conf) != enable_conf) ? false : true;
    if (pc->data->supports_polarity)
        if (!(val & PWM_DUTY_POSITIVE))
            state->polarity = PWM_POLARITY_INVERSED;
    /* 关闭总线时钟 */
    clk_disable(pc->pclk);
    
/*
 * rockchip_pwm_apply函数的内容
 * 配置pwm的参数
*/
static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *state)
    struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
    /* 使能总线时钟 */
    ret = clk_enable(pc->pclk);
    /* 将硬件当前的实际配置(周期、占空比、极性、使能状态等)读取到 curstate 结构体中 */
    pwm_get_state(pwm, &curstate);
    enabled = curstate.enabled;
    /* 如果需要修改极性、并且不支持锁存,需要先警用pwm */
    if (state->polarity != curstate.polarity && enabled && !pc->data->supports_lock)
        ret = rockchip_pwm_enable(chip, pwm, false);
        enabled = false;
    /*
     * 读写寄存器配置pwm
    */
    rockchip_pwm_config(chip, pwm, state);
        struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
        div = (u64)pc->clk_rate * state->period;
        period = DIV_ROUND_CLOSEST_ULL(div, pc->data->prescaler * NSEC_PER_SEC);
        div = (u64)pc->clk_rate * state->duty_cycle;
        duty = DIV_ROUND_CLOSEST_ULL(div, pc->data->prescaler * NSEC_PER_SEC);
        local_irq_save(flags);
        ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl);
        if (pc->data->supports_lock)
            /* 支持锁存,那么要将寄存器的锁存器设置 */
            ctrl |= PWM_LOCK_EN;
            writel_relaxed(ctrl, pc->base + pc->data->regs.ctrl);
        writel(period, pc->base + pc->data->regs.period);
        writel(duty, pc->base + pc->data->regs.duty);
        /* 极性配置 */
        if (pc->data->supports_polarity)
            ctrl &= ~PWM_POLARITY_MASK;
            if (state->polarity == PWM_POLARITY_INVERSED)
                ctrl |= PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSITIVE;
            else
                ctrl |= PWM_DUTY_POSITIVE | PWM_INACTIVE_NEGATIVE;
        /*
         * Unlock and set polarity at the same time,
         * the configuration of duty, period and polarity
         * would be effective together at next period.
         */
        if (pc->data->supports_lock)
            ctrl &= ~PWM_LOCK_EN;
        writel(ctrl, pc->base + pc->data->regs.ctrl);
        local_irq_restore(flags);
    /* 根据state参数,使能或者不使能pwm */
    if (state->enabled != enabled)
        /* 读写寄存器使能Pwm */
        ret = rockchip_pwm_enable(chip, pwm, state->enabled);
    /*
     * 读寄存器获取当前pwm的状态信息
    */
    rockchip_pwm_get_state(chip, pwm, state);
    /*
     * 如果要使能pwm,那么这里要配置pwm引脚
    */
    if (state->enabled || pc->oneshot)
        ret = pinctrl_select_state(pc->pinctrl, pc->active_state);
    /* 关闭总线时钟 */
    clk_disable(pc->pclk);

 

参考讯为的视频:

https://www.bilibili.com/video/BV1um42157VT?vd_source=eb04ac3759f85a5dd795269e17334fee&spm_id_from=333.788.videopod.sections

 

posted @ 2026-08-15 08:26  小小的番茄  阅读(24)  评论(0)    收藏  举报