Forums

PWM Application for leopard dm365

  • I want to test PWM driver(Pulse width modulator) to illuminate LED sothat I want to write PWM application with desired frequency, duty cycle etc for leopard board dm365 on IPNC kernel 2.6.18 version. How to set frequency and duty cycle for registers in user space ?

     

    Can any body help me? Thanks in advance.

  • The kernel 2.6.18 from DVSDK 2.10 has a PWM driver (driver/char/davinci_pwm.c). You could use this driver to configure the frequency, duty cycle and etc. Note that pinmux registers should be set properly to see the PWM output on the IO pin.

     

    ramjeey

    I want to test PWM driver(Pulse width modulator) to illuminate LED sothat I want to write PWM application with desired frequency, duty cycle etc for leopard board dm365 on IPNC kernel 2.6.18 version. How to set frequency and duty cycle for registers in user space ?

     

    Can any body help me? Thanks in advance.

     

  • Thanks for reply Leon .

    But I think Pinmuxing has been done in driver in pwm init function so I did not do pinmuxing in my Appliction explicitly as below , But I  am not able to generate PWM as PWM0 using GIO23 (Pl note that this is not GPIO based PWM). Its an independent PWM generation so only I have to set various parameters in Application and sent to driver via ioctl function on /dev/davinci_pwm0 diver node. So please help me I am eagerly waiting for reply.

    #include <stdio.h>
    #include <sys/errno.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <sys/mman.h>
    #include <linux/ioctl.h>

    #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

    int main(int argc, char* argv[])
    {

        int fd = 0;
        int arg = 0;
        unsigned int cmd = 0;
        unsigned int status = -1;

          fd = open("/dev/davinci_pwm0", O_WRONLY);
       
        if (fd == -1) {
                    printf("open() failed!\n");
                    return -ENOENT;
            }

       
        /* configuring PWM0 in continuous mode for 100 Hz - 50% duty cycle*/
        cmd = PWMIOC_SET_MODE;
        arg = PWM_CONTINUOUS_MODE;

        status = ioctl(fd, cmd, &arg);
            if ( status == -1) {
                    printf("ioctl() failed!\n");
                    return -ENOENT;
        }

        cmd = PWMIOC_SET_PERIOD;
        arg = .01;  // As frequency is 100 period should be .01

        status = ioctl(fd, cmd, &arg);
            if ( status == -1) {
                    printf("ioctl() failed!\n");
                    return -ENOENT;
        }

        cmd = PWMIOC_SET_DURATION;
        arg = 3;

        status = ioctl(fd, cmd, &arg);
            if ( status == -1) {
                    printf("ioctl() failed!\n");
                    return -ENOENT;
        }

        cmd = PWMIOC_SET_RPT_VAL;
        arg = 0;

        status = ioctl(fd, cmd, &arg);
            if ( status == -1) {
                    printf("ioctl() failed!\n");
                    return -ENOENT;
        }

        cmd = PWMIOC_SET_FIRST_PHASE_STATE;
        arg = 0;

        status = ioctl(fd, cmd, &arg);
            if ( status == -1) {
                    printf("ioctl() failed!\n");
                    return -ENOENT;
        }

        cmd = PWMIOC_SET_INACT_OUT_STATE;
        arg = 0;

        status = ioctl(fd, cmd, &arg);
            if ( status == -1) {
                    printf("ioctl() failed!\n");
                    return -ENOENT;
        }

        cmd = PWMIOC_START;
        //arg = 1;
        printf("I m after pwm start\n");
        status = ioctl(fd, cmd);
            if ( status == -1) {
                    printf("ioctl() failed!\n");
                    return -ENOENT;
        }


        close(fd);

        return 0;
    }

     

    Please look into data sheet(PWM for Leopard board Dm365)  and Tell me pinmuxing  is required in Application or not.

  • I don't see PWM driver initializes pinmux in its driver. Maybe I missed or we are looking at different kernel. Can you post the code?

    Also, PWM0 is multiplexed with GIO92 or GIO23. So I think you will need to explicitly configure PINMUX.

  • /*

    * 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/interrupt.h> /* For IRQ_HANDLED and irqreturn_t */

    #include <asm/uaccess.h> /* for VERIFY_READ/VERIFY_WRITE/copy_from_user */

    #include <linux/clk.h>

    #include <linux/wait.h>

    #include <linux/platform_device.h>

    #include <asm/arch/hardware.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)

    struct pwm_davinci_device {

    char name[20];

    int intr_complete;

    dev_t devno;

    davinci_pwmregsovly regs;

    wait_queue_head_t intr_wait;

    struct clk *pwm_clk;

    };

    char *dm644x_name[] = { "PWM0_CLK", "PWM1_CLK", "PWM2_CLK" };

    char *dm646x_name[] = { "PWM0_CLK", "PWM1_CLK" };

    char *dm3xx_name[] = { "PWM0_CLK", "PWM1_CLK", "PWM2_CLK", "PWM3_CLK" };

    /* Instance of the private WDT 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;

    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_dev->regs->ph1d = 0xf;

    pwm_dev->regs->rpt = 1;

    pwm_dev->regs->cfg |= 0x1;

    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;

    /* This is called when the reference count goes to zero */

    return 0;

    }

    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) {

    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;

    case PWMIOC_SET_PERIOD:

    get_user(mode, (int *)arg);

    if (mode < 0 || mode > 0xffffffff)

    return -EINVAL;

    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;

    case PWMIOC_SET_DURATION:

    get_user(mode, (int *)arg);

    if (mode < 0 || mode > 0xffffffff)

    return -EINVAL;

    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;

    case PWMIOC_SET_RPT_VAL:

    get_user(mode, (int *)arg);

    if (mode < 0 || mode > 0xff)

    return -EINVAL;

    pwm_dev->regs->rpt = mode;

    break;

    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;

    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;

    case PWMIOC_START:

    pwm_dev->regs->start = 0x1;

    break;

    case PWMIOC_STOP:

    if (pwm_dev->regs->cfg & 0x1 && pwm_dev->regs->cfg & 0x20000)

    pwm_dev->regs->cfg &= 0xFFFFFFFC;

    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 *pwm_class;

    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);

    }

    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[i] = dm646x_name[i];

    } else if (cpu_is_davinci_dm355() || cpu_is_davinci_dm365()) {

    pwm_minor_count = DM3XX_PWM_MINORS;

    for (i = 0; i < pwm_minor_count; i++)

    name[i] = dm3xx_name[i];

    } else {

    pwm_minor_count = DM644X_PWM_MINORS;

    for (i = 0; i < pwm_minor_count; i++)

    name[i] = dm644x_name[i];

    }

    size = pwm_device_count * pwm_minor_count;

    /* Register the driver in the kernel */

    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_init(&c_dev, &pwm_fops);

    c_dev.owner = THIS_MODULE;

    c_dev.ops = &pwm_fops;

    /* addding character device */

    result = cdev_add(&c_dev, devno, pwm_minor_count);

    if (result) {

    printk("DaVinciPWM:Error adding DavinciPWM\n");

    unregister_chrdev_region(devno, size);

    return result;

    }

    pwm_class = class_create(THIS_MODULE, "davinci_pwm");

    if (!pwm_class) {

    cdev_del(&c_dev);

    return -EIO;

    }

    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);

    /* 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;

    }

    /* 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;

    }

    devno =

       MKDEV(pwm_major,

     pwm_minor_start + i * pwm_minor_count + j);

    class_device_create(pwm_class, NULL, devno, NULL,

    "davinci_pwm%d", j);

    /*

    * DM3XX has PWM3 IRQ at #28

    */

    if (j == 3) {

    result =

    request_irq(IRQ_DM3XX_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_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_enable(pwm_dev_array[j]->pwm_clk);

    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[i]);

    driver_unregister(&pwm_driver[i]);

    devno = MKDEV(pwm_major, pwm_minor_start + i);

    class_device_destroy(pwm_class,devno);

    if (i == 3)

    free_irq(IRQ_DM3XX_PWMINT3, pwm_dev_array[i]);

    else

    free_irq(IRQ_PWMINT0 + i, pwm_dev_array[i]);

    clk_disable(pwm_dev_array[i]->pwm_clk);

    kfree(pwm_dev_array[i]);

    }

    class_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");

  • Thanks Leo , This is code for davinci_pwm.c driver of IPNC 2.6.18 kernel, I am not sure in driver pinmultiplexing has been done in driver or not but with fd = open("/dev/davinci_pwm0", O_RDONLY); , mmap is getting failed.

    One more thing is that If you compare TMS320DM36x PWM and TMS320DM643x PWM , you will find in TMS320DM643x PWM data sheet pinmuxing has to be done because they wrote explicitly to do but not in DM36x.

    So I am confused should I do pinmuxing or not to select GIO23 not GIO92.

    /*

    * 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/interrupt.h> /* For IRQ_HANDLED and irqreturn_t */

    #include <asm/uaccess.h> /* for VERIFY_READ/VERIFY_WRITE/copy_from_user */

    #include <linux/clk.h>

    #include <linux/wait.h>

    #include <linux/platform_device.h>

    #include <asm/arch/hardware.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)

    struct pwm_davinci_device {

    char name[20];

    int intr_complete;

    dev_t devno;

    davinci_pwmregsovly regs;

    wait_queue_head_t intr_wait;

    struct clk *pwm_clk;

    };

    char *dm644x_name[] = { "PWM0_CLK", "PWM1_CLK", "PWM2_CLK" };

    char *dm646x_name[] = { "PWM0_CLK", "PWM1_CLK" };

    char *dm3xx_name[] = { "PWM0_CLK", "PWM1_CLK", "PWM2_CLK", "PWM3_CLK" };

    /* Instance of the private WDT 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;

    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_dev->regs->ph1d = 0xf;

    pwm_dev->regs->rpt = 1;

    pwm_dev->regs->cfg |= 0x1;

    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;

    /* This is called when the reference count goes to zero */

    return 0;

    }

    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) {

    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;

    case PWMIOC_SET_PERIOD:

    get_user(mode, (int *)arg);

    if (mode < 0 || mode > 0xffffffff)

    return -EINVAL;

    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;

    case PWMIOC_SET_DURATION:

    get_user(mode, (int *)arg);

    if (mode < 0 || mode > 0xffffffff)

    return -EINVAL;

    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;

    case PWMIOC_SET_RPT_VAL:

    get_user(mode, (int *)arg);

    if (mode < 0 || mode > 0xff)

    return -EINVAL;

    pwm_dev->regs->rpt = mode;

    break;

    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;

    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;

    case PWMIOC_START:

    pwm_dev->regs->start = 0x1;

    break;

    case PWMIOC_STOP:

    if (pwm_dev->regs->cfg & 0x1 && pwm_dev->regs->cfg & 0x20000)

    pwm_dev->regs->cfg &= 0xFFFFFFFC;

    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 *pwm_class;

    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);

    }

    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[i] = dm646x_name[i];

    } else if (cpu_is_davinci_dm355() || cpu_is_davinci_dm365()) {

    pwm_minor_count = DM3XX_PWM_MINORS;

    for (i = 0; i < pwm_minor_count; i++)

    name[i] = dm3xx_name[i];

    } else {

    pwm_minor_count = DM644X_PWM_MINORS;

    for (i = 0; i < pwm_minor_count; i++)

    name[i] = dm644x_name[i];

    }

    size = pwm_device_count * pwm_minor_count;

    /* Register the driver in the kernel */

    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_init(&c_dev, &pwm_fops);

    c_dev.owner = THIS_MODULE;

    c_dev.ops = &pwm_fops;

    /* addding character device */

    result = cdev_add(&c_dev, devno, pwm_minor_count);

    if (result) {

    printk("DaVinciPWM:Error adding DavinciPWM\n");

    unregister_chrdev_region(devno, size);

    return result;

    }

    pwm_class = class_create(THIS_MODULE, "davinci_pwm");

    if (!pwm_class) {

    cdev_del(&c_dev);

    return -EIO;

    }

    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);

    /* 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;

    }

    /* 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;

    }

    devno =

       MKDEV(pwm_major,

     pwm_minor_start + i * pwm_minor_count + j);

    class_device_create(pwm_class, NULL, devno, NULL,

    "davinci_pwm%d", j);

    /*

    * DM3XX has PWM3 IRQ at #28

    */

    if (j == 3) {

    result =

    request_irq(IRQ_DM3XX_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_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_enable(pwm_dev_array[j]->pwm_clk);

    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[i]);

    driver_unregister(&pwm_driver[i]);

    devno = MKDEV(pwm_major, pwm_minor_start + i);

    class_device_destroy(pwm_class,devno);

    if (i == 3)

    free_irq(IRQ_DM3XX_PWMINT3, pwm_dev_array[i]);

    else

    free_irq(IRQ_PWMINT0 + i, pwm_dev_array[i]);

    clk_disable(pwm_dev_array[i]->pwm_clk);

    kfree(pwm_dev_array[i]);

    }

    class_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");

  • Hi All,

    In consequence of above thread to configure pwm0 in continuous mode, EVTRIG has to set as per data sheet ,but I donot have any ioctl to set EVTRIG from application as per driver /driver/char/davinci_pwm0 in IPNC kernel 2.10.18.

    Is it (EVTRIG) register required to set to start pwm0?

    If yes , how to set EVTRIG from user space without any ioctl available?

  • We used the PWM driver in kernel 2.6.18 before. It works pretty well. And we configured the Pinmux and enable the GPIO in application. I guess the easiest way for you is to add the Pinmux and GPIO config registers in the PWM driver.

  • Hi Leo and All,

    Thanks for reply Leo .

    I generated PWM by pinmuxing gpio as pwm with desired frequency and duty cycle.

    Now I have to generate gpio based PWM means generate PWM by toggling GPIO pin (ON or OFF) with some dalay.

    Now I have problem with delay . After setting delay waveform on CRO is not uniform (variable period ).

    For setting delay I used sleep, setitimer and timer_create with delays in seconds , miliseconds and microsecond resepectively but for any delay waveforms are not uniform.

    It seems problem is with my code because for running gpio CPU load would not be high so sleep  , setitimer and timer_create should provide proper delay.

    I am configuring GPIO 92 for PWM by using pinmux for gpio, my code is as below have a look and suggest me solution ,

    I am struggling  a lot. As I am thinking sleep , setitimer and timer_create proves delay with precision seconds to some miliseconds.

    Now idea what to do?

    #include <linux/delay.h>

    #include <stdio.h>

    #include <string.h>

    #include <signal.h>

    #include <sys/time.h>

    #include <sys/errno.h>

    #include <sys/stat.h>

    #include <fcntl.h>

    #include <sys/mman.h>

    #define ENTRY_UNIT               0

    #define SYS_MODUL_ADD_MMAP       0x01C40000                               //starting Address of system module

    #define MMAP_START_GPIO_ADD      0x01C67000                                //starting Address of GPIO

    #define MMAP_SIZE                2048                                     //size of page

    #define PADCONFS_START           0x01C40000                               //starting Address of system module

    #define PADCONFS_START_GPIO      0x01C67000                               //starting Address of GPIO

    #define PADCONFS_SIZE            25                                       //No of bytes read by resister

    int rc = 0;

    int fd = 0;

    unsigned long *padconfig_start = 0;

    unsigned long *padconfig_start_gpio = 0;

    char *mmap_addr = 0;

    char *mmap_addr_gpio = 0;

    static int gpio_num = 92;                                        //to access into main function

    static int dir = 0;                                                //If dir is 1 dir == 0 becomes false evenif one passes dir = 0 from main function.

    muxoffset = 4;

    int bankoffset = 96;

    int main()

        {

            fd = open("/dev/mem", O_RDWR);

           if (fd == -1) {

                   printf("open() failed!\n");

                   return -ENOENT;

           }

           mmap_addr = mmap(0, MMAP_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED,

                           fd, (SYS_MODUL_ADD_MMAP & ~(MMAP_SIZE - 1)));

           if (mmap_addr == MAP_FAILED) {

                   printf("mmap() failed!\n");

                   return -ENOMEM;

           }

           muxoffset = muxoffset & (MMAP_SIZE -1);

           padconfig_start = (unsigned long *)(mmap_addr + (PADCONFS_START + muxoffset - SYS_MODUL_ADD_MMAP));

           padconfig_start[0] = padconfig_start[0] & 0xfffffffc; //PINMUXn to be used as GPIOm ,clear 0 & 1st bit

           mmap_addr_gpio = mmap(0, MMAP_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED,

                           fd, (MMAP_START_GPIO_ADD & ~(MMAP_SIZE - 1)));

           if (mmap_addr_gpio == MAP_FAILED) {

                   printf("mmap() failed!\n");

           }

                 bankoffset = bankoffset & (MMAP_SIZE -1); //added

           padconfig_start_gpio = (unsigned long *)(mmap_addr_gpio + (PADCONFS_START_GPIO + bankoffset - MMAP_START_GPIO_ADD)); //Address of Bank to set Direction

     if(0 == dir) //Direction is output

           {

                   padconfig_start_gpio[0] = padconfig_start_gpio[0]  & (~(1 << gpio_num%32)); // set DIR only when GPIO needs as O/P,Direction setting by clearing the bit

           }

    //      padconfig_start_gpio[1] = padconfig_start_gpio[1] ^ (1 << gpio_num%32);

           padconfig_start_gpio[1] = padconfig_start_gpio[1] ^ (1 << 28);

          while(1)

           {

                  // (padconfig_start_gpio[1]) = ~ (padconfig_start_gpio[1] & (1 << gpio_num%32));

                   (padconfig_start_gpio[1]) = ~ (padconfig_start_gpio[1] & (1 << 28));

                  sleep(1);

                }

    }