1. 根据硬件工程生成设备数文件 dts
/ {
amba_pl: amba_pl {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
ranges ;
axi_timer_0: timer@42800000 {
clock-frequency = <100000000>;
clock-names = "ref_clk";
clocks = <&clkc 0>;
compatible = "ricky,xps-timer-1.00.a";
interrupt-parent = <&intc>;
interrupts = <0 29 4>;
reg = <0x42800000 0x10000>;
xlnx,count-width = <0x20>;
xlnx,gen0-assert = <0x1>;
xlnx,gen1-assert = <0x1>;
xlnx,one-timer-only = <0x0>;
xlnx,trig0-assert = <0x1>;
xlnx,trig1-assert = <0x1>;
};
};
};
2. 驱动程序
* intr_example.c - The simplest kernel module.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
/* Standard module information, edit as appropriate */
MODULE_LICENSE("GPL");
MODULE_AUTHOR
("Xilinx Inc.");
MODULE_DESCRIPTION
("intr_example - loadable module template generated by petalinux-create -t modules");
#define DRIVER_NAME "intr_example"
#define XIL_AXI_TIMER_BASEADDR 0x42800000
#define XIL_AXI_TIMER_HIGHADDR 0x4280FFFF
#define XIL_AXI_TIMER_TCSR_OFFSET 0x0
#define XIL_AXI_TIMER_TLR_OFFSET 0x4
#define XIL_AXI_TIMER_TCR_OFFSET 0x8
#define XIL_AXI_TIMER_CSR_INT_OCCURED_MASK 0x00000100
#define XIL_AXI_TIMER_CSR_CASC_MASK 0x00000800
#define XIL_AXI_TIMER_CSR_ENABLE_ALL_MASK 0x00000400
#define XIL_AXI_TIMER_CSR_ENABLE_PWM_MASK 0x00000200
#define XIL_AXI_TIMER_CSR_INT_OCCURED_MASK 0x00000100
#define XIL_AXI_TIMER_CSR_ENABLE_TMR_MASK 0x00000080
#define XIL_AXI_TIMER_CSR_ENABLE_INT_MASK 0x00000040
#define XIL_AXI_TIMER_CSR_LOAD_MASK 0x00000020
#define XIL_AXI_TIMER_CSR_AUTO_RELOAD_MASK 0x00000010
#define XIL_AXI_TIMER_CSR_EXT_CAPTURE_MASK 0x00000008
#define XIL_AXI_TIMER_CSR_EXT_GENERATE_MASK 0x00000004
#define XIL_AXI_TIMER_CSR_DOWN_COUNT_MASK 0x00000002
#define XIL_AXI_TIMER_CSR_CAPTURE_MODE_MASK 0x00000001
#define TIMER_CNT 0xF8000000
/* Simple example of how to receive command line parameters to your module.
Delete if you don't need them */
unsigned myint = 0xdeadbeef;
char *mystr = "default";
module_param(myint, int, S_IRUGO);
module_param(mystr, charp, S_IRUGO);
struct intr_example_local {
int irq;
unsigned long mem_start;
unsigned long mem_end;
void __iomem *base_addr;
};
static int int_cnt;
static irqreturn_t intr_example_irq(int irq, struct intr_example_local *lp)
{
unsigned int data;
/*
* Clear Interrupt
*/
data = ioread32(lp->base_addr + XIL_AXI_TIMER_TCSR_OFFSET);
iowrite32(data | XIL_AXI_TIMER_CSR_INT_OCCURED_MASK,
lp->base_addr + XIL_AXI_TIMER_TCSR_OFFSET);
return IRQ_HANDLED;
}
static int intr_example_probe(struct platform_device *pdev)
{
struct resource *r_irq; /* Interrupt resources */
struct resource *r_mem; /* IO mem resources */
struct device *dev = &pdev->dev;
struct intr_example_local *lp = NULL;
unsigned int data = 0;
int rc = 0;
dev_info(dev, "Device Tree Probing\n");
/* Get iospace for the device */
r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r_mem) {
dev_err(dev, "invalid address\n");
return -ENODEV;
}
lp = (struct intr_example_local *) kmalloc(sizeof(struct intr_example_local), GFP_KERNEL);
if (!lp) {
dev_err(dev, "Cound not allocate intr_example device\n");
return -ENOMEM;
}
dev_set_drvdata(dev, lp);
lp->mem_start = r_mem->start;
lp->mem_end = r_mem->end;
if (!request_mem_region(lp->mem_start,
lp->mem_end - lp->mem_start + 1,
DRIVER_NAME)) {
dev_err(dev, "Couldn't lock memory region at %p\n",
(void *)lp->mem_start);
rc = -EBUSY;
goto error1;
}
lp->base_addr = ioremap_nocache(lp->mem_start, lp->mem_end - lp->mem_start + 1);
if (!lp->base_addr) {
dev_err(dev, "intr_example: Could not allocate iomem\n");
rc = -EIO;
goto error2;
}
/* Get IRQ for the device */
r_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!r_irq) {
dev_info(dev, "no IRQ found\n");
dev_info(dev, "intr_example at 0x%08x mapped to 0x%08x\n",
(unsigned int __force)lp->mem_start,
(unsigned int __force)lp->base_addr);
return 0;
}
lp->irq = r_irq->start;
rc = request_irq(lp->irq, &intr_example_irq, 0, DRIVER_NAME, lp);
if (rc) {
dev_err(dev, "testmodule: Could not allocate interrupt %d.\n",
lp->irq);
goto error3;
}
dev_info(dev,"intr_example at 0x%08x mapped to 0x%08x, irq=%d\n",
(unsigned int __force)lp->mem_start,
(unsigned int __force)lp->base_addr,
lp->irq);
return 0;
error3:
free_irq(lp->irq, lp);
error2:
release_mem_region(lp->mem_start, lp->mem_end - lp->mem_start + 1);
error1:
kfree(lp);
dev_set_drvdata(dev, NULL);
return rc;
}
static int intr_example_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct intr_example_local *lp = dev_get_drvdata(dev);
free_irq(lp->irq, lp);
release_mem_region(lp->mem_start, lp->mem_end - lp->mem_start + 1);
kfree(lp);
dev_set_drvdata(dev, NULL);
return 0;
}
#ifdef CONFIG_OF
static struct of_device_id intr_example_of_match[] = {
{ .compatible = "ricky,xps-timer-1.00.a", },
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(of, intr_example_of_match);
#else
# define intr_example_of_match
#endif
static struct platform_driver intr_example_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = intr_example_of_match,
},
.probe = intr_example_probe,
.remove = intr_example_remove,
};
static int __init intr_example_init(void)
{
printk("<1>Hello module world.\n");
printk("<1>Module parameters were (0x%08x) and \"%s\"\n", myint,
mystr);
return platform_driver_register(&intr_example_driver);
}
static void __exit intr_example_exit(void)
{
platform_driver_unregister(&intr_example_driver);
printk(KERN_ALERT "Goodbye module world.\n");
}
module_init(intr_example_init);
module_exit(intr_example_exit);