免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1038 | 回复: 0
打印 上一主题 下一主题

达芬奇DM644X平台BSP之davinci_pwm.c浅析 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-05-03 21:49 |只看该作者 |倒序浏览

                                                                               
                                --------------------------------------------本文系本站原创,欢迎转载!转载请注明出处:http://
zhiqiang0071.cublog.cn
--------------------------------------------
davinci_pwm.c:
/*
* Copyright (C) 2006 Texas Instruments Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
/* include Linux files */
#include linux/config.h>
#include linux/module.h>
#include linux/init.h>
#include linux/kernel.h>    /* printk() */
#include linux/slab.h>        /* kmalloc() */
#include linux/fs.h>        /* everything... */
#include linux/errno.h>    /* error codes */
#include linux/types.h>    /* size_t */
#include linux/cdev.h>        /* Used for struct cdev */
#include linux/dma-mapping.h>    /* For class_simple_create */
#include linux/interrupt.h>    /* For IRQ_HANDLED and irqreturn_t */
#include asm/uaccess.h>    /* for VERIFY_READ/VERIFY_WRITE/
                   copy_from_user */
#include linux/wait.h>
#include linux/devfs_fs_kernel.h>    /* for devfs */
#include asm/hardware/clock.h>
#include asm/arch/davinci_pwm.h>
#include asm/arch/cpu.h>
#include asm/semaphore.h>
#include asm/arch/irqs.h>
#define    DRIVER_NAME        "PWM"
#define    DAVINCI_PWM_TIMEOUT    (1*HZ)
/*
一、用ti的datasheet上的话来说明达芬奇平台pwm模块的特点:
    1.This PWM peripheral is basically a timer with a period counter and a first-phase
    duration comparator, where bit width of the period and first-phase duration are
    both programmable.
    2.The PWM has the following features:
    · 32-bit period counter
    · 32-bit first-phase duration counter
    · 8-bit repeat counter for one-shot operation. One-shot operation will produce
      N + 1 periods of the waveform, where N is the repeat counter value.
    · Configurable to operate in either one-shot or continuous mode.
    · One-shot operation can be triggered by the CCD VSYNC output of the video
      processing subsystem to allow any of the PWM instantiations to be used as a CCD timer.
    · Configurable PWM output pin inactive state.
    · Interrupt and EDMA synchronization events.
    · Emulation support for stop or free-run operation.
二、应用程序调用过程
    1.使用open()打开;
    2.使用ioctl()设置参数。
三、调试中可能出现的问题
    1.应用程序使用ioctl调用时,最后一个形参必须是指针,从驱动中能看出;
    2.一般没什么大难题。
*/
/* 用于管理达芬奇平台上的每个pwm接口 */
struct pwm_davinci_device {
    char name[20];
    int intr_complete;
    dev_t devno;    // 字符设备号
    davinci_pwmregsovly regs;    // 该pwm接口的物理寄存器结构体
    wait_queue_head_t intr_wait;
    struct clk *pwm_clk;    // 该pwm接口的时钟
};
char *dm644x_name[] = { "PWM0_CLK", "PWM1_CLK", "PWM2_CLK" };
char *dm646x_name[] = { "PWM0_CLK", "PWM1_CLK" };
char *dm355_name[] = { "PWM0_CLK", "PWM1_CLK", "PWM2_CLK", "PWM3_CLK" };
/* Instance of the private PWM device structure */
static struct pwm_davinci_device *pwm_dev_array[DAVINCI_PWM_MINORS];
static DEFINE_SPINLOCK(pwm_dev_array_lock);
static unsigned int pwm_major = 0;
static unsigned int pwm_minor_start = 0;
static unsigned int pwm_minor_count = DM644X_PWM_MINORS;
static unsigned int pwm_device_count = 1;
/* For registeration of charatcer device*/
static struct cdev c_dev;
/* 通过次设备号来获得该pwm接口的控制机构体 */
struct pwm_davinci_device *pwm_dev_get_by_minor(unsigned index)
{
    struct pwm_davinci_device *pwm_dev;
    spin_lock(&pwm_dev_array_lock);
    pwm_dev = pwm_dev_array[index];
    spin_unlock(&pwm_dev_array_lock);
    return pwm_dev;
}
static loff_t pwm_llseek(struct file *file, loff_t offset, int whence)
{
    return -ESPIPE;        /* Not seekable */
}
/* 字符设备驱动的打开函数 */
static int pwm_open(struct inode *inode, struct file *file)
{
    unsigned int minor = iminor(inode);
    struct pwm_davinci_device *pwm_dev;
    pwm_dev = pwm_dev_get_by_minor(minor);
    /* sample configuration */
    pwm_dev->regs->per = 0xf;    // pwm总周期
    pwm_dev->regs->ph1d = 0xf;    // 第一相的周期
    pwm_dev->regs->rpt = 1;        // One-shot mode下的重复次数
    pwm_dev->regs->cfg |= 0x1;    // 配置成One shot mode
    pwm_dev->intr_complete = 0;
    return 0;
}
/* 字符设备驱动的关闭函数, */
static int pwm_release(struct inode *inode, struct file *file)
{
    unsigned int minor = iminor(inode);
    struct pwm_davinci_device *pwm_dev;
    pwm_dev = pwm_dev_get_by_minor(minor);
    pwm_dev->regs->cfg &= 0xFFFFFFFC;    // 关闭pwm
    /* This is called when the reference count goes to zero */
    return 0;
}
/* 字符设备驱动的ioctl函数,用于设置pwm参数,控制pwm运行模式*/
int pwm_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
          unsigned long arg)
{
    int mode;
    unsigned int minor = iminor(inode);
    struct pwm_davinci_device *pwm_dev;
    pwm_dev = pwm_dev_get_by_minor(minor);
    switch (cmd) {
    /* 设置pwm模式,有两种模式,One-Shot Mode和Continuous Mode */
    case PWMIOC_SET_MODE:
        if (pwm_dev->regs->cfg & 0x20000)
            return -EBUSY;
        get_user(mode, (int *)arg);
        if (mode == PWM_ONESHOT_MODE) {
            pwm_dev->regs->cfg &= 0xFFFFFFFC;
            pwm_dev->regs->cfg |= 0x1;
        } else if (mode == PWM_CONTINUOUS_MODE) {
            pwm_dev->regs->cfg &= 0xFFFFFFFC;
            pwm_dev->regs->cfg |= 0x2;
        } else
            return -EINVAL;
        break;
    /*
    设置pwm周期,设置时如果pwm运行在continuous模式并且正在运行,那么必须的等待pwm一个
    周期完成后(通过产生中断来同步)再设置per寄存器
    */
    case PWMIOC_SET_PERIOD:
        get_user(mode, (int *)arg);
        if (mode  0 || mode > 0xffffffff)
            return -EINVAL;
        // 检测pwm是否运行在continuous模式
        if (pwm_dev->regs->cfg & 0x2 && pwm_dev->regs->cfg & 0x20000) {
            if (mode  7)
                return -EINVAL;
            /* Enable PWM interrupts */
            pwm_dev->regs->cfg |= 0x40;
            /* wait for the transaction to complete */
            wait_event_timeout(pwm_dev->intr_wait,
                       pwm_dev->intr_complete,
                       DAVINCI_PWM_TIMEOUT);
            if (pwm_dev->intr_complete)
                pwm_dev->regs->per = mode;
            else
                return -1;
        } else
            pwm_dev->regs->per = mode;
        break;
        
    /*
    设置pwm第一相周期,设置时如果pwm运行在continuous模式并且正在运行,那么必须的等待pwm一个
    周期完成后(通过产生中断来同步)再设置ph1d寄存器
    */
    case PWMIOC_SET_DURATION:
        get_user(mode, (int *)arg);
        if (mode  0 || mode > 0xffffffff)
            return -EINVAL;
        // 检测pwm是否运行在continuous模式
        if (pwm_dev->regs->cfg & 0x2 && pwm_dev->regs->cfg & 0x20000) {
            /* Enable PWM interrupts */
            pwm_dev->regs->cfg |= 0x40;
            /* wait for the transaction to complete */
            wait_event_timeout(pwm_dev->intr_wait,
                       pwm_dev->intr_complete,
                       DAVINCI_PWM_TIMEOUT);
            if (pwm_dev->intr_complete)
                pwm_dev->regs->ph1d = mode;
            else
                return -1;
        } else
            pwm_dev->regs->ph1d = mode;
        break;
        
    /* 在one-shot operation,设置重复次数 */
    case PWMIOC_SET_RPT_VAL:
        get_user(mode, (int *)arg);
        if (mode  0 || mode > 0xff)
            return -EINVAL;
        pwm_dev->regs->rpt = mode;
        break;
        
    /* 设置First-phase output level,也就是第一相的输出电平 */
    case PWMIOC_SET_FIRST_PHASE_STATE:
        get_user(mode, (int *)arg);
        if (pwm_dev->regs->cfg & 0x20000)
            return -EBUSY;
        if (mode == 1)    // 输出高电平
            pwm_dev->regs->cfg |= 0x10;
        else if (mode == 0)    // 输出低电平
            pwm_dev->regs->cfg &= ~0x10;
        else
            return -EINVAL;
        break;
        
    /* 设置Inactive output level,也就是pwm停止时的输出电平 */
    case PWMIOC_SET_INACT_OUT_STATE:
        get_user(mode, (int *)arg);
        if (pwm_dev->regs->cfg & 0x20000)
            return -EBUSY;
        if (mode == 1)
            pwm_dev->regs->cfg |= 0x20;
        else if (mode == 0)
            pwm_dev->regs->cfg &= ~0x20;
        else
            return -EINVAL;
        break;
        
    /* pwm开始运行 */
    case PWMIOC_START:
        pwm_dev->regs->start = 0x1;
        break;
        
    /* pwm停止。针对不同的运行模式采取不同的操作 */
    case PWMIOC_STOP:
        // 如果运行在one-shot mode,则直接停止
        if (pwm_dev->regs->cfg & 0x1 && pwm_dev->regs->cfg & 0x20000)
            pwm_dev->regs->cfg &= 0xFFFFFFFC;
        /* 如果运行在continuous mode,则通过中断来同步,先设置成one-shot mode,运行完后
        自然就停止了。
        */
        if (pwm_dev->regs->cfg & 0x2 && pwm_dev->regs->cfg & 0x20000) {
            unsigned long temp;
            temp = pwm_dev->regs->cfg;
            temp &= 0xFFFFFFFC;
            temp |= 0x1;
            /* Enable PWM interrupts */
            pwm_dev->regs->cfg |= 0x40;
            /* wait for the transaction to complete */
            wait_event_timeout(pwm_dev->intr_wait,
                       pwm_dev->intr_complete,
                       DAVINCI_PWM_TIMEOUT);
            if (pwm_dev->intr_complete)
                pwm_dev->regs->cfg = temp;
            else
                return -1;
        }
        break;
    }
    return 0;
}
static int pwm_remove(struct device *device)
{
    return 0;
}
static void pwm_platform_release(struct device *device)
{
    /* this function does nothing */
}
/* 字符设备驱动的例程集 */
static struct file_operations pwm_fops = {
    .owner = THIS_MODULE,
    .llseek = pwm_llseek,
    .open = pwm_open,
    .release = pwm_release,
    .ioctl = pwm_ioctl,
};
static struct class_simple *pwm_class = NULL;
static struct platform_device pwm_device[] = {
    [0] = {
        .name = "davinci_pwm0",
        .id = 0,
        .dev = {
            .release = pwm_platform_release,
        }
    },
    [1] = {
        .name = "davinci_pwm1",
        .id = 1,
        .dev = {
            .release = pwm_platform_release,
        }
    },
    [2] = {
        .name = "davinci_pwm2",
        .id = 2,
        .dev = {
            .release = pwm_platform_release,
        }
    },
    [3] = {.name = "davinci_pwm3",
           .id = 3,
           .dev =  {
            .release = pwm_platform_release,
        }
    }
};
static struct device_driver pwm_driver[] = {
    [0] = {
        .name = "davinci_pwm0",
        .bus = &platform_bus_type,
        .remove = pwm_remove
    },
    [1] = {
        .name = "davinci_pwm1",
        .bus = &platform_bus_type,
        .remove = pwm_remove
    },
    [2] = {
        .name = "davinci_pwm2",
        .bus = &platform_bus_type,
        .remove = pwm_remove
    },
    [3] = {
        .name = "davinci_pwm3",
        .bus = &platform_bus_type,
        .remove = pwm_remove
    },
};
/*
* This function marks a transaction as complete.
*/
static inline void pwm_davinci_complete_intr(struct pwm_davinci_device *dev)
{
    dev->intr_complete = 1;
    wake_up(&dev->intr_wait);
}
/* pwm中断处理程序,用于同步,设置某些寄存器 */
static irqreturn_t pwm_isr(int irq, void *dev_id, struct pt_regs *regs)
{
    struct pwm_davinci_device *dev = dev_id;
    /* Disable PWM interrupts */
    dev->regs->cfg &= ~0x40;
    pwm_davinci_complete_intr(dev);
    return IRQ_HANDLED;
}
/* 驱动模块加载的初始化函数 */
static int __init pwm_init(void)
{
    int result;
    dev_t devno;
    unsigned int size, i, j;
    char *name[DAVINCI_PWM_MINORS];
    if (cpu_is_davinci_dm6467()) {
        pwm_minor_count = DM646X_PWM_MINORS;
        for (i = 0; i  pwm_minor_count; i++)
            name = dm646x_name;
    } else if (cpu_is_davinci_dm355()) {
        pwm_minor_count = DM355_PWM_MINORS;
        for (i = 0; i  pwm_minor_count; i++)
            name = dm355_name;
    } else {
        pwm_minor_count = DM644X_PWM_MINORS;
        for (i = 0; i  pwm_minor_count; i++)
            name = dm644x_name;
    }
    size = pwm_device_count * pwm_minor_count;
    /* Register the driver in the kernel */
    /* 自动分配主设备号并分配size个次设备号*/
    result = alloc_chrdev_region(&devno, 0, size, DRIVER_NAME);
    if (result  0) {
        printk("DaVinciPWM: Module intialization failed.\
                        could not register character device\n");
        return -ENODEV;
    }
    // 获取主设备号
    pwm_major = MAJOR(devno);
    /* Initialize of character device */
    /* 初始化cdev,kobj,并注册c_dev.ops */
    cdev_init(&c_dev, &pwm_fops);
    c_dev.owner = THIS_MODULE;
    c_dev.ops = &pwm_fops;
    /* addding character device */
    /*
       将该c_dev加入到kobj_map中。这样当应用程序调用open()时,便能根据设备号找到该cdev。查找cdev
       是在chrdev_open()例程中进行的,在该例程中,找到cdev后会将其注册到inode->i_cdev,并将
       inode->i_devices加入到该cdev->list()(list_add(&inode->i_devices, &p->list)),
       再将该cdev->ops注册到filp->f_op,最后调用filp->f_op->open(inode,filp);
     */
    result = cdev_add(&c_dev, devno, pwm_minor_count);
    if (result) {
        printk("DaVinciPWM:Error adding DavinciPWM\n");
        unregister_chrdev_region(devno, size);
        return result;
    }
    /* 生成一个simple class类并注册到subsys */
    pwm_class = class_simple_create(THIS_MODULE, "davinci_pwm");
    if (!pwm_class) {
        cdev_del(&c_dev);
        return -EIO;
    }
    /*
     注册所有达芬奇平台pwm接口的device driver和platform device;将device加入到
     pwm_class,在/dev/下生成相应的节点,并开启时钟。
    */
    for (i = 0; i  pwm_device_count; i++) {
        for (j = 0; j  pwm_minor_count; j++) {
            pwm_dev_array[j] =
                kmalloc(sizeof(struct pwm_davinci_device),GFP_KERNEL);
            pwm_dev_array[j]->devno = devno;
            init_waitqueue_head(&pwm_dev_array[j]->intr_wait);
            sprintf(pwm_dev_array[j]->name, "davinci_pwm%d", j);
            /* 注册为 platform driver */
            /* register driver as a platform driver */
            if (driver_register(&pwm_driver[j]) != 0) {
                unregister_chrdev_region(devno, size);
                cdev_del(&c_dev);
                kfree(pwm_dev_array[j]);
                return -EINVAL;
            }
            /* 注册为 platform device */
            /* Register the drive as a platform device */
            if (platform_device_register(&pwm_device[j]) != 0) {
                driver_unregister(&pwm_driver[j]);
                unregister_chrdev_region(devno, size);
                cdev_del(&c_dev);
                kfree(pwm_dev_array[j]);
                return -EINVAL;
            }
            /* 在/dev目录下生成节点 */
            devno =
                MKDEV(pwm_major,
                  pwm_minor_start + i * pwm_minor_count + j);
            class_simple_device_add(pwm_class, devno, NULL,
                        "davinci_pwm%d", j);
            /* 注册中断例程 */
            /*
             * DM355 has PWM3 IRQ at #28
             */
            if (j == 3) {
                result =
                    request_irq(IRQ_DM355_PWMINT3, pwm_isr,
                        SA_INTERRUPT,
                        pwm_dev_array[j]->name,
                        pwm_dev_array[j]);
            } else {
                result = request_irq(IRQ_PWMINT0 + j,
                        pwm_isr, SA_INTERRUPT,
                        pwm_dev_array[j]->name,
                        pwm_dev_array[j]);
            }
            if (result  0) {
                printk("Cannot initialize IRQ \n");
                platform_device_unregister(&pwm_device[j]);
                driver_unregister(&pwm_driver[j]);
                kfree(pwm_dev_array[j]);
                return result;
            }
            /* 打开pwm模块时钟 */
            pwm_dev_array[j]->pwm_clk = clk_get(NULL, *(name + j));
            if (IS_ERR(pwm_dev_array[j]->pwm_clk)) {
                printk("Cannot get clock\n");
                return -1;
            }
            clk_use(pwm_dev_array[j]->pwm_clk);
            clk_enable(pwm_dev_array[j]->pwm_clk);
            /* 获得该pwm接口的物理寄存器地址 */
            pwm_dev_array[j]->regs =
                (davinci_pwmregsovly) IO_ADDRESS(DAVINCI_PWM0_BASE +
                                 j * 0x400);
        }
    }
    return 0;
}
/* 驱动模块卸载时调用的函数 */
static void __exit pwm_exit(void)
{
    dev_t devno;
    unsigned int size, i;
    /* 释放掉所有申请的资源 */
    if (pwm_class != NULL) {
        size = pwm_device_count * pwm_minor_count;
        for (i = 0; i  size; i++) {
            platform_device_unregister(&pwm_device);
            driver_unregister(&pwm_driver);
            devno = MKDEV(pwm_major, pwm_minor_start + i);
            class_simple_device_remove(devno);
            if ((i == 3) && (cpu_is_davinci_dm355()))
                free_irq(IRQ_DM355_PWMINT3, pwm_dev_array);
            else
                free_irq(IRQ_PWMINT0 + i, pwm_dev_array);
            clk_unuse(pwm_dev_array->pwm_clk);
            clk_disable(pwm_dev_array->pwm_clk);
            kfree(pwm_dev_array);
        }
        class_simple_destroy(pwm_class);
    }
    cdev_del(&c_dev);
    /* Release major/minor numbers */
    if (pwm_major != 0) {
        devno = MKDEV(pwm_major, pwm_minor_start);
        size = pwm_device_count * pwm_minor_count;
        unregister_chrdev_region(devno, size);
    }
}
module_init(pwm_init);
module_exit(pwm_exit);
MODULE_AUTHOR("Texas Instruments");
MODULE_LICENSE("GPL");
davinci_pwm.h:
/*
*  linux/drivers/char/davinci_pwm.h
*
*  BRIEF MODULE DESCRIPTION
*      DaVinci PWM register definitions
*
*  Copyright (C) 2006 Texas Instruments.
*
*  This program is free software; you can redistribute  it and/or modify it
*  under  the terms of  the GNU General  Public License as published by the
*  Free Software Foundation;  either version 2 of the  License, or (at your
*  option) any later version.
*
*  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
*  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
*  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
*  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
*  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
*  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
*  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
*  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
*  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
*  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*  You should have received a copy of the  GNU General Public License along
*  with this program; if not, write  to the Free Software Foundation, Inc.,
*  675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _DAVINCI_PWM_H
#define _DAVINCI_PWM_H
/**************************************************************************\
* Register Overlay Structure
\**************************************************************************/
typedef struct {
    unsigned int pid;
    unsigned int pcr;
    unsigned int cfg;
    unsigned int start;
    unsigned int rpt;
    unsigned int per;
    unsigned int ph1d;
} davinci_pwmregs;
/**************************************************************************\
* Overlay structure typedef definition
\**************************************************************************/
typedef volatile davinci_pwmregs *davinci_pwmregsovly;
#define PWM_MINORS        3
#define DM646X_PWM_MINORS    2
#define DM644X_PWM_MINORS    3
#define DM355_PWM_MINORS    4
#define DAVINCI_PWM_MINORS    DM355_PWM_MINORS /* MAX of all PWM_MINORS */
#define    PWMIOC_SET_MODE            0x01
#define    PWMIOC_SET_PERIOD        0x02
#define    PWMIOC_SET_DURATION        0x03
#define    PWMIOC_SET_RPT_VAL        0x04
#define    PWMIOC_START            0x05
#define    PWMIOC_STOP            0x06
#define    PWMIOC_SET_FIRST_PHASE_STATE    0x07
#define    PWMIOC_SET_INACT_OUT_STATE    0x08
#define    PWM_ONESHOT_MODE    0
#define    PWM_CONTINUOUS_MODE    1
#endif                /* _DAVINCI_PWM_H */
               
               
               
               
               
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/79779/showart_1917274.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP