1/* 2 * Sets up interrupt handlers for various hardware switches which are 3 * connected to interrupt lines. 4 * 5 * Copyright 2005-2207 PMC-Sierra, Inc. 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the 9 * Free Software Foundation; either version 2 of the License, or (at your 10 * option) any later version. 11 * 12 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 13 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 14 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 15 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 16 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 17 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 18 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 19 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 20 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 21 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 * 23 * You should have received a copy of the GNU General Public License along 24 * with this program; if not, write to the Free Software Foundation, Inc., 25 * 675 Mass Ave, Cambridge, MA 02139, USA. 26 */ 27 28#include <linux/kernel.h> 29#include <linux/init.h> 30#include <linux/interrupt.h> 31 32#include <msp_int.h> 33#include <msp_regs.h> 34#include <msp_regops.h> 35 36/* For hwbutton_interrupt->initial_state */ 37#define HWBUTTON_HI 0x1 38#define HWBUTTON_LO 0x2 39 40/* 41 * This struct describes a hardware button 42 */ 43struct hwbutton_interrupt { 44 char *name; /* Name of button */ 45 int irq; /* Actual LINUX IRQ */ 46 int eirq; /* Extended IRQ number (0-7) */ 47 int initial_state; /* The "normal" state of the switch */ 48 void (*handle_hi)(void *); /* Handler: switch input has gone HI */ 49 void (*handle_lo)(void *); /* Handler: switch input has gone LO */ 50 void *data; /* Optional data to pass to handler */ 51}; 52 53#ifdef CONFIG_PMC_MSP7120_GW 54extern void msp_restart(char *); 55 56static void softreset_push(void *data) 57{ 58 printk(KERN_WARNING "SOFTRESET switch was pushed\n"); 59 60 /* 61 * In the future you could move this to the release handler, 62 * timing the difference between the 'push' and 'release', and only 63 * doing this ungraceful restart if the button has been down for 64 * a certain amount of time; otherwise doing a graceful restart. 65 */ 66 67 msp_restart(NULL); 68} 69 70static void softreset_release(void *data) 71{ 72 printk(KERN_WARNING "SOFTRESET switch was released\n"); 73 74 /* Do nothing */ 75} 76 77static void standby_on(void *data) 78{ 79 printk(KERN_WARNING "STANDBY switch was set to ON (not implemented)\n"); 80 81 /* TODO: Put board in standby mode */ 82} 83 84static void standby_off(void *data) 85{ 86 printk(KERN_WARNING 87 "STANDBY switch was set to OFF (not implemented)\n"); 88 89 /* TODO: Take out of standby mode */ 90} 91 92static struct hwbutton_interrupt softreset_sw = { 93 .name = "Softreset button", 94 .irq = MSP_INT_EXT0, 95 .eirq = 0, 96 .initial_state = HWBUTTON_HI, 97 .handle_hi = softreset_release, 98 .handle_lo = softreset_push, 99 .data = NULL, 100}; 101 102static struct hwbutton_interrupt standby_sw = { 103 .name = "Standby switch", 104 .irq = MSP_INT_EXT1, 105 .eirq = 1, 106 .initial_state = HWBUTTON_HI, 107 .handle_hi = standby_off, 108 .handle_lo = standby_on, 109 .data = NULL, 110}; 111#endif /* CONFIG_PMC_MSP7120_GW */ 112 113static irqreturn_t hwbutton_handler(int irq, void *data) 114{ 115 struct hwbutton_interrupt *hirq = data; 116 unsigned long cic_ext = *CIC_EXT_CFG_REG; 117 118 if (CIC_EXT_IS_ACTIVE_HI(cic_ext, hirq->eirq)) { 119 /* Interrupt: pin is now HI */ 120 CIC_EXT_SET_ACTIVE_LO(cic_ext, hirq->eirq); 121 hirq->handle_hi(hirq->data); 122 } else { 123 /* Interrupt: pin is now LO */ 124 CIC_EXT_SET_ACTIVE_HI(cic_ext, hirq->eirq); 125 hirq->handle_lo(hirq->data); 126 } 127 128 /* 129 * Invert the POLARITY of this level interrupt to ack the interrupt 130 * Thus next state change will invoke the opposite message 131 */ 132 *CIC_EXT_CFG_REG = cic_ext; 133 134 return IRQ_HANDLED; 135} 136 137static int msp_hwbutton_register(struct hwbutton_interrupt *hirq) 138{ 139 unsigned long cic_ext; 140 141 if (hirq->handle_hi == NULL || hirq->handle_lo == NULL) 142 return -EINVAL; 143 144 cic_ext = *CIC_EXT_CFG_REG; 145 CIC_EXT_SET_TRIGGER_LEVEL(cic_ext, hirq->eirq); 146 if (hirq->initial_state == HWBUTTON_HI) 147 CIC_EXT_SET_ACTIVE_LO(cic_ext, hirq->eirq); 148 else 149 CIC_EXT_SET_ACTIVE_HI(cic_ext, hirq->eirq); 150 *CIC_EXT_CFG_REG = cic_ext; 151 152 return request_irq(hirq->irq, hwbutton_handler, 0, 153 hirq->name, hirq); 154} 155 156static int __init msp_hwbutton_setup(void) 157{ 158#ifdef CONFIG_PMC_MSP7120_GW 159 msp_hwbutton_register(&softreset_sw); 160 msp_hwbutton_register(&standby_sw); 161#endif 162 return 0; 163} 164 165subsys_initcall(msp_hwbutton_setup); 166