]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
ssb: add locking around gpio register accesses
authorHauke Mehrtens <hauke@hauke-m.de>
Tue, 20 Nov 2012 22:24:32 +0000 (22:24 +0000)
committerJohn Crispin <blogic@openwrt.org>
Wed, 21 Nov 2012 20:55:52 +0000 (21:55 +0100)
The GPIOs are access through some registers in the chip common core or
over extif. We need locking around these GPIO accesses, all GPIOs are
accessed through the same registers and parallel writes will cause
problems.

Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
Patchwork: http://patchwork.linux-mips.org/patch/4590
Acked-by: Florian Fainelli <florian@openwrt.org>
drivers/ssb/driver_chipcommon.c
drivers/ssb/driver_extif.c
drivers/ssb/main.c
drivers/ssb/ssb_private.h
include/linux/ssb/ssb_driver_chipcommon.h
include/linux/ssb/ssb_driver_extif.h

index 4df492665565eb74bc70964af2282b7e7cdf2a6c..24e02bb2ecd8b581ab5146cc4d442f4083d34147 100644 (file)
@@ -284,6 +284,9 @@ void ssb_chipcommon_init(struct ssb_chipcommon *cc)
 {
        if (!cc->dev)
                return; /* We don't have a ChipCommon */
+
+       spin_lock_init(&cc->gpio_lock);
+
        if (cc->dev->id.revision >= 11)
                cc->status = chipco_read32(cc, SSB_CHIPCO_CHIPSTAT);
        ssb_dprintk(KERN_INFO PFX "chipcommon status is 0x%x\n", cc->status);
@@ -418,44 +421,93 @@ u32 ssb_chipco_gpio_in(struct ssb_chipcommon *cc, u32 mask)
 
 u32 ssb_chipco_gpio_out(struct ssb_chipcommon *cc, u32 mask, u32 value)
 {
-       return chipco_write32_masked(cc, SSB_CHIPCO_GPIOOUT, mask, value);
+       unsigned long flags;
+       u32 res = 0;
+
+       spin_lock_irqsave(&cc->gpio_lock, flags);
+       res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOOUT, mask, value);
+       spin_unlock_irqrestore(&cc->gpio_lock, flags);
+
+       return res;
 }
 
 u32 ssb_chipco_gpio_outen(struct ssb_chipcommon *cc, u32 mask, u32 value)
 {
-       return chipco_write32_masked(cc, SSB_CHIPCO_GPIOOUTEN, mask, value);
+       unsigned long flags;
+       u32 res = 0;
+
+       spin_lock_irqsave(&cc->gpio_lock, flags);
+       res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOOUTEN, mask, value);
+       spin_unlock_irqrestore(&cc->gpio_lock, flags);
+
+       return res;
 }
 
 u32 ssb_chipco_gpio_control(struct ssb_chipcommon *cc, u32 mask, u32 value)
 {
-       return chipco_write32_masked(cc, SSB_CHIPCO_GPIOCTL, mask, value);
+       unsigned long flags;
+       u32 res = 0;
+
+       spin_lock_irqsave(&cc->gpio_lock, flags);
+       res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOCTL, mask, value);
+       spin_unlock_irqrestore(&cc->gpio_lock, flags);
+
+       return res;
 }
 EXPORT_SYMBOL(ssb_chipco_gpio_control);
 
 u32 ssb_chipco_gpio_intmask(struct ssb_chipcommon *cc, u32 mask, u32 value)
 {
-       return chipco_write32_masked(cc, SSB_CHIPCO_GPIOIRQ, mask, value);
+       unsigned long flags;
+       u32 res = 0;
+
+       spin_lock_irqsave(&cc->gpio_lock, flags);
+       res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOIRQ, mask, value);
+       spin_unlock_irqrestore(&cc->gpio_lock, flags);
+
+       return res;
 }
 
 u32 ssb_chipco_gpio_polarity(struct ssb_chipcommon *cc, u32 mask, u32 value)
 {
-       return chipco_write32_masked(cc, SSB_CHIPCO_GPIOPOL, mask, value);
+       unsigned long flags;
+       u32 res = 0;
+
+       spin_lock_irqsave(&cc->gpio_lock, flags);
+       res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOPOL, mask, value);
+       spin_unlock_irqrestore(&cc->gpio_lock, flags);
+
+       return res;
 }
 
 u32 ssb_chipco_gpio_pullup(struct ssb_chipcommon *cc, u32 mask, u32 value)
 {
+       unsigned long flags;
+       u32 res = 0;
+
        if (cc->dev->id.revision < 20)
                return 0xffffffff;
 
-       return chipco_write32_masked(cc, SSB_CHIPCO_GPIOPULLUP, mask, value);
+       spin_lock_irqsave(&cc->gpio_lock, flags);
+       res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOPULLUP, mask, value);
+       spin_unlock_irqrestore(&cc->gpio_lock, flags);
+
+       return res;
 }
 
 u32 ssb_chipco_gpio_pulldown(struct ssb_chipcommon *cc, u32 mask, u32 value)
 {
+       unsigned long flags;
+       u32 res = 0;
+
        if (cc->dev->id.revision < 20)
                return 0xffffffff;
 
-       return chipco_write32_masked(cc, SSB_CHIPCO_GPIOPULLDOWN, mask, value);
+       spin_lock_irqsave(&cc->gpio_lock, flags);
+       res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOPULLDOWN, mask, value);
+       spin_unlock_irqrestore(&cc->gpio_lock, flags);
+
+       return res;
 }
 
 #ifdef CONFIG_SSB_SERIAL
index dc47f30e9cf7c1be458291ba0d9691bc7768e569..e1d0bb8ad7256c55a463617dd1b5745c307725e0 100644 (file)
@@ -118,6 +118,13 @@ void ssb_extif_watchdog_timer_set(struct ssb_extif *extif,
        extif_write32(extif, SSB_EXTIF_WATCHDOG, ticks);
 }
 
+void ssb_extif_init(struct ssb_extif *extif)
+{
+       if (!extif->dev)
+               return; /* We don't have a Extif core */
+       spin_lock_init(&extif->gpio_lock);
+}
+
 u32 ssb_extif_gpio_in(struct ssb_extif *extif, u32 mask)
 {
        return extif_read32(extif, SSB_EXTIF_GPIO_IN) & mask;
@@ -125,22 +132,50 @@ u32 ssb_extif_gpio_in(struct ssb_extif *extif, u32 mask)
 
 u32 ssb_extif_gpio_out(struct ssb_extif *extif, u32 mask, u32 value)
 {
-       return extif_write32_masked(extif, SSB_EXTIF_GPIO_OUT(0),
+       unsigned long flags;
+       u32 res = 0;
+
+       spin_lock_irqsave(&extif->gpio_lock, flags);
+       res = extif_write32_masked(extif, SSB_EXTIF_GPIO_OUT(0),
                                   mask, value);
+       spin_unlock_irqrestore(&extif->gpio_lock, flags);
+
+       return res;
 }
 
 u32 ssb_extif_gpio_outen(struct ssb_extif *extif, u32 mask, u32 value)
 {
-       return extif_write32_masked(extif, SSB_EXTIF_GPIO_OUTEN(0),
+       unsigned long flags;
+       u32 res = 0;
+
+       spin_lock_irqsave(&extif->gpio_lock, flags);
+       res = extif_write32_masked(extif, SSB_EXTIF_GPIO_OUTEN(0),
                                   mask, value);
+       spin_unlock_irqrestore(&extif->gpio_lock, flags);
+
+       return res;
 }
 
 u32 ssb_extif_gpio_polarity(struct ssb_extif *extif, u32 mask, u32 value)
 {
-       return extif_write32_masked(extif, SSB_EXTIF_GPIO_INTPOL, mask, value);
+       unsigned long flags;
+       u32 res = 0;
+
+       spin_lock_irqsave(&extif->gpio_lock, flags);
+       res = extif_write32_masked(extif, SSB_EXTIF_GPIO_INTPOL, mask, value);
+       spin_unlock_irqrestore(&extif->gpio_lock, flags);
+
+       return res;
 }
 
 u32 ssb_extif_gpio_intmask(struct ssb_extif *extif, u32 mask, u32 value)
 {
-       return extif_write32_masked(extif, SSB_EXTIF_GPIO_INTMASK, mask, value);
+       unsigned long flags;
+       u32 res = 0;
+
+       spin_lock_irqsave(&extif->gpio_lock, flags);
+       res = extif_write32_masked(extif, SSB_EXTIF_GPIO_INTMASK, mask, value);
+       spin_unlock_irqrestore(&extif->gpio_lock, flags);
+
+       return res;
 }
index df0f145c22fc80c175d277cca0f68647fbfd90fd..6fe2d102734a645be77d3d7fe22d2c1b58046985 100644 (file)
@@ -796,6 +796,7 @@ static int __devinit ssb_bus_register(struct ssb_bus *bus,
        if (err)
                goto err_pcmcia_exit;
        ssb_chipcommon_init(&bus->chipco);
+       ssb_extif_init(&bus->extif);
        ssb_mipscore_init(&bus->mipscore);
        err = ssb_fetch_invariants(bus, get_invariants);
        if (err) {
index a305550b4b65ecce6c5e6e925c63c36667edd3b0..d6a1ba9394d97208c88d1f0d9b07416667a3c89a 100644 (file)
@@ -211,4 +211,12 @@ static inline void b43_pci_ssb_bridge_exit(void)
 extern u32 ssb_pmu_get_cpu_clock(struct ssb_chipcommon *cc);
 extern u32 ssb_pmu_get_controlclock(struct ssb_chipcommon *cc);
 
+#ifdef CONFIG_SSB_DRIVER_EXTIF
+extern void ssb_extif_init(struct ssb_extif *extif);
+#else
+static inline void ssb_extif_init(struct ssb_extif *extif)
+{
+}
+#endif
+
 #endif /* LINUX_SSB_PRIVATE_H_ */
index c8d07c95d76ef4a965ca247aa0c721df21769746..30b694345d471f2488e4314d93f161a9a0c656c0 100644 (file)
@@ -590,6 +590,7 @@ struct ssb_chipcommon {
        u32 status;
        /* Fast Powerup Delay constant */
        u16 fast_pwrup_delay;
+       spinlock_t gpio_lock;
        struct ssb_chipcommon_pmu pmu;
 };
 
index 91161f0aa22bb874aa928943fcbdd6f74f65eda6..bd2306854a910aad9022d2f7798b911588711a5b 100644 (file)
 
 struct ssb_extif {
        struct ssb_device *dev;
+       spinlock_t gpio_lock;
 };
 
 static inline bool ssb_extif_available(struct ssb_extif *extif)