嵌入式Linux驱动
在TI的芯片AM4378开发板上运行linux.
工具链是gcc-linaro-5.3-2016.02-x86_64_arm-linux-gnueabihf
板子内核版本4.1.18
主机Ubuntu18.x
添加QEP驱动
步骤如下:
1.在内核文件夹下linux-master/drivers新建文件夹qep
添加驱动文件tieqep.c
/*
* TI eQEP driver for AM33xx devices
* sysfs entries
* - position = absolute - current position; relative - last latched value
* - mode => 0 - absolute; 1 - relative
* - period => sampling period for the hardware
* - enable => 0 - eQEP disabled, 1 - eQEP enabled
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/pm_runtime.h>
#include <linux/of_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/input.h>
// Include the PWMSS subsystem headers, because this unit controls
// whether our clock source is enabled, and if eQEP is to be
// enabled, we need to start the clock
#include "../pwm/pwm-tipwmss.h"
// eQEP register offsets from its base IO address
#define QPOSCNT 0x0000
#define QPOSINIT 0x0004
#define QPOSMAX 0x0008
#define QPOSCMP 0x000C
#define QPOSILAT 0x0010
#define QPOSSLAT 0x0014
#define QPOSLAT 0x0018
#define QUTMR 0x001C
#define QUPRD 0x0020
#define QWDTMR 0x0024
#define QWDPRD 0x0026
#define QDECCTL 0x0028
#define QEPCTL 0x002A
#define QCAPCTL 0x002C
#define QPOSCTL 0x002E
#define QEINT 0x0030
#define QFLG 0x0032
#define QCLR 0x0034
#define QFRC 0x0036
#define QEPSTS 0x0038
#define QCTMR 0x003A
#define QCPRD 0x003C
#define QCTMRLAT 0x003E
#define QCPRDLAT 0x0040
#define QREVID 0x005C
// Bits for the QDECTL register
#define QSRC1 (0x0001 << 15)
#define QSRC0 (0x0001 << 14)
#define SOEN (0x0001 << 13)
#define SPSEL (0x0001 << 12)
#define XCR (0x0001 << 11)
#define SWAP (0x0001 << 10)
#define IGATE (0x0001 << 9)
#define QAP (0x0001 << 8)
#define QBP (0x0001 << 7)
#define QIP (0x0001 << 6)
#define QSP (0x0001 << 5)
// Bits for the QEPCTL register
#define FREESOFT1 (0x0001 << 15)
#define FREESOFT0 (0x0001 << 14)
#define PCRM1 (0x0001 << 13)
#define PCRM0 (0x0001 << 12)
#define SEI1 (0x0001 << 11)
#define SEI0 (0x0001 << 10)
#define IEI1 (0x0001 << 9)
#define IEI0 (0x0001 << 8)
#define SWI (0x0001 << 7)
#define SEL (0x0001 << 6)
#define IEL1 (0x0001 << 5)
#define IEL0 (0x0001 << 4)
#define PHEN (0x0001 << 3)
#define QCLM (0x0001 << 2)
#define UTE (0x0001 << 1)
#define WDE (0x0001 << 0)
// Bits for the QEPCTL register
#define QDF (0x0001 << 5)
// Bits for the QCAPCTL register
#define CEN (0x0001 << 15)
#define CCPS2 (0x0001 << 6)
#define CCPS0 (0x0001 << 5)
#define CCPS1 (0x0001 << 4)
#define UPPS3 (0x0001 << 3)
#define UPPS2 (0x0001 << 2)
#define UPPS1 (0x0001 << 1)
#define UPPS0 (0x0001 << 0)
// Bits for the QPOSCTL register
#define PCSHDW (0x0001 << 15)
#define PCLOAD (0x0001 << 14)
#define PCPOL (0x0001 << 13)
#define PCE (0x0001 << 12)
#define PCSPW11 (0x0001 << 11)
#define PCSPW10 (0x0001 << 10)
#define PCSPW9 (0x0001 << 9)
#define PCSPW8 (0x0001 << 8)
#define PCSPW7 (0x0001 << 7)
#define PCSPW6 (0x0001 << 6)
#define PCSPW5 (0x0001 << 5)
#define PCSPW4 (0x0001 << 4)
#define PCSPW3 (0x0001 << 3)
#define PCSPW2 (0x0001 << 2)
#define PCSPW1 (0x0001 << 1)
#define PCSPW0 (0x0001 << 0)
// Bits for the interrupt registers
#define EQEP_INTERRUPT_MASK (0x0FFF)
#define UTOF (0x0001 << 11)
// Bits to control the clock in the PWMSS subsystem
#define PWMSS_EQEPCLK_EN BIT(4)
#define PWMSS_EQEPCLK_STOP_REQ BIT(5)
#define PWMSS_EQEPCLK_EN_ACK BIT(4)
// Modes for the eQEP unit
// Absolute - the position entry represents the current position of the encoder.
// Poll this value and it will be notified every period nanoseconds
// Relative - the position entry represents the last latched position of the encoder
// This value is latched every period nanoseconds and the internal counter
// is subsequenty reset
#define TIEQEP_MODE_ABSOLUTE 0
#define TIEQEP_MODE_RELATIVE 1
// Structure defining the characteristics of the eQEP unit
struct eqep_chip
{
// Platform device for this eQEP unit
struct platform_device *pdev;
// Pointer to the base of the memory of the eQEP unit
void __iomem *mmio_base;
// SYSCLKOUT to the eQEP unit
u32 clk_rate;
// IRQ for the eQEP unit
u16 irq;
// Mode of the eQEP unit
u8 mode;
// work stuct for the notify userspace work
struct work_struct notify_work;
// Backup for driver suspension
u16 prior_qepctl;
u16 prior_qeint;
};
// Notify userspace work
void notify_handler (struct work_struct *work)
{
// Get a reference to the eQEP driver
struct eqep_chip *eqep = container_of(work, struct eqep_chip, notify_work);
// Notify the userspace
sysfs_notify(&eqep->pdev->dev.kobj, NULL, "position");
}
// eQEP Interrupt handler
static irqreturn_t eqep_irq_handler(int irq, void *dev_id)
{
// Get the instance information
struct platform_device *pdev = dev_id;
struct eqep_chip *eqep = platform_get_drvdata(pdev);
// Get the interrupt flags
u16 iflags = readw(eqep->mmio_base + QFLG) & EQEP_INTERRUPT_MASK;
// Check the interrupt source(s)
if(iflags & UTOF)
{
// Handle the unit timer overflow interrupt by notifying any potential pollers
schedule_work(&eqep->notify_work);
}