Linux pwm (设备驱动)

本文详细介绍了SUNXI平台上的脉冲宽度调制(PWM)控制器驱动实现,包括驱动结构、设备节点创建、文件操作及 ioctl 接口处理等。深入探讨了PWM配置、使能、禁用等操作的内核实现机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/*
 * drivers/pwm/pwm-sunxi-dev.c
 *
 * Allwinnertech pulse-width-modulation controller driver
 *
 * Copyright (C) 2018 AllWinner
 *
 *
 * This file is licensed under the terms of the GNU General Public
 * License version 2. This program is licensed "as is" without any
 * warranty of any kind, whether express or implied.
 */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/pinctrl/consumer.h>
#include <asm/io.h>
#include <linux/pwm.h>

static u32 debug_mask;
#define dprintk(level_mask, fmt, arg...)                \
do {                                    \
    if (unlikely(debug_mask & level_mask))                \
        pr_warn("%s()%d - "fmt, __func__, __LINE__, ##arg);    \
} while (0)

#define IRTX_ERR(fmt, arg...) pr_err("%s()%d - "fmt, __func__, __LINE__, ##arg)

#define PWM_IOCTL_BASE 'P'
#define PWM_CONFIG    _IOW(PWM_IOCTL_BASE, 1 , struct pwm_config)
#define PWM_ENABLE    _IOW(PWM_IOCTL_BASE, 2 , int)
#define PWM_DISABLE   _IOW(PWM_IOCTL_BASE, 3 , int)
#define GROUP_PWM_CONFIG    _IOW(PWM_IOCTL_BASE, 4, struct pwm_config)

struct pwm_config {
    int pwm_id;        //id,0对应pwm0,类推
    int duty_ns;       //占空比时间,单位ns
    int period_ns;     //周期时间,单位ns
    int pwm_polarity;  // 0表示 PWM_POLARITY_NORMAL, 1表示PWM_POLARITY_INVERSED
    
    int group_channel;      //组使用 0表示不使用,1表示使用第零组,2表示使用第一组
    int group_run_count;    //组脉冲数
};
/*
struct group_pwm_config {
    int pwm_id;             //id, 组中用到的一个pwm    
    int group_duty_ns;        //组占空比
    int group_period_ns;    //组周期
    int group_polarity;     //组极性 0表示normal, 1表示inversed
    int group_run_count;    //组脉冲数
    int group_channel;         // 组使用 0表示不使用,1表示使用第零组,2表示使用第一组    
};
*/
struct sunxi_pwm_group {
    unsigned int group0_used;
    unsigned int group0_mask;
    
    unsigned int group1_used;
    unsigned int group1_mask;
};

struct sunxi_pwm_chip {
    struct pwm_chip chip;
    void __iomem *base;
    struct sunxi_pwm_config *config;
    struct sunxi_pwm_group group;
#if defined(CLK_GATE_SUPPORT)
    struct clk *pwm_clk;
#endif
    unsigned int g_channel;
};

struct sunxi_pwm_dev {
    struct device *dev;
    struct cdev cdev;
    dev_t chrdev;
};

static struct sunxi_pwm_dev    *sunxi_pwm_dev;
static struct class            *sunxi_pwm_class;

static int sunxi_pwm_open(struct inode *inode, struct file *filp)
{
    filp->private_data = sunxi_pwm_dev; 
    return 0;
}

static int sunxi_pwm_release(struct inode *inode, struct file *filp)
{
    return 0;
}

static long  sunxi_pwm_unlocked_ioctl(struct file *filp, unsigned int cmd,
        unsigned long arg)
{
    unsigned int size;
    struct pwm_config *code;
    struct group_pwm_config *group_config;
    int ret, i;

    static struct pwm_device *pwm;
    switch (cmd) {
    case PWM_CONFIG:
        size = _IOC_SIZE(cmd);
        code = (struct pwm_config *)kzalloc(size, GFP_KERNEL);
        if(IS_ERR_OR_NULL(code)){
            IRTX_ERR("not enough memory\n");
            return -ENOMEM;
        }

        if(copy_from_user(code, (void __user *)arg, size)){
            IRTX_ERR("copy buffer err\n");
            return -ENOMEM;
        }

        pwm = pwm_request(code->pwm_id, "sunxi_pwm0");
        if(IS_ERR(pwm)){
            IRTX_ERR("pwm err\n");
            return -ENOMEM;
        }else
            IRTX_ERR("pwm success\n");
        
//        pwm->chip_data = (struct pwm_config *)kzalloc(size, GFP_KERNEL);
        pwm->chip_data = code;
        
        ret = pwm_config(pwm, code->duty_ns, code->period_ns);
        if(ret < 0){
            IRTX_ERR("pwm ioctl err\n");
            return -ENOMEM;
        }

        pwm_set_polarity(pwm,code->pwm_polarity);
        
        kfree(code);
        break;
    case GROUP_PWM_CONFIG:
        size = _IOC_SIZE(cmd);
//        group_config = (struct group_pwm_config *)kzalloc(size, GFP_KERNEL);
        code = (struct pwm_config *)kzalloc(size, GFP_KERNEL);
        if(IS_ERR_OR_NULL(code)){
            IRTX_ERR("not enough memory\n");
            return -ENOMEM;
        }

        if(copy_from_user(code, (void __user *)arg, size)){
            IRTX_ERR("copy buffer err\n");
            return -ENOMEM;
        }

        pwm = pwm_request(0, "sunxi_group0");  //是否能重复request同一个id
        if(IS_ERR(pwm)){
            IRTX_ERR("pwm err\n");
            return -ENOMEM;
        }else
            IRTX_ERR("pwm success\n");
        
//        pwm->chip_data = (struct pwm_config *)kzalloc(size, GFP_KERNEL);
        pwm->chip_data = code;
        
//struct  pwm_chip *chip = to_sunxi_pwm_chip(pwm);
//struct  sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip);
struct  pwm_chip *chip = container_of(pwm, struct pwm_chip, pwms);    
struct    sunxi_pwm_chip *pc = container_of(chip, struct sunxi_pwm_chip, chip);
    
int group0_mask = pc->group.group0_mask;
        
        for (i = 0; i < 10; i ++) {
            if (group0_mask &= 1 << i) {
                pwm = pwm_request(i, "sunxi_group0");  //是否能重复request同一个id
                if(IS_ERR(pwm)){
                    IRTX_ERR("pwm err\n");
                    return -ENOMEM;
                }else
                    IRTX_ERR("pwm success\n");
                
                ret = pwm_config(pwm, code->duty_ns, code->period_ns);
                if(ret < 0){
                    IRTX_ERR("pwm ioctl err\n");
                    return -ENOMEM;
                }
            }
        }
        
        pwm_set_polarity(pwm,code->pwm_polarity);
        
        kfree(code);
        break;
    case PWM_ENABLE:
        pwm_enable(pwm);
        break;
    case PWM_DISABLE:
        pwm_disable(pwm);
        break;
    default:
        IRTX_ERR("a err cmd");
        return -ENOTTY;
    }
    return 0;
}

static long  sunxi_pwm_compat_ioctl(struct file *filp, unsigned int cmd,
        unsigned long arg)
{
    unsigned long translated_arg = (unsigned long)compat_ptr(arg);

    return sunxi_pwm_unlocked_ioctl(filp, cmd, translated_arg);
}

static const struct file_operations sunxi_pwm_fops = {
    .owner        = THIS_MODULE,
    .unlocked_ioctl    = sunxi_pwm_unlocked_ioctl,
    .compat_ioctl   = sunxi_pwm_compat_ioctl,
    .open        = sunxi_pwm_open,
    .release    = sunxi_pwm_release,
};

static int __init sunxi_pwm_init(void)
{
    int err = 0;
    struct device *dev;

    sunxi_pwm_dev= kzalloc(sizeof(struct sunxi_pwm_dev), GFP_KERNEL);
    if (sunxi_pwm_dev == NULL) {
        IRTX_ERR("kzalloc failed!\n");
        return -ENOMEM;
    }

    err = alloc_chrdev_region(&sunxi_pwm_dev->chrdev, 0, 1, "sunxi-pwm-dev");

    if (err) {
        IRTX_ERR("alloc_chrdev_region failed!\n");
        goto alloc_chrdev_err;
    }

    cdev_init(&(sunxi_pwm_dev->cdev), &sunxi_pwm_fops);
    sunxi_pwm_dev->cdev.owner = THIS_MODULE;
    err = cdev_add(&(sunxi_pwm_dev->cdev), sunxi_pwm_dev->chrdev, 1);
    if (err) {
        IRTX_ERR("cdev_add failed!\n");
        goto cdev_add_err;
    }

    sunxi_pwm_class = class_create(THIS_MODULE, "sunxi_pwm_char_class");
    if (IS_ERR(sunxi_pwm_class)) {
        err = PTR_ERR(sunxi_pwm_class);
        IRTX_ERR("class_create failed!\n");
        goto class_err;
    }

    dev = device_create(sunxi_pwm_class, NULL, sunxi_pwm_dev->chrdev, NULL,
            "sunxi_pwm%d", 0);
    if (IS_ERR(dev)) {
        err = PTR_ERR(dev);
        IRTX_ERR("device_create failed!\n");
        goto device_err;
    }

    return 0;

device_err:
    device_destroy(sunxi_pwm_class, sunxi_pwm_dev->chrdev);
class_err:
    cdev_del(&(sunxi_pwm_dev->cdev));
cdev_add_err:
    unregister_chrdev_region(sunxi_pwm_dev->chrdev, 1);
alloc_chrdev_err:
    kfree(sunxi_pwm_dev);

    return err;
}

static void __exit sunxi_pwm_exit(void)
{
    cdev_del(&(sunxi_pwm_dev->cdev));
    unregister_chrdev_region(sunxi_pwm_dev->chrdev, 1);
    device_destroy(sunxi_pwm_class, sunxi_pwm_dev->chrdev);
    class_destroy(sunxi_pwm_class);
    kfree(sunxi_pwm_dev);
}


module_init(sunxi_pwm_init);
module_exit(sunxi_pwm_exit);
MODULE_AUTHOR("Li huaxing");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("SUNXI_PWM char");

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值