LINUX KERNEL驱动程序在处理映射的寄存器后挂起/冻结 [英] LINUX KERNEL driver hangs/freeze after handling mapped register

查看:142
本文介绍了LINUX KERNEL驱动程序在处理映射的寄存器后挂起/冻结的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm completely new developing in LINUX kernel, and I'm having some problems in a new LINUX driver I'm developing.

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

After I map NXP PWM registers using ioremap()/ioremap_nocache() and then I try to write to the register mappend my system hags/freeze.

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

Could you please help me understanding what it is happening?

我的驱动程序是这样:

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

推荐答案

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

Typical reason of hang at register access is that hardware module owning the register is either powered down, or not clocked.

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

Per imx6ul device tree from mainline kernel (arch/arm/boot/dts/imx6ul.dtsi), there are clocks to enable:

                    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

By the way, driver for this module is available, drivers/pwm/pwm-imx.c

这篇关于LINUX KERNEL驱动程序在处理映射的寄存器后挂起/冻结的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆