处理映射的寄存器后,LINUX KERNEL驱动程序挂起/冻结

时间:2018-09-28 02:30:24

标签: linux-kernel linux-device-driver ioremap

我是LINUX内核的全新开发人员,并且在我正在开发的新LINUX驱动程序中遇到一些问题。

使用ioremap()/ ioremap_nocache()映射NXP PWM寄存器后,然后尝试写入寄存器mappend,使我的系统处于停滞状态/冻结状态。

您能帮我了解发生了什么吗?

我的驱动程序是这样:

#include <linux/device.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kthread.h>  // for threads
#include <linux/fs.h>
#include <linux/sched.h>    // for task_struct
#include <linux/delay.h>    // for ndelay
#include <linux/uaccess.h>  // Required for the copy to user function
#include <asm/io.h>         // for ioremap()

#include <linux/interrupt.h>
#include <linux/gpio.h>


#define MX3_PWMCR_PRESCALER(x)      ((((x) - 1) & 0xFFF) << 4)
#define DEVICE_NAME "pwm_cus_drv"
#define CLASS_NAME  "pwm_custom_driver"



static volatile void __iomem *mmio_pwm1_base = NULL;
static volatile void __iomem *mmio_pwm2_base = NULL;

static int    majorNumber;
static struct class*  vfd_char_dev_class  = NULL;
static struct device* vfd_char_dev = NULL;
static struct device_driver vfd_driver;


static int     dev_open(struct inode *inodep, struct file *file_ptr);
static int     dev_release(struct inode *inodep, struct file *file_ptr);
static ssize_t dev_read(struct file *file_ptr, char *buffer, size_t len, loff_t *offset);
static ssize_t dev_write(struct file *file_ptr, char *buffer, size_t len, loff_t *offset);
static long dev_ioctl(struct file *file_ptr, unsigned int cmd, unsigned long arg);
static irqreturn_t pwm_imx_futaba_isr(int irq, void *dev_id);
static bool Initialize_PWM_Signals(void);
static void pwm_init(void);


/**
 * ISR used to attend PWM rising edge interrupt activation.
 */
irqreturn_t pwm_imx_futaba_isr(int irq, void *dev_id)
{
   if(NULL != mmio_pwm1_base)
   {
      writel(0x00000078, mmio_pwm1_base + 0x04);

      gpio_set_value(47, 1);
      gpio_set_value(47, 0);
   }

   return IRQ_HANDLED;
}

/**
 *
 */
bool Initialize_PWM_Signals(void)
{
   u32 cs_pin_dir_value = 0;
   u32 cs_pin_out_value = 0;
   u32 duty_cycles = 0;
   u32 period_cycles = 0;
   u32 cr_1 = 0;
   u32 cr_2 = 0;

   pwm_init();

   period_cycles = ((24000000)/(4000)) - 2; /* 4 KHz     */
   duty_cycles = period_cycles / 2;         /* duty = 50% */
   printk(KERN_NOTICE "PWM data. PERIOD[%d] DUTY[%d]\n", period_cycles, duty_cycles);

   cr_1 = MX3_PWMCR_PRESCALER(1) | (1 << 24) | (1 << 23) | (2 << 16);
   cr_2 = MX3_PWMCR_PRESCALER(1) | (1 << 24) | (1 << 23) | (2 << 16);

   printk(KERN_NOTICE "Disabling IMX6UL PWMs \n"); 

   /*******************************/
   /* AFTER THIS, THE KERNEL HANGS*/
   /*******************************/

   writel(cr_1, mmio_pwm1_base + 0x00);
   writel(cr_2, mmio_pwm2_base + 0x00);


   printk(KERN_NOTICE "PWMs disabled\n");

   if (1)
   {
      /* Configure IMX6UL PWM1 */
      printk(KERN_NOTICE " Configuring PWM1 \n");
      writel(duty_cycles, mmio_pwm1_base + 0x0C);
      writel(period_cycles, mmio_pwm1_base + 0x10);

      /* Configure IMX6UL PWM2 */
      printk(KERN_NOTICE " Configuring PWM2 \n");
      writel(duty_cycles, mmio_pwm2_base + 0x0C);
      writel(period_cycles, mmio_pwm2_base + 0x10);

      cr_1 |= (1 << 0);
      cr_2 |= (1 << 0);

      printk(KERN_NOTICE "Enabling IRQs !!\n");
      writel(0x00000002, mmio_pwm1_base + 0x08);

      /* Enabling IMX6UL PWMs */
      printk(KERN_NOTICE " Enabling PWMs \n");
      writel(cr_1, mmio_pwm1_base + 0x00);
      writel(cr_2, mmio_pwm2_base + 0x00);
   }

   return 0;
}

/**
 *
 */
int dev_open(struct inode *inodep, struct file *file_ptr)
{
   printk(KERN_NOTICE "\n[%s]\n", __func__);

   Initialize_PWM_Signals();

   printk(KERN_NOTICE "[%s] Driver initialized \n", __func__);
}

/**
 *
 */
int dev_release(struct inode *inodep, struct file *file_ptr)
{
   printk(KERN_NOTICE "\n[%s]\n", __func__);
}

/**
 *
 */
ssize_t dev_read(struct file *file_ptr, char *buffer, size_t len, loff_t *offset)
{
   printk(KERN_NOTICE "\n[%s]\n", __func__);
}

/**
 *
 */
ssize_t dev_write(struct file *file_ptr, char *buffer, size_t len, loff_t *offset)
{
   printk(KERN_NOTICE "\n[%s]\n", __func__);
}

/**
 *
 */
long dev_ioctl(struct file *file_ptr, unsigned int cmd, unsigned long arg)
{
   printk(KERN_NOTICE "\n[%s]\n", __func__);
}

/**
 *
 */
void pwm_init(void)
{
   printk(KERN_ALERT "[%s]\n", __func__);

   if(NULL != request_mem_region(0x2080000, 0x4000, DEVICE_NAME))
   {
      mmio_pwm1_base = ioremap_nocache(0x2080000, 0x4000);

      if(IS_ERR(mmio_pwm1_base))
      {
         printk(KERN_NOTICE "Failed to map memory 1\n");
      }
   }
   else
   {
      printk(KERN_NOTICE "Failed to map memory 2\n");
   }

   if(NULL != request_mem_region(0x2084000, 0x4000, DEVICE_NAME))
   {
      mmio_pwm2_base = ioremap_nocache(0x2084000, 0x4000);

      if(IS_ERR(mmio_pwm2_base))
      {
         printk(KERN_NOTICE "Failed to map memory 3\n");
      }
   }
   else
   {
      printk(KERN_NOTICE "Failed to map memory 4\n");
   }

   printk(KERN_NOTICE "PWMs memory mapped \n");

}

static const struct file_operations fops =
{
   .owner  = THIS_MODULE,
   .open = dev_open,
   .read = dev_read,
   .write = dev_write,
   .release = dev_release,
   .unlocked_ioctl = dev_ioctl,
   .compat_ioctl = dev_ioctl,
};

struct bus_type futaba_bus_type =
{
   .name       = DEVICE_NAME,
};

static int pwm_driver_init(void)
{
   unsigned irqflags = 0;
   unsigned ret = 0;
   const char *dev_name = "pwm1_irq";
   u32 pwm_irq = 25;

   majorNumber = register_chrdev(0, DEVICE_NAME, &fops);

   if (majorNumber < 0)
   {
      printk(KERN_NOTICE "EBBChar failed to register a major number\n");
      return majorNumber;
   }

   vfd_char_dev_class = class_create(THIS_MODULE, CLASS_NAME);
   if (IS_ERR(vfd_char_dev_class))
   {
      unregister_chrdev(majorNumber, DEVICE_NAME);
      printk(KERN_NOTICE "Failed to register device class\n");

      return PTR_ERR(vfd_char_dev_class);
   }

   vfd_char_dev = device_create(vfd_char_dev_class, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME);
   if (IS_ERR(vfd_char_dev))
   {
      class_destroy(vfd_char_dev_class);
      unregister_chrdev(majorNumber, DEVICE_NAME);
      printk(KERN_ALERT "Failed to create the device\n");

      return PTR_ERR(vfd_char_dev);
   }

   ret = request_irq(pwm_irq, pwm_imx_futaba_isr, irqflags, dev_name, DEVICE_NAME);
   if (0 != ret)
   {
      printk(KERN_NOTICE "can't get irq: %d\n", ret);
   }

   return 0;
}

static void pwm_driver_exit(void)
{
   device_destroy(vfd_char_dev_class, MKDEV(majorNumber, 0));
   class_unregister(vfd_char_dev_class);
   class_destroy(vfd_char_dev_class);
   unregister_chrdev(majorNumber, DEVICE_NAME);

   iounmap(mmio_pwm1_base);
   iounmap(mmio_pwm2_base);
}

module_init(pwm_driver_init);
module_exit(pwm_driver_exit);

MODULE_AUTHOR("New Drivers developer");
MODULE_DESCRIPTION(" PWM Handler ");
MODULE_LICENSE("GPL");

2 个答案:

答案 0 :(得分:1)

感谢您的所有回答。使用一些NXP建议后,我解决了此问题。最后,问题是由于未启用IPG / PER时钟引起的。根据恩智浦的说法,必须先启用时钟,然后再开始修改与模块关联的寄存器(在我的示例中为PWM)。

答案 1 :(得分:0)

挂起寄存器访问的典型原因是拥有寄存器的硬件模块已掉电或未计时。

每个来自主线内核(arch / arm / boot / dts / imx6ul.dtsi)的imx6ul设备树,都有启用时钟:

                    pwm1: pwm@2080000 {
                            compatible = "fsl,imx6ul-pwm", "fsl,imx27-pwm";
                            reg = <0x02080000 0x4000>;
                            interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>;
                            clocks = <&clks IMX6UL_CLK_PWM1>,
                                     <&clks IMX6UL_CLK_PWM1>;
                            clock-names = "ipg", "per";
                            #pwm-cells = <2>;
                            status = "disabled";
                    };

顺便说一下,该模块的驱动程序可用,可以是drivers / pwm / pwm-imx.c