需求描述:
主板上的状态灯应能正确显示ONU模块的连通状态,即当光节点模块插到主板插槽之后,状态灯应亮,移掉之后应灭。
硬件接口:
When SFP module is plugged in, the GPIO[2] status will change from high to low automatically.
When SW detect the change, just make the GPIO[18] to generate low signal (default is high), the ONU led will be lighted.
以上为接任务时的信息。于是开始寻找解决问题的信息。
GPIO的背景信息?
即通用输入输出。把CPU针脚不通过总线或控制器直接连到外设上来进行控制的工业标准,比如连一个针脚到LED上,通过置一个bit的0/1就可以控制该LED得亮灭。
硬件的结构是怎样的? 找schematics看一看:
1.右边是CPU,左边是pin脚连的东西,“>>”,“<<”表示输入的方向
2.对应第一张图,左边是灯,右边是连到的CPU的GPIO接口
3.对应第一张图,左边是CPU
软件的方案?
有2种实现方法,一种是对GPIO[2]的电位进行轮询,一种是让GPIO[2]的电位变化产生中断。
找到一个当前主板上的别人使用GPIO的例子,该例子的功能是:按下硬件重启按钮后会向CPU的GPIO针脚发使主板重启。
此例子是用中断实现的,准备也用中断的方式来实现。
那么,怎么让GPIO的[2]的电位变化产生中断呢?
在现在的设备上看看,列出已经被占用的中断号:
root:/proc# cat interrupts
CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 CPU6
0: 526 83 11 4 1423 164 355 MIPS SMTC_IPI
2: 0 0 0 0 0 40 MIPS pmcmsp_tsmac
3: 0 0 3 0 0 00 MIPS pmcmsp_tsmac
4: 0 0 0 0 0 00 MIPS ehci_hcd:usb1, ehci_hcd:usb2
5: 0 0 0 0 13 00 MIPS pmcmsp_tsmac
6: 0 0 0 0 0 00 MIPS CIC cascade
8: 0 0 0 0 0 00 MSP_CIC Softreset button
21: 0 0 0 0 0 00 MSP_CIC PER cascade
25: 5853436 1664 28 4 0 00 MSP_CIC timer
27: 3575 0 0 0 0 00 MSP_CIC serial
34: 0 0 0 0 5851128 138 30 MSP_CIC VPE1 local timer
46: 1 0 0 0 0 00 MSP_PER pmcmsptwi
ERR: 1
现有按钮的硬件是怎么连的?
现有按钮的中断是怎么实现的?
在上图中可以看到GPIO21针脚同时连到了按钮与IRQ0(Interrupt Request 0)。查看代码
subsys_initcall(msp_hwbutton_setup); //注册子系统
static int __init msp_hwbutton_setup(void) { msp_hwbutton_register(&softreset_sw); ... } static struct hwbutton_interrupt softreset_sw = { .name = "Softreset button", .irq = MSP_INT_EXT0, .eirq = 0, .initial_state = HWBUTTON_HI, .handle_hi = softreset_release, //按钮松开时调用的函数 .handle_lo = softreset_push,//按钮按下时调用的函数 .data = NULL, }; /* * This struct describes a hardware button */ struct hwbutton_interrupt { char *name; /* Name of button */ int irq; /* Actual LINUX IRQ */ //下面外部中断所对应的系统中断,定义在主板系统的lib中 int eirq; /* Extended IRQ number (0-7) */ //GPIO2分了一线到IRQ0 int initial_state; /* The "normal" state of the switch *///这个从硬件工程师处获悉 void (*handle_hi)(void *); /* Handler: switch input has gone HI */ //handler做什么从功能需求处获悉 void (*handle_lo)(void *); /* Handler: switch input has gone LO */ //handler做什么从功能需求处获悉 void *data; /* Optional data to pass to handler */ //传给handler的参数 };
#define MSP_MSB_BASE (0x18000000) /* MSbus address start */ #define MSP_CPUIF_BASE (MSP_MSB_BASE + 0xC00000) /* CPU interface registers */ /* Central Interrupt Controller Registers */ #define MSP_CIC_BASE (MSP_CPUIF_BASE + 0x8000) /* Central Interrupt registers */ #define CIC_EXT_CFG_REG regptr(MSP_CIC_BASE + 0x00) /* External interrupt configuration */ #define EXT_INT_EDGE(eirq) (1 << eirq) #define CIC_EXT_SET_TRIGGER_LEVEL(reg, eirq) reg &= ~EXT_INT_EDGE(eirq) static int msp_hwbutton_register(struct hwbutton_interrupt *hirq) { unsigned long cic_ext; if (hirq->handle_hi == NULL || hirq->handle_lo == NULL) return -EINVAL; cic_ext = *CIC_EXT_CFG_REG; CIC_EXT_SET_TRIGGER_LEVEL(cic_ext, hirq->eirq); if (hirq->initial_state == HWBUTTON_HI) CIC_EXT_SET_ACTIVE_LO(cic_ext, hirq->eirq); else CIC_EXT_SET_ACTIVE_HI(cic_ext, hirq->eirq); *CIC_EXT_CFG_REG = cic_ext; return request_irq(hirq->irq, hwbutton_handler, SA_INTERRUPT, hirq->name, (void*)hirq); //相当于 return request_irq( MSP_INT_EXT0, hwbutton_handler, SA_INTERRUPT, “Softrest Button”, & }
static irqreturn_t hwbutton_handler(int irq, void *data) { struct hwbutton_interrupt *hirq = (struct hwbutton_interrupt *)data; unsigned long cic_ext = *CIC_EXT_CFG_REG; if (irq != hirq->irq) return IRQ_NONE; if (CIC_EXT_IS_ACTIVE_HI(cic_ext, hirq->eirq)) { /* Interrupt: pin is now HI */ CIC_EXT_SET_ACTIVE_LO(cic_ext, hirq->eirq); hirq->handle_hi(hirq->data); } else { /* Interrupt: pin is now LO */ CIC_EXT_SET_ACTIVE_HI(cic_ext, hirq->eirq); hirq->handle_lo(hirq->data); } /* * Invert the POLARITY of this level interrupt to ack the interrupt * Thus next state change will invoke the opposite message */ *CIC_EXT_CFG_REG = cic_ext; return IRQ_HANDLED; }
现在的问题?
在此CPU中,GPIO0-15可以配置产生内部中断,GPIO16及其以上不能通过配置产生;上面的例子用的是GPIO21,不能通过配置产生中断,所以才要接到IRQ0
要实现的LED灯的GPIO的脚不像按钮的例子,是没有接到外部中断的,但要求对LED的GPIO2进行电位判断,在GPIO0-15的范围内,是可以通过配置CPU来产生中断的。
但是如何进行配置的文档CPU厂商不提供,另外CPU的SDK现在也不支持对GPIO中断进行配置。
小结:以能获取的硬件与软件,无法使GPIO2产生中断。
那么只能走轮询的路了。代码如下:
static unsigned long e220_sfp_led_update_delay = 0; static struct timer_list e220_sfp_led_update_timer; static msp_gpio_data_t e220_sfp_old_status; static void e220_sfp_led_update(unsigned long data) { msp_gpio_data_t e220_sfp_current_status; if (msp_gpio_pin_mode(MSP_GPIO_INPUT, 2) != 0) { printk(KERN_INFO "GPIO[2] input mode failed/n"); } if (msp_gpio_pin_mode(MSP_GPIO_OUTPUT, 18) != 0) { printk(KERN_INFO "GPIO[18] output mode failed/n"); } e220_sfp_current_status = msp_gpio_pin_get(2); if (e220_sfp_current_status != e220_sfp_old_status) { if(e220_sfp_current_status == MSP_GPIO_LO) { printk(KERN_INFO "SFP module inserted /n"); msp_gpio_pin_lo(18); } else if (e220_sfp_current_status == MSP_GPIO_HI) { printk(KERN_INFO "SFP module removed /n"); msp_gpio_pin_hi(18); } else { printk(KERN_INFO "SFP module state unknown /n"); } e220_sfp_old_status = e220_sfp_current_status; } mod_timer(&e220_sfp_led_update_timer, jiffies + msecs_to_jiffies(1000)); } static int e220_sfp_status_watch_start(void) { e220_sfp_old_status = MSP_GPIO_NONE; setup_timer(&e220_sfp_led_update_timer, e220_sfp_led_update, NULL); //expire timer immediately return mod_timer(&e220_sfp_led_update_timer, jiffies + 1); }
ok.