[vc_row][vc_column width=”1/1″][vc_column_text]External interrupt is an important mechanism to handle external event by pcDuino. pcDuino provides two external interrupt pins (pins 2 and 3). If you need to use more than two external interrupt pins, in this post, we are looking at how to make the external interrupt drive.
[/vc_column_text][/vc_column][/vc_row][vc_row][vc_column width=”1/1″][vc_tour][vc_tab title=”What is interrupt” tab_id=”1388480554-1-12″][vc_column_text]Compared to the speed of CPU or even of memory, I/O devices are considered extremely slow. Printers, keyboards, mice are all very slow devices. They take a long time to respond, and furthermore, they only require occasional handling by the CPU. Even though you may feel you’re using the keyboard or mouse all the time, the CPU runs so much faster than you can, that it’s practically as if you’re not using it at all.
One way for a CPU to communicate with an I/O device is through polling. That’s discussed in a different set of class notes .
However, if the device is slow, and rarely requires servicing, external interrupts are usually a better way to go.
This way of handling I/O devices usually called interrupt driven I/O.
A CPU usually has at least one input pin devoted to interrupts. Whenever a device wants the CPU to pay attention, it sends a a signal to this pin.
The protocol usually runs like this:
I/O device sets the INT pin from 0 to 1.
The CPU completes the current instruction, and then saves the state of the program. For older ISPs, this involved copying registers to the stack. For newer ones ISPs, this usually means switching to a supervisor (operating system) set of registers.
The CPU then determines which kind interrupt has occurred. It can send a signal to the I/O device to ask for an interrupt number.
The I/O device can place the interrupt number on the data bus.
The CPU then runs an interrupt handler (a special function) for that.
Once the interrupt has been handled, the I/O device turns the INT value back to 0, and the CPU resumes running the program that was interrupted.
The detail can be found at: http://www.cs.umd.edu/class/spring2003/cmsc311/Notes/IO/extInt.html[/vc_column_text][/vc_tab][vc_tab title=”A10 External interrupt pins” tab_id=”1388480554-2-9″][vc_column_text]Allwinner A10 has 32 GPIO which support EINT:
- PH0 – PH21 ( correspondence EINT0 – EINT21 )
- PI10 – PI19 ( correspondence EINT22 – EINT31 )
The correspondence between pcDuino’s Arduino hardware and A10:
You can find the correspondence on
https://github.com/pcduino/kernel/blob/master/sunxi-boards/sys_config/a10/pcduino.fex
Because of the pin’s reuse, GPIO5/GPIO6 are not able to handle external interrupt.[/vc_column_text][/vc_tab][vc_tab title=”External interrupt kernel driver code” tab_id=”1388482403509-2-4″][vc_column_text]
/* * keypad driver for pcDuino */ #include <linux/err.h> #include <linux/init.h> #include <linux/input.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/slab.h> #define swkp_msg(...) printk("[kpad]: "__VA_ARGS__); struct sw_keypad { struct input_dev *input_dev; struct platform_device *pdev; u32 pio_hdle; int irq; u32 irq_mask; }; #define REG_RD(fmt...) __raw_readl(fmt) #define GPIO_IRQ 28 #define GPIO_TEST_BASE 0xf1c20800 #define GPIO_INT_BASE 0xf1c20400 #define PORT_H ('H' - 'A' + 1) #define PORT_I ('I' - 'A' + 1) struct sun4i_gpio_data { int status; unsigned gpio_handler; script_gpio_set_t info; char name[8]; }; struct sb_gpio_attr { struct sun4i_gpio_data gpio_i; u32 port; u32 port_num; unsigned short irq_num; unsigned short key_code; }; static int kp_used = 0; static int key_cnt = 0; static struct sb_gpio_attr *gpio_keys; static int sw_keypad_gpio_request(struct sw_keypad *keypad) { keypad->pio_hdle = gpio_request_ex("gpio_para", NULL); if (!keypad->pio_hdle) { swkp_msg("request pio parameter failed\n"); return -1; } return 0; } static void sw_keypad_gpio_release(struct sw_keypad *keypad) { gpio_release(keypad->pio_hdle, 1); keypad->pio_hdle = 0; } static irqreturn_t sw_keypad_irq(int irq, void *dev_id) { struct sw_keypad *keypad = (struct sw_keypad *)dev_id; unsigned int status; int j; status = REG_RD(GPIO_TEST_BASE + 0x214 ) ; for ( j = 0; j< key_cnt; ++j ) { if ( status & (1<<gpio_keys[j].irq_num)) { printk("mask=0x%.8x irq_num = %d, code=%d\n", keypad->irq_mask, gpio_keys[j].irq_num, gpio_keys[j].key_code); input_report_key(keypad->input_dev, gpio_keys[j].key_code,1); input_report_key(keypad->input_dev, gpio_keys[j].key_code,0); input_sync(keypad->input_dev); } } __raw_writel(status, GPIO_TEST_BASE + 0x214); return IRQ_HANDLED; } static int __devinit sw_keypad_probe(struct platform_device *pdev) { struct sw_keypad *keypad = kzalloc(sizeof(struct sw_keypad), GFP_KERNEL); struct input_dev *input_dev; int error; int i; int ret; u32 irq_ctl; char pin[16]; swkp_msg("sw keypad probe\n"); input_dev = input_allocate_device(); if (!keypad || !input_dev) { error = -ENOMEM; goto err_free_mem; } /* initialize the gpio */ if (sw_keypad_gpio_request(keypad)) { error = -ENODEV; goto err_free_mem; } for(i = 0; i < key_cnt; i++) { /* get gpio for the key */ memset((void *)pin, 0, sizeof(pin)); sprintf(pin, "key_pin_%d", i); ret = script_parser_fetch("sb_keypad_para", pin, (int *)&gpio_keys[i].gpio_i.info, sizeof(script_gpio_set_t)); if(ret) { pr_err("%s script_parser_fetch \"sb_keypad_para\" \"%s\" error\n", __FUNCTION__, pin); break; } gpio_keys[i].gpio_i.gpio_handler = gpio_request_ex("sb_keypad_para", pin); if(!gpio_keys[i].gpio_i.gpio_handler) { pr_err("%s can not get \"sb_keypad_para\" \"%s\" gpio handler,\ already used by others?", __FUNCTION__, pin); break; } /* get key code */ memset((void *)pin, 0, sizeof(pin)); sprintf(pin, "key_code_%d", i); ret = script_parser_fetch("sb_keypad_para", pin,(int *)&gpio_keys[i].key_code, 1); if(ret) { pr_err("%s script_parser_fetch \"sb_keypad_para\" \"%s\" error\n", __FUNCTION__, pin); break; } swkp_msg("%s: P%c%d, code=%d\n", pin, gpio_keys[i].gpio_i.info.port +'A' -1, gpio_keys[i].gpio_i.info.port_num, gpio_keys[i].key_code); /* set interrupt bit */ gpio_keys[i].port = gpio_keys[i].gpio_i.info.port; gpio_keys[i].port_num = gpio_keys[i].gpio_i.info.port_num; if(gpio_keys[i].port == PORT_H){ if((gpio_keys[i].port_num >= 0) && (gpio_keys[i].port_num <= 21)){ irq_ctl = REG_RD(GPIO_TEST_BASE + 0x210); __raw_writel((1 << gpio_keys[i].port_num) | irq_ctl, GPIO_TEST_BASE + 0x210); gpio_keys[i].irq_num = gpio_keys[i].port_num; } /* PI10 - PI19 (EINT22 - EINT31)*/ }else if(gpio_keys[i].port == PORT_I){ if((gpio_keys[i].port_num >= 10) && (gpio_keys[i].port_num <= 19)){ irq_ctl = REG_RD(GPIO_TEST_BASE + 0x210); __raw_writel((1 << (gpio_keys[i].port_num + 12)) | irq_ctl, GPIO_TEST_BASE + 0x210); gpio_keys[i].irq_num = gpio_keys[i].port_num + 12; } } else{ printk("this area don`t have EINT FUNCTION\n"); return 1; } } keypad->input_dev = input_dev; input_dev->name = pdev->name; input_dev->id.bustype = BUS_HOST; input_dev->dev.parent = &pdev->dev; input_set_drvdata(input_dev, keypad); keypad->irq_mask = 0; input_dev->evbit[0] = BIT_MASK(EV_KEY); for (i=0; i< key_cnt; i++) { set_bit(gpio_keys[i].key_code, input_dev->keybit); keypad->irq_mask |= (1<< gpio_keys[i].irq_num); } keypad->irq = GPIO_IRQ; error = request_irq(keypad->irq, sw_keypad_irq, 0, dev_name(&pdev->dev), keypad); if (error) { swkp_msg("failed to register keypad interrupt\n"); goto err_free_gpio; } error = input_register_device(keypad->input_dev); if (error) goto err_free_irq; platform_set_drvdata(pdev, keypad); keypad->pdev = pdev; swkp_msg("sw keypad probe done, irq %d\n", keypad->irq); return 0; err_free_irq: free_irq(keypad->irq, keypad); err_free_gpio: gpio_release(keypad->pio_hdle, 1); keypad->pio_hdle = 0; err_free_mem: input_free_device(input_dev); kfree(keypad); return error; } static int __devexit sw_keypad_remove(struct platform_device *pdev) { struct sw_keypad *keypad = platform_get_drvdata(pdev); swkp_msg("sw keypad remove\n"); platform_set_drvdata(pdev, NULL); input_unregister_device(keypad->input_dev); __raw_writel(0x00, GPIO_TEST_BASE + 0x210); free_irq(keypad->irq, keypad); sw_keypad_gpio_release(keypad); kfree(keypad); return 0; } static void sw_keypad_dev_release(struct device *dev) { swkp_msg("%s %d\n", __FUNCTION__, __LINE__); } static struct platform_device sw_device_keypad = { .name = "sb-keypad", .id = -1, .num_resources = 0, .dev = { .release = sw_keypad_dev_release, } }; static struct platform_driver sw_keypad_driver = { .probe = sw_keypad_probe, .remove = __devexit_p(sw_keypad_remove), .driver = { .name = "sb-keypad", .owner = THIS_MODULE, }, }; static int __init sw_keypad_init(void) { int ret; swkp_msg("sw keypad init\n"); kp_used = 0; ret = script_parser_fetch("sb_keypad_para", "sb_key_used", &kp_used, sizeof(int)); if (ret) { printk("sw keypad fetch keypad uning configuration failed\n"); return -1; } ret = script_parser_fetch("sb_keypad_para", "key_num", &key_cnt, sizeof(key_cnt)/sizeof(int)); if(ret) { printk("sw keypad fetch key_num configuration failed\n"); return -1; } gpio_keys = kzalloc(sizeof(struct sb_gpio_attr) * key_cnt, GFP_KERNEL); if (kp_used) { platform_device_register(&sw_device_keypad); return platform_driver_register(&sw_keypad_driver); } else { pr_warning("keypad: cannot find using configuration, return without doing anything!\n"); return 0; } } module_init(sw_keypad_init); static void __exit sw_keypad_exit(void) { swkp_msg("sw keypad exit\n"); if (kp_used) { kp_used = 0; platform_driver_unregister(&sw_keypad_driver); platform_device_unregister(&sw_device_keypad); kfree(gpio_keys); } } module_exit(sw_keypad_exit); MODULE_LICENSE("GPL"); |
You can download the code here.[/vc_column_text][/vc_tab][vc_tab title=”Compile” tab_id=”1388482544695-3-6″][vc_column_text]Copy the file keypad.c to a folder under kernel, like linux-sunxi/drivers\char,
then modify linux-sunxi/drivers\char\Makefile, add obj-m += keypad.o in it.
Will create file keypad.ko at the some folder when compiling.[/vc_column_text][/vc_tab][vc_tab title=”FAQ” tab_id=”1388482925020-4-9″][vc_column_text]
sb_key_used = 1
key_num = 3
key_pin_0 = port:PH17<6><1><3>
key_code_0 = 158
key_pin_1 = port:PH18<6><1><3>
key_code_1 = 139
key_pin_2 = port:PH19<6><1><3>
key_code_2 = 102
[/vc_column_text][/vc_tab][/vc_tour][/vc_column][/vc_row]
Leave a Reply
You must be logged in to post a comment.