aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Albaugh <michael.albaugh@qlogic.com>2007-05-17 10:05:04 -0400
committerRoland Dreier <rolandd@cisco.com>2007-07-09 23:12:25 -0400
commit17b2eb9fe6bfadcb3ece308ed50193d10b71ba6e (patch)
tree567c1e7d11cc14990ecabd11fe23b1941a2ebcfd
parent82466f00ec6ef0a5ca7ea8991c731af2ec561c7d (diff)
IB/ipath: Lock and always use shadow copies of GPIO register
The new LED blinking interface adds more contention for the unprotected GPIO pins that were already shared, though not commonly at the same time. We add locks to the accesses to these pins so that Read-Modify-Write is now safe. Some of these locks are added at interrupt context, so we shadow the registers which drive and inspect these pins to avoid the mmio read/writes. This mitigates the effects of the locks and hastens us through the interrupt. Add locking and always use shadows for registers controlling GPIO pins (ExtCtrl and GPIOout). The use of shadows implies doing less I/O, which can make I2C operation too fast on some platforms. An explicit udelay(1) in SCL manipulation fixes that. Signed-off-by: Michael Albaugh <michael.albaugh@qlogic.com> Signed-off-by: Roland Dreier <rolandd@cisco.com>
-rw-r--r--drivers/infiniband/hw/ipath/ipath_eeprom.c68
-rw-r--r--drivers/infiniband/hw/ipath/ipath_iba6110.c3
-rw-r--r--drivers/infiniband/hw/ipath/ipath_iba6120.c3
-rw-r--r--drivers/infiniband/hw/ipath/ipath_init_chip.c2
-rw-r--r--drivers/infiniband/hw/ipath/ipath_kernel.h7
5 files changed, 53 insertions, 30 deletions
diff --git a/drivers/infiniband/hw/ipath/ipath_eeprom.c b/drivers/infiniband/hw/ipath/ipath_eeprom.c
index 030185f90ee2..26daac9d8b63 100644
--- a/drivers/infiniband/hw/ipath/ipath_eeprom.c
+++ b/drivers/infiniband/hw/ipath/ipath_eeprom.c
@@ -95,39 +95,37 @@ static int i2c_gpio_set(struct ipath_devdata *dd,
95 enum i2c_type line, 95 enum i2c_type line,
96 enum i2c_state new_line_state) 96 enum i2c_state new_line_state)
97{ 97{
98 u64 read_val, write_val, mask, *gpioval; 98 u64 out_mask, dir_mask, *gpioval;
99 unsigned long flags = 0;
99 100
100 gpioval = &dd->ipath_gpio_out; 101 gpioval = &dd->ipath_gpio_out;
101 read_val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_extctrl);
102 if (line == i2c_line_scl)
103 mask = dd->ipath_gpio_scl;
104 else
105 mask = dd->ipath_gpio_sda;
106 102
107 if (new_line_state == i2c_line_high) 103 if (line == i2c_line_scl) {
104 dir_mask = dd->ipath_gpio_scl;
105 out_mask = (1UL << dd->ipath_gpio_scl_num);
106 } else {
107 dir_mask = dd->ipath_gpio_sda;
108 out_mask = (1UL << dd->ipath_gpio_sda_num);
109 }
110
111 spin_lock_irqsave(&dd->ipath_gpio_lock, flags);
112 if (new_line_state == i2c_line_high) {
108 /* tri-state the output rather than force high */ 113 /* tri-state the output rather than force high */
109 write_val = read_val & ~mask; 114 dd->ipath_extctrl &= ~dir_mask;
110 else 115 } else {
111 /* config line to be an output */ 116 /* config line to be an output */
112 write_val = read_val | mask; 117 dd->ipath_extctrl |= dir_mask;
113 ipath_write_kreg(dd, dd->ipath_kregs->kr_extctrl, write_val); 118 }
119 ipath_write_kreg(dd, dd->ipath_kregs->kr_extctrl, dd->ipath_extctrl);
114 120
115 /* set high and verify */ 121 /* set output as well (no real verify) */
116 if (new_line_state == i2c_line_high) 122 if (new_line_state == i2c_line_high)
117 write_val = 0x1UL; 123 *gpioval |= out_mask;
118 else 124 else
119 write_val = 0x0UL; 125 *gpioval &= ~out_mask;
120 126
121 if (line == i2c_line_scl) {
122 write_val <<= dd->ipath_gpio_scl_num;
123 *gpioval = *gpioval & ~(1UL << dd->ipath_gpio_scl_num);
124 *gpioval |= write_val;
125 } else {
126 write_val <<= dd->ipath_gpio_sda_num;
127 *gpioval = *gpioval & ~(1UL << dd->ipath_gpio_sda_num);
128 *gpioval |= write_val;
129 }
130 ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_out, *gpioval); 127 ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_out, *gpioval);
128 spin_unlock_irqrestore(&dd->ipath_gpio_lock, flags);
131 129
132 return 0; 130 return 0;
133} 131}
@@ -145,8 +143,9 @@ static int i2c_gpio_get(struct ipath_devdata *dd,
145 enum i2c_type line, 143 enum i2c_type line,
146 enum i2c_state *curr_statep) 144 enum i2c_state *curr_statep)
147{ 145{
148 u64 read_val, write_val, mask; 146 u64 read_val, mask;
149 int ret; 147 int ret;
148 unsigned long flags = 0;
150 149
151 /* check args */ 150 /* check args */
152 if (curr_statep == NULL) { 151 if (curr_statep == NULL) {
@@ -154,15 +153,21 @@ static int i2c_gpio_get(struct ipath_devdata *dd,
154 goto bail; 153 goto bail;
155 } 154 }
156 155
157 read_val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_extctrl);
158 /* config line to be an input */ 156 /* config line to be an input */
159 if (line == i2c_line_scl) 157 if (line == i2c_line_scl)
160 mask = dd->ipath_gpio_scl; 158 mask = dd->ipath_gpio_scl;
161 else 159 else
162 mask = dd->ipath_gpio_sda; 160 mask = dd->ipath_gpio_sda;
163 write_val = read_val & ~mask; 161
164 ipath_write_kreg(dd, dd->ipath_kregs->kr_extctrl, write_val); 162 spin_lock_irqsave(&dd->ipath_gpio_lock, flags);
163 dd->ipath_extctrl &= ~mask;
164 ipath_write_kreg(dd, dd->ipath_kregs->kr_extctrl, dd->ipath_extctrl);
165 /*
166 * Below is very unlikely to reflect true input state if Output
167 * Enable actually changed.
168 */
165 read_val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_extstatus); 169 read_val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_extstatus);
170 spin_unlock_irqrestore(&dd->ipath_gpio_lock, flags);
166 171
167 if (read_val & mask) 172 if (read_val & mask)
168 *curr_statep = i2c_line_high; 173 *curr_statep = i2c_line_high;
@@ -192,6 +197,7 @@ static void i2c_wait_for_writes(struct ipath_devdata *dd)
192 197
193static void scl_out(struct ipath_devdata *dd, u8 bit) 198static void scl_out(struct ipath_devdata *dd, u8 bit)
194{ 199{
200 udelay(1);
195 i2c_gpio_set(dd, i2c_line_scl, bit ? i2c_line_high : i2c_line_low); 201 i2c_gpio_set(dd, i2c_line_scl, bit ? i2c_line_high : i2c_line_low);
196 202
197 i2c_wait_for_writes(dd); 203 i2c_wait_for_writes(dd);
@@ -314,12 +320,18 @@ static int eeprom_reset(struct ipath_devdata *dd)
314 int clock_cycles_left = 9; 320 int clock_cycles_left = 9;
315 u64 *gpioval = &dd->ipath_gpio_out; 321 u64 *gpioval = &dd->ipath_gpio_out;
316 int ret; 322 int ret;
323 unsigned long flags;
317 324
318 eeprom_init = 1; 325 spin_lock_irqsave(&dd->ipath_gpio_lock, flags);
326 /* Make sure shadows are consistent */
327 dd->ipath_extctrl = ipath_read_kreg64(dd, dd->ipath_kregs->kr_extctrl);
319 *gpioval = ipath_read_kreg64(dd, dd->ipath_kregs->kr_gpio_out); 328 *gpioval = ipath_read_kreg64(dd, dd->ipath_kregs->kr_gpio_out);
329 spin_unlock_irqrestore(&dd->ipath_gpio_lock, flags);
330
320 ipath_cdbg(VERBOSE, "Resetting i2c eeprom; initial gpioout reg " 331 ipath_cdbg(VERBOSE, "Resetting i2c eeprom; initial gpioout reg "
321 "is %llx\n", (unsigned long long) *gpioval); 332 "is %llx\n", (unsigned long long) *gpioval);
322 333
334 eeprom_init = 1;
323 /* 335 /*
324 * This is to get the i2c into a known state, by first going low, 336 * This is to get the i2c into a known state, by first going low,
325 * then tristate sda (and then tristate scl as first thing 337 * then tristate sda (and then tristate scl as first thing
diff --git a/drivers/infiniband/hw/ipath/ipath_iba6110.c b/drivers/infiniband/hw/ipath/ipath_iba6110.c
index 4372c6c50ff6..8482ea366fb1 100644
--- a/drivers/infiniband/hw/ipath/ipath_iba6110.c
+++ b/drivers/infiniband/hw/ipath/ipath_iba6110.c
@@ -1059,6 +1059,7 @@ static void ipath_setup_ht_setextled(struct ipath_devdata *dd,
1059 u64 lst, u64 ltst) 1059 u64 lst, u64 ltst)
1060{ 1060{
1061 u64 extctl; 1061 u64 extctl;
1062 unsigned long flags = 0;
1062 1063
1063 /* the diags use the LED to indicate diag info, so we leave 1064 /* the diags use the LED to indicate diag info, so we leave
1064 * the external LED alone when the diags are running */ 1065 * the external LED alone when the diags are running */
@@ -1075,6 +1076,7 @@ static void ipath_setup_ht_setextled(struct ipath_devdata *dd,
1075 : INFINIPATH_IBCS_L_STATE_DOWN; 1076 : INFINIPATH_IBCS_L_STATE_DOWN;
1076 } 1077 }
1077 1078
1079 spin_lock_irqsave(&dd->ipath_gpio_lock, flags);
1078 /* 1080 /*
1079 * start by setting both LED control bits to off, then turn 1081 * start by setting both LED control bits to off, then turn
1080 * on the appropriate bit(s). 1082 * on the appropriate bit(s).
@@ -1103,6 +1105,7 @@ static void ipath_setup_ht_setextled(struct ipath_devdata *dd,
1103 } 1105 }
1104 dd->ipath_extctrl = extctl; 1106 dd->ipath_extctrl = extctl;
1105 ipath_write_kreg(dd, dd->ipath_kregs->kr_extctrl, extctl); 1107 ipath_write_kreg(dd, dd->ipath_kregs->kr_extctrl, extctl);
1108 spin_unlock_irqrestore(&dd->ipath_gpio_lock, flags);
1106} 1109}
1107 1110
1108static void ipath_init_ht_variables(struct ipath_devdata *dd) 1111static void ipath_init_ht_variables(struct ipath_devdata *dd)
diff --git a/drivers/infiniband/hw/ipath/ipath_iba6120.c b/drivers/infiniband/hw/ipath/ipath_iba6120.c
index bcb70d67dbb9..2345bb011acd 100644
--- a/drivers/infiniband/hw/ipath/ipath_iba6120.c
+++ b/drivers/infiniband/hw/ipath/ipath_iba6120.c
@@ -791,6 +791,7 @@ static void ipath_setup_pe_setextled(struct ipath_devdata *dd, u64 lst,
791 u64 ltst) 791 u64 ltst)
792{ 792{
793 u64 extctl; 793 u64 extctl;
794 unsigned long flags = 0;
794 795
795 /* the diags use the LED to indicate diag info, so we leave 796 /* the diags use the LED to indicate diag info, so we leave
796 * the external LED alone when the diags are running */ 797 * the external LED alone when the diags are running */
@@ -807,6 +808,7 @@ static void ipath_setup_pe_setextled(struct ipath_devdata *dd, u64 lst,
807 : INFINIPATH_IBCS_L_STATE_DOWN; 808 : INFINIPATH_IBCS_L_STATE_DOWN;
808 } 809 }
809 810
811 spin_lock_irqsave(&dd->ipath_gpio_lock, flags);
810 extctl = dd->ipath_extctrl & ~(INFINIPATH_EXTC_LED1PRIPORT_ON | 812 extctl = dd->ipath_extctrl & ~(INFINIPATH_EXTC_LED1PRIPORT_ON |
811 INFINIPATH_EXTC_LED2PRIPORT_ON); 813 INFINIPATH_EXTC_LED2PRIPORT_ON);
812 814
@@ -816,6 +818,7 @@ static void ipath_setup_pe_setextled(struct ipath_devdata *dd, u64 lst,
816 extctl |= INFINIPATH_EXTC_LED1PRIPORT_ON; 818 extctl |= INFINIPATH_EXTC_LED1PRIPORT_ON;
817 dd->ipath_extctrl = extctl; 819 dd->ipath_extctrl = extctl;
818 ipath_write_kreg(dd, dd->ipath_kregs->kr_extctrl, extctl); 820 ipath_write_kreg(dd, dd->ipath_kregs->kr_extctrl, extctl);
821 spin_unlock_irqrestore(&dd->ipath_gpio_lock, flags);
819} 822}
820 823
821/** 824/**
diff --git a/drivers/infiniband/hw/ipath/ipath_init_chip.c b/drivers/infiniband/hw/ipath/ipath_init_chip.c
index 7045ba689494..f6ee7a83595a 100644
--- a/drivers/infiniband/hw/ipath/ipath_init_chip.c
+++ b/drivers/infiniband/hw/ipath/ipath_init_chip.c
@@ -340,6 +340,8 @@ static int init_chip_first(struct ipath_devdata *dd,
340 340
341 spin_lock_init(&dd->ipath_tid_lock); 341 spin_lock_init(&dd->ipath_tid_lock);
342 342
343 spin_lock_init(&dd->ipath_gpio_lock);
344
343done: 345done:
344 *pdp = pd; 346 *pdp = pd;
345 return ret; 347 return ret;
diff --git a/drivers/infiniband/hw/ipath/ipath_kernel.h b/drivers/infiniband/hw/ipath/ipath_kernel.h
index 2f39db7df31c..bd1088a99891 100644
--- a/drivers/infiniband/hw/ipath/ipath_kernel.h
+++ b/drivers/infiniband/hw/ipath/ipath_kernel.h
@@ -399,6 +399,8 @@ struct ipath_devdata {
399 u64 ipath_gpio_out; 399 u64 ipath_gpio_out;
400 /* shadow the gpio mask register */ 400 /* shadow the gpio mask register */
401 u64 ipath_gpio_mask; 401 u64 ipath_gpio_mask;
402 /* shadow the gpio output enable, etc... */
403 u64 ipath_extctrl;
402 /* kr_revision shadow */ 404 /* kr_revision shadow */
403 u64 ipath_revision; 405 u64 ipath_revision;
404 /* 406 /*
@@ -473,8 +475,6 @@ struct ipath_devdata {
473 u32 ipath_cregbase; 475 u32 ipath_cregbase;
474 /* shadow the control register contents */ 476 /* shadow the control register contents */
475 u32 ipath_control; 477 u32 ipath_control;
476 /* shadow the gpio output contents */
477 u32 ipath_extctrl;
478 /* PCI revision register (HTC rev on FPGA) */ 478 /* PCI revision register (HTC rev on FPGA) */
479 u32 ipath_pcirev; 479 u32 ipath_pcirev;
480 480
@@ -576,6 +576,9 @@ struct ipath_devdata {
576 u64 ipath_gpio_sda; 576 u64 ipath_gpio_sda;
577 u64 ipath_gpio_scl; 577 u64 ipath_gpio_scl;
578 578
579 /* lock for doing RMW of shadows/regs for ExtCtrl and GPIO */
580 spinlock_t ipath_gpio_lock;
581
579 /* used to override LED behavior */ 582 /* used to override LED behavior */
580 u8 ipath_led_override; /* Substituted for normal value, if non-zero */ 583 u8 ipath_led_override; /* Substituted for normal value, if non-zero */
581 u16 ipath_led_override_timeoff; /* delta to next timer event */ 584 u16 ipath_led_override_timeoff; /* delta to next timer event */