Драйвер LINUX KERNEL зависает / зависает после обработки сопоставленного регистра - PullRequest
0 голосов
/ 28 сентября 2018

Я занимаюсь разработкой ядра Linux, и у меня возникли некоторые проблемы с новым драйвером LINUX, который я разрабатываю.

После того, как я отобразил регистры NXP PWM, используя ioremap () / ioremap_nocache ()и затем я пытаюсь записать в регистр mappend моей системы hags / freeze.

Не могли бы вы помочь мне понять, что происходит?

Мой драйвер такой:

#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 голосов
/ 31 января 2019

Спасибо за все ваши ответы.Я решил эту проблему после использования некоторых рекомендаций NXP.В конце проблема была вызвана тем, что часы IPG / PER не были включены.Согласно NXP, часы должны быть включены в первую очередь перед началом изменения регистров, связанных с модулем (в моем случае для ШИМ).

0 голосов
/ 08 октября 2018

Типичная причина зависания при доступе к регистру состоит в том, что аппаратный модуль, владеющий регистром, либо выключен, либо не синхронизирован.

Для дерева устройств imx6ul из основного ядра (arch / arm / boot / dts / imx6ul).dtsi), есть часы для включения:

                    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

...