2012-11-19 22:58:31

by Hauke Mehrtens

[permalink] [raw]
Subject: [PATCH 0/8] bcma/ssb/BCM47XX: add GPIO driver to ssb/bcma

This is a complete rewrote of the original patch "MIPS: BCM47xx: use
gpiolib"
Instead of providing the GPIO driver in the arch code it is now moved
into ssb and bcma and could also be used by other systems. The GPIO
functions in drivers/ssb/embedded.c are still used by arch/mips/bcm47xx
/wgt634u.c, but I am planing to write some code for baord detection and
a driver for LED and the buttons, after that wgt634u.c could be removed.

This is based on mips/master tree.

Hauke Mehrtens (8):
bcma: add locking around GPIO register accesses
bcma: add bcma_chipco_gpio_pull{up,down}
bcma: add comment to bcma_chipco_gpio_control
bcma: add GPIO driver
ssb: add ssb_chipco_gpio_pull{up,down}
ssb: add locking around gpio register accesses
ssb: add GPIO driver
MIPS: BCM47XX: remove GPIO driver

arch/mips/Kconfig | 2 +-
arch/mips/bcm47xx/Kconfig | 2 +
arch/mips/bcm47xx/Makefile | 2 +-
arch/mips/bcm47xx/gpio.c | 102 ----------------
arch/mips/include/asm/mach-bcm47xx/gpio.h | 154 ++----------------------
drivers/bcma/Kconfig | 9 ++
drivers/bcma/Makefile | 1 +
drivers/bcma/bcma_private.h | 10 ++
drivers/bcma/driver_chipcommon.c | 90 +++++++++++++-
drivers/bcma/driver_gpio.c | 95 +++++++++++++++
drivers/bcma/main.c | 3 +
drivers/ssb/Kconfig | 9 ++
drivers/ssb/Makefile | 1 +
drivers/ssb/driver_chipcommon.c | 87 +++++++++++++-
drivers/ssb/driver_extif.c | 52 +++++++-
drivers/ssb/driver_gpio.c | 170 +++++++++++++++++++++++++++
drivers/ssb/main.c | 2 +
drivers/ssb/ssb_private.h | 17 +++
include/linux/bcma/bcma_driver_chipcommon.h | 10 ++
include/linux/ssb/ssb.h | 4 +
include/linux/ssb/ssb_driver_chipcommon.h | 3 +
include/linux/ssb/ssb_driver_extif.h | 1 +
22 files changed, 559 insertions(+), 267 deletions(-)
delete mode 100644 arch/mips/bcm47xx/gpio.c
create mode 100644 drivers/bcma/driver_gpio.c
create mode 100644 drivers/ssb/driver_gpio.c

--
1.7.10.4



2012-11-19 22:59:06

by Hauke Mehrtens

[permalink] [raw]
Subject: [PATCH 8/8] MIPS: BCM47XX: remove GPIO driver

Instated of providing an own GPIO driver use the one provided by ssb and
bcma.

Signed-off-by: Hauke Mehrtens <[email protected]>
---
arch/mips/Kconfig | 2 +-
arch/mips/bcm47xx/Kconfig | 2 +
arch/mips/bcm47xx/Makefile | 2 +-
arch/mips/bcm47xx/gpio.c | 102 -------------------
arch/mips/include/asm/mach-bcm47xx/gpio.h | 154 ++---------------------------
5 files changed, 12 insertions(+), 250 deletions(-)
delete mode 100644 arch/mips/bcm47xx/gpio.c

diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index dba9390..abc8b69 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -105,6 +105,7 @@ config ATH79

config BCM47XX
bool "Broadcom BCM47XX based boards"
+ select ARCH_WANT_OPTIONAL_GPIOLIB
select CEVT_R4K
select CSRC_R4K
select DMA_NONCOHERENT
@@ -112,7 +113,6 @@ config BCM47XX
select IRQ_CPU
select SYS_SUPPORTS_32BIT_KERNEL
select SYS_SUPPORTS_LITTLE_ENDIAN
- select GENERIC_GPIO
select SYS_HAS_EARLY_PRINTK
select CFE
help
diff --git a/arch/mips/bcm47xx/Kconfig b/arch/mips/bcm47xx/Kconfig
index b311be4..d7af29f 100644
--- a/arch/mips/bcm47xx/Kconfig
+++ b/arch/mips/bcm47xx/Kconfig
@@ -9,6 +9,7 @@ config BCM47XX_SSB
select SSB_EMBEDDED
select SSB_B43_PCI_BRIDGE if PCI
select SSB_PCICORE_HOSTMODE if PCI
+ select SSB_DRIVER_GPIO
default y
help
Add support for old Broadcom BCM47xx boards with Sonics Silicon Backplane support.
@@ -23,6 +24,7 @@ config BCM47XX_BCMA
select BCMA_DRIVER_MIPS
select BCMA_HOST_PCI if PCI
select BCMA_DRIVER_PCI_HOSTMODE if PCI
+ select BCMA_DRIVER_GPIO
default y
help
Add support for new Broadcom BCM47xx boards with Broadcom specific Advanced Microcontroller Bus.
diff --git a/arch/mips/bcm47xx/Makefile b/arch/mips/bcm47xx/Makefile
index 4389de1..1a3567f 100644
--- a/arch/mips/bcm47xx/Makefile
+++ b/arch/mips/bcm47xx/Makefile
@@ -3,5 +3,5 @@
# under Linux.
#

-obj-y += gpio.o irq.o nvram.o prom.o serial.o setup.o time.o sprom.o
+obj-y += irq.o nvram.o prom.o serial.o setup.o time.o sprom.o
obj-$(CONFIG_BCM47XX_SSB) += wgt634u.o
diff --git a/arch/mips/bcm47xx/gpio.c b/arch/mips/bcm47xx/gpio.c
deleted file mode 100644
index 5ebdf62..0000000
--- a/arch/mips/bcm47xx/gpio.c
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 2007 Aurelien Jarno <[email protected]>
- */
-
-#include <linux/export.h>
-#include <linux/ssb/ssb.h>
-#include <linux/ssb/ssb_driver_chipcommon.h>
-#include <linux/ssb/ssb_driver_extif.h>
-#include <asm/mach-bcm47xx/bcm47xx.h>
-#include <asm/mach-bcm47xx/gpio.h>
-
-#if (BCM47XX_CHIPCO_GPIO_LINES > BCM47XX_EXTIF_GPIO_LINES)
-static DECLARE_BITMAP(gpio_in_use, BCM47XX_CHIPCO_GPIO_LINES);
-#else
-static DECLARE_BITMAP(gpio_in_use, BCM47XX_EXTIF_GPIO_LINES);
-#endif
-
-int gpio_request(unsigned gpio, const char *tag)
-{
- switch (bcm47xx_bus_type) {
-#ifdef CONFIG_BCM47XX_SSB
- case BCM47XX_BUS_TYPE_SSB:
- if (ssb_chipco_available(&bcm47xx_bus.ssb.chipco) &&
- ((unsigned)gpio >= BCM47XX_CHIPCO_GPIO_LINES))
- return -EINVAL;
-
- if (ssb_extif_available(&bcm47xx_bus.ssb.extif) &&
- ((unsigned)gpio >= BCM47XX_EXTIF_GPIO_LINES))
- return -EINVAL;
-
- if (test_and_set_bit(gpio, gpio_in_use))
- return -EBUSY;
-
- return 0;
-#endif
-#ifdef CONFIG_BCM47XX_BCMA
- case BCM47XX_BUS_TYPE_BCMA:
- if (gpio >= BCM47XX_CHIPCO_GPIO_LINES)
- return -EINVAL;
-
- if (test_and_set_bit(gpio, gpio_in_use))
- return -EBUSY;
-
- return 0;
-#endif
- }
- return -EINVAL;
-}
-EXPORT_SYMBOL(gpio_request);
-
-void gpio_free(unsigned gpio)
-{
- switch (bcm47xx_bus_type) {
-#ifdef CONFIG_BCM47XX_SSB
- case BCM47XX_BUS_TYPE_SSB:
- if (ssb_chipco_available(&bcm47xx_bus.ssb.chipco) &&
- ((unsigned)gpio >= BCM47XX_CHIPCO_GPIO_LINES))
- return;
-
- if (ssb_extif_available(&bcm47xx_bus.ssb.extif) &&
- ((unsigned)gpio >= BCM47XX_EXTIF_GPIO_LINES))
- return;
-
- clear_bit(gpio, gpio_in_use);
- return;
-#endif
-#ifdef CONFIG_BCM47XX_BCMA
- case BCM47XX_BUS_TYPE_BCMA:
- if (gpio >= BCM47XX_CHIPCO_GPIO_LINES)
- return;
-
- clear_bit(gpio, gpio_in_use);
- return;
-#endif
- }
-}
-EXPORT_SYMBOL(gpio_free);
-
-int gpio_to_irq(unsigned gpio)
-{
- switch (bcm47xx_bus_type) {
-#ifdef CONFIG_BCM47XX_SSB
- case BCM47XX_BUS_TYPE_SSB:
- if (ssb_chipco_available(&bcm47xx_bus.ssb.chipco))
- return ssb_mips_irq(bcm47xx_bus.ssb.chipco.dev) + 2;
- else if (ssb_extif_available(&bcm47xx_bus.ssb.extif))
- return ssb_mips_irq(bcm47xx_bus.ssb.extif.dev) + 2;
- else
- return -EINVAL;
-#endif
-#ifdef CONFIG_BCM47XX_BCMA
- case BCM47XX_BUS_TYPE_BCMA:
- return bcma_core_mips_irq(bcm47xx_bus.bcma.bus.drv_cc.core) + 2;
-#endif
- }
- return -EINVAL;
-}
-EXPORT_SYMBOL_GPL(gpio_to_irq);
diff --git a/arch/mips/include/asm/mach-bcm47xx/gpio.h b/arch/mips/include/asm/mach-bcm47xx/gpio.h
index 2ef17e8..90daefa 100644
--- a/arch/mips/include/asm/mach-bcm47xx/gpio.h
+++ b/arch/mips/include/asm/mach-bcm47xx/gpio.h
@@ -1,155 +1,17 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 2007 Aurelien Jarno <[email protected]>
- */
+#ifndef __ASM_MIPS_MACH_BCM47XX_GPIO_H
+#define __ASM_MIPS_MACH_BCM47XX_GPIO_H

-#ifndef __BCM47XX_GPIO_H
-#define __BCM47XX_GPIO_H
+#include <asm-generic/gpio.h>

-#include <linux/ssb/ssb_embedded.h>
-#include <linux/bcma/bcma.h>
-#include <asm/mach-bcm47xx/bcm47xx.h>
+#define gpio_get_value __gpio_get_value
+#define gpio_set_value __gpio_set_value

-#define BCM47XX_EXTIF_GPIO_LINES 5
-#define BCM47XX_CHIPCO_GPIO_LINES 16
+#define gpio_cansleep __gpio_cansleep
+#define gpio_to_irq __gpio_to_irq

-extern int gpio_request(unsigned gpio, const char *label);
-extern void gpio_free(unsigned gpio);
-extern int gpio_to_irq(unsigned gpio);
-
-static inline int gpio_get_value(unsigned gpio)
+static inline int irq_to_gpio(unsigned int irq)
{
- switch (bcm47xx_bus_type) {
-#ifdef CONFIG_BCM47XX_SSB
- case BCM47XX_BUS_TYPE_SSB:
- return ssb_gpio_in(&bcm47xx_bus.ssb, 1 << gpio);
-#endif
-#ifdef CONFIG_BCM47XX_BCMA
- case BCM47XX_BUS_TYPE_BCMA:
- return bcma_chipco_gpio_in(&bcm47xx_bus.bcma.bus.drv_cc,
- 1 << gpio);
-#endif
- }
return -EINVAL;
}

-#define gpio_get_value_cansleep gpio_get_value
-
-static inline void gpio_set_value(unsigned gpio, int value)
-{
- switch (bcm47xx_bus_type) {
-#ifdef CONFIG_BCM47XX_SSB
- case BCM47XX_BUS_TYPE_SSB:
- ssb_gpio_out(&bcm47xx_bus.ssb, 1 << gpio,
- value ? 1 << gpio : 0);
- return;
-#endif
-#ifdef CONFIG_BCM47XX_BCMA
- case BCM47XX_BUS_TYPE_BCMA:
- bcma_chipco_gpio_out(&bcm47xx_bus.bcma.bus.drv_cc, 1 << gpio,
- value ? 1 << gpio : 0);
- return;
#endif
- }
-}
-
-#define gpio_set_value_cansleep gpio_set_value
-
-static inline int gpio_cansleep(unsigned gpio)
-{
- return 0;
-}
-
-static inline int gpio_is_valid(unsigned gpio)
-{
- return gpio < (BCM47XX_EXTIF_GPIO_LINES + BCM47XX_CHIPCO_GPIO_LINES);
-}
-
-
-static inline int gpio_direction_input(unsigned gpio)
-{
- switch (bcm47xx_bus_type) {
-#ifdef CONFIG_BCM47XX_SSB
- case BCM47XX_BUS_TYPE_SSB:
- ssb_gpio_outen(&bcm47xx_bus.ssb, 1 << gpio, 0);
- return 0;
-#endif
-#ifdef CONFIG_BCM47XX_BCMA
- case BCM47XX_BUS_TYPE_BCMA:
- bcma_chipco_gpio_outen(&bcm47xx_bus.bcma.bus.drv_cc, 1 << gpio,
- 0);
- return 0;
-#endif
- }
- return -EINVAL;
-}
-
-static inline int gpio_direction_output(unsigned gpio, int value)
-{
- switch (bcm47xx_bus_type) {
-#ifdef CONFIG_BCM47XX_SSB
- case BCM47XX_BUS_TYPE_SSB:
- /* first set the gpio out value */
- ssb_gpio_out(&bcm47xx_bus.ssb, 1 << gpio,
- value ? 1 << gpio : 0);
- /* then set the gpio mode */
- ssb_gpio_outen(&bcm47xx_bus.ssb, 1 << gpio, 1 << gpio);
- return 0;
-#endif
-#ifdef CONFIG_BCM47XX_BCMA
- case BCM47XX_BUS_TYPE_BCMA:
- /* first set the gpio out value */
- bcma_chipco_gpio_out(&bcm47xx_bus.bcma.bus.drv_cc, 1 << gpio,
- value ? 1 << gpio : 0);
- /* then set the gpio mode */
- bcma_chipco_gpio_outen(&bcm47xx_bus.bcma.bus.drv_cc, 1 << gpio,
- 1 << gpio);
- return 0;
-#endif
- }
- return -EINVAL;
-}
-
-static inline int gpio_intmask(unsigned gpio, int value)
-{
- switch (bcm47xx_bus_type) {
-#ifdef CONFIG_BCM47XX_SSB
- case BCM47XX_BUS_TYPE_SSB:
- ssb_gpio_intmask(&bcm47xx_bus.ssb, 1 << gpio,
- value ? 1 << gpio : 0);
- return 0;
-#endif
-#ifdef CONFIG_BCM47XX_BCMA
- case BCM47XX_BUS_TYPE_BCMA:
- bcma_chipco_gpio_intmask(&bcm47xx_bus.bcma.bus.drv_cc,
- 1 << gpio, value ? 1 << gpio : 0);
- return 0;
-#endif
- }
- return -EINVAL;
-}
-
-static inline int gpio_polarity(unsigned gpio, int value)
-{
- switch (bcm47xx_bus_type) {
-#ifdef CONFIG_BCM47XX_SSB
- case BCM47XX_BUS_TYPE_SSB:
- ssb_gpio_polarity(&bcm47xx_bus.ssb, 1 << gpio,
- value ? 1 << gpio : 0);
- return 0;
-#endif
-#ifdef CONFIG_BCM47XX_BCMA
- case BCM47XX_BUS_TYPE_BCMA:
- bcma_chipco_gpio_polarity(&bcm47xx_bus.bcma.bus.drv_cc,
- 1 << gpio, value ? 1 << gpio : 0);
- return 0;
-#endif
- }
- return -EINVAL;
-}
-
-
-#endif /* __BCM47XX_GPIO_H */
--
1.7.10.4


2012-11-19 22:58:49

by Hauke Mehrtens

[permalink] [raw]
Subject: [PATCH 4/8] bcma: add GPIO driver

Register a GPIO driver to access the GPIOs provided by the chip.
The GPIOs of the SoC should always start at 0 and the other GPIOs could
start at a random position. There is just one SoC in a system and when
they start at 0 the number is predictable.

Signed-off-by: Hauke Mehrtens <[email protected]>
---
drivers/bcma/Kconfig | 9 +++
drivers/bcma/Makefile | 1 +
drivers/bcma/bcma_private.h | 10 +++
drivers/bcma/driver_gpio.c | 95 +++++++++++++++++++++++++++
drivers/bcma/main.c | 3 +
include/linux/bcma/bcma_driver_chipcommon.h | 5 ++
6 files changed, 123 insertions(+)
create mode 100644 drivers/bcma/driver_gpio.c

diff --git a/drivers/bcma/Kconfig b/drivers/bcma/Kconfig
index a533af2..871400c 100644
--- a/drivers/bcma/Kconfig
+++ b/drivers/bcma/Kconfig
@@ -65,6 +65,15 @@ config BCMA_DRIVER_GMAC_CMN

If unsure, say N

+config BCMA_DRIVER_GPIO
+ bool
+ depends on BCMA
+ select GPIOLIB
+ help
+ Driver to provide access to the GPIO pins.
+
+ If unsure, say N
+
config BCMA_DEBUG
bool "BCMA debugging"
depends on BCMA
diff --git a/drivers/bcma/Makefile b/drivers/bcma/Makefile
index 8ad42d4..734b32f 100644
--- a/drivers/bcma/Makefile
+++ b/drivers/bcma/Makefile
@@ -6,6 +6,7 @@ bcma-y += driver_pci.o
bcma-$(CONFIG_BCMA_DRIVER_PCI_HOSTMODE) += driver_pci_host.o
bcma-$(CONFIG_BCMA_DRIVER_MIPS) += driver_mips.o
bcma-$(CONFIG_BCMA_DRIVER_GMAC_CMN) += driver_gmac_cmn.o
+bcma-$(CONFIG_BCMA_DRIVER_GPIO) += driver_gpio.o
bcma-$(CONFIG_BCMA_HOST_PCI) += host_pci.o
bcma-$(CONFIG_BCMA_HOST_SOC) += host_soc.o
obj-$(CONFIG_BCMA) += bcma.o
diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h
index 169fc58..522d949 100644
--- a/drivers/bcma/bcma_private.h
+++ b/drivers/bcma/bcma_private.h
@@ -89,4 +89,14 @@ bool __devinit bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc);
void __devinit bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc);
#endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */

+#ifdef CONFIG_BCMA_DRIVER_GPIO
+/* driver_gpio.c */
+int bcma_gpio_init(struct bcma_drv_cc *cc);
+#else
+static inline int bcma_gpio_init(struct bcma_drv_cc *cc)
+{
+ return 0;
+}
+#endif /* CONFIG_BCMA_DRIVER_GPIO */
+
#endif
diff --git a/drivers/bcma/driver_gpio.c b/drivers/bcma/driver_gpio.c
new file mode 100644
index 0000000..2b9e404
--- /dev/null
+++ b/drivers/bcma/driver_gpio.c
@@ -0,0 +1,95 @@
+/*
+ * Broadcom specific AMBA
+ * GPIO driver
+ *
+ * Copyright 2011, Broadcom Corporation
+ * Copyright 2012, Hauke Mehrtens <[email protected]>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include <linux/gpio.h>
+#include <linux/export.h>
+#include <linux/bcma/bcma.h>
+
+#include "bcma_private.h"
+
+static inline struct bcma_drv_cc *bcma_gpio_get_cc(struct gpio_chip *chip)
+{
+ return container_of(chip, struct bcma_drv_cc, gpio);
+}
+
+static int bcma_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
+{
+ struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip);
+
+ return !!bcma_chipco_gpio_in(cc, 1 << gpio);
+}
+
+static void bcma_gpio_set_value(struct gpio_chip *chip, unsigned gpio,
+ int value)
+{
+ struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip);
+
+ bcma_chipco_gpio_out(cc, 1 << gpio, value ? 1 << gpio : 0);
+}
+
+static int bcma_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
+{
+ struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip);
+
+ bcma_chipco_gpio_outen(cc, 1 << gpio, 0);
+ return 0;
+}
+
+static int bcma_gpio_direction_output(struct gpio_chip *chip, unsigned gpio,
+ int value)
+{
+ struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip);
+
+ bcma_chipco_gpio_outen(cc, 1 << gpio, 1 << gpio);
+ bcma_chipco_gpio_out(cc, 1 << gpio, value ? 1 << gpio : 0);
+ return 0;
+}
+
+static int bcma_gpio_request(struct gpio_chip *chip, unsigned gpio)
+{
+ struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip);
+
+ bcma_chipco_gpio_control(cc, 1 << gpio, 0);
+ /* clear pulldown */
+ bcma_chipco_gpio_pulldown(cc, 1 << gpio, 0);
+ /* Set pullup */
+ bcma_chipco_gpio_pullup(cc, 1 << gpio, 1 << gpio);
+
+ return 0;
+}
+
+static void bcma_gpio_free(struct gpio_chip *chip, unsigned gpio)
+{
+ struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip);
+
+ /* clear pullup */
+ bcma_chipco_gpio_pullup(cc, 1 << gpio, 0);
+}
+
+int bcma_gpio_init(struct bcma_drv_cc *cc)
+{
+ struct gpio_chip *chip = &cc->gpio;
+
+ chip->label = "bcma_gpio";
+ chip->owner = THIS_MODULE;
+ chip->request = bcma_gpio_request;
+ chip->free = bcma_gpio_free;
+ chip->get = bcma_gpio_get_value;
+ chip->set = bcma_gpio_set_value;
+ chip->direction_input = bcma_gpio_direction_input;
+ chip->direction_output = bcma_gpio_direction_output;
+ chip->ngpio = 16;
+ if (cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC)
+ chip->base = 0;
+ else
+ chip->base = -1;
+
+ return gpiochip_add(chip);
+}
diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c
index d865470..0c2e5ea 100644
--- a/drivers/bcma/main.c
+++ b/drivers/bcma/main.c
@@ -152,6 +152,9 @@ static int bcma_register_cores(struct bcma_bus *bus)
bcma_err(bus, "Error registering NAND flash\n");
}
#endif
+ err = bcma_gpio_init(&bus->drv_cc);
+ if (err)
+ bcma_err(bus, "Error registering GPIO driver\n");

return 0;
}
diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h
index 2567026..7d662a9 100644
--- a/include/linux/bcma/bcma_driver_chipcommon.h
+++ b/include/linux/bcma/bcma_driver_chipcommon.h
@@ -1,6 +1,8 @@
#ifndef LINUX_BCMA_DRIVER_CC_H_
#define LINUX_BCMA_DRIVER_CC_H_

+#include <linux/gpio.h>
+
/** ChipCommon core registers. **/
#define BCMA_CC_ID 0x0000
#define BCMA_CC_ID_ID 0x0000FFFF
@@ -570,6 +572,9 @@ struct bcma_drv_cc {

/* Lock for GPIO register access. */
spinlock_t gpio_lock;
+#ifdef CONFIG_BCMA_DRIVER_GPIO
+ struct gpio_chip gpio;
+#endif
};

/* Register access */
--
1.7.10.4


2012-11-19 22:58:39

by Hauke Mehrtens

[permalink] [raw]
Subject: [PATCH 3/8] bcma: add comment to bcma_chipco_gpio_control

Add description to the function.

Signed-off-by: Hauke Mehrtens <[email protected]>
---
drivers/bcma/driver_chipcommon.c | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/drivers/bcma/driver_chipcommon.c b/drivers/bcma/driver_chipcommon.c
index f3c736a..67a1c6b 100644
--- a/drivers/bcma/driver_chipcommon.c
+++ b/drivers/bcma/driver_chipcommon.c
@@ -115,6 +115,10 @@ u32 bcma_chipco_gpio_outen(struct bcma_drv_cc *cc, u32 mask, u32 value)
return res;
}

+/*
+ * If the bit is set to 0, chipcommon controlls this GPIO,
+ * if the bit is set to 1, it is used by some part of the chip and not our code.
+ */
u32 bcma_chipco_gpio_control(struct bcma_drv_cc *cc, u32 mask, u32 value)
{
unsigned long flags;
--
1.7.10.4


2012-11-19 22:58:57

by Hauke Mehrtens

[permalink] [raw]
Subject: [PATCH 7/8] ssb: add GPIO driver

Register a GPIO driver to access the GPIOs provided by the chip.
The GPIOs of the SoC should always start at 0 and the other GPIOs could
start at a random position. There is just one SoC in a system and when
they start at 0 the number is predictable.

Signed-off-by: Hauke Mehrtens <[email protected]>
---
drivers/ssb/Kconfig | 9 +++
drivers/ssb/Makefile | 1 +
drivers/ssb/driver_gpio.c | 170 +++++++++++++++++++++++++++++++++++++++++++++
drivers/ssb/main.c | 1 +
drivers/ssb/ssb_private.h | 9 +++
include/linux/ssb/ssb.h | 4 ++
6 files changed, 194 insertions(+)
create mode 100644 drivers/ssb/driver_gpio.c

diff --git a/drivers/ssb/Kconfig b/drivers/ssb/Kconfig
index 42cdaa9..8b5460f 100644
--- a/drivers/ssb/Kconfig
+++ b/drivers/ssb/Kconfig
@@ -160,4 +160,13 @@ config SSB_DRIVER_GIGE

If unsure, say N

+config SSB_DRIVER_GPIO
+ bool
+ depends on SSB
+ select GPIOLIB
+ help
+ Driver to provide access to the GPIO pins.
+
+ If unsure, say N
+
endmenu
diff --git a/drivers/ssb/Makefile b/drivers/ssb/Makefile
index 656e58b..9159ba7 100644
--- a/drivers/ssb/Makefile
+++ b/drivers/ssb/Makefile
@@ -15,6 +15,7 @@ ssb-$(CONFIG_SSB_DRIVER_MIPS) += driver_mipscore.o
ssb-$(CONFIG_SSB_DRIVER_EXTIF) += driver_extif.o
ssb-$(CONFIG_SSB_DRIVER_PCICORE) += driver_pcicore.o
ssb-$(CONFIG_SSB_DRIVER_GIGE) += driver_gige.o
+ssb-$(CONFIG_SSB_DRIVER_GPIO) += driver_gpio.o

# b43 pci-ssb-bridge driver
# Not strictly a part of SSB, but kept here for convenience
diff --git a/drivers/ssb/driver_gpio.c b/drivers/ssb/driver_gpio.c
new file mode 100644
index 0000000..e4ab9f6
--- /dev/null
+++ b/drivers/ssb/driver_gpio.c
@@ -0,0 +1,170 @@
+/*
+ * Sonics Silicon Backplane
+ * GPIO driver
+ *
+ * Copyright 2011, Broadcom Corporation
+ * Copyright 2012, Hauke Mehrtens <[email protected]>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include <linux/gpio.h>
+#include <linux/export.h>
+#include <linux/ssb/ssb.h>
+
+#include "ssb_private.h"
+
+static struct ssb_bus *ssb_gpio_get_bus(struct gpio_chip *chip)
+{
+ return container_of(chip, struct ssb_bus, gpio);
+}
+
+static int ssb_gpio_chipco_get_value(struct gpio_chip *chip, unsigned gpio)
+{
+ struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+ return !!ssb_chipco_gpio_in(&bus->chipco, 1 << gpio);
+}
+
+static void ssb_gpio_chipco_set_value(struct gpio_chip *chip, unsigned gpio,
+ int value)
+{
+ struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+ ssb_chipco_gpio_out(&bus->chipco, 1 << gpio, value ? 1 << gpio : 0);
+}
+
+static int ssb_gpio_chipco_direction_input(struct gpio_chip *chip,
+ unsigned gpio)
+{
+ struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+ ssb_chipco_gpio_outen(&bus->chipco, 1 << gpio, 0);
+ return 0;
+}
+
+static int ssb_gpio_chipco_direction_output(struct gpio_chip *chip,
+ unsigned gpio, int value)
+{
+ struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+ ssb_chipco_gpio_outen(&bus->chipco, 1 << gpio, 1 << gpio);
+ ssb_chipco_gpio_out(&bus->chipco, 1 << gpio, value ? 1 << gpio : 0);
+ return 0;
+}
+
+static int ssb_gpio_chipco_request(struct gpio_chip *chip, unsigned gpio)
+{
+ struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+ ssb_chipco_gpio_control(&bus->chipco, 1 << gpio, 0);
+ /* clear pulldown */
+ ssb_chipco_gpio_pulldown(&bus->chipco, 1 << gpio, 0);
+ /* Set pullup */
+ ssb_chipco_gpio_pullup(&bus->chipco, 1 << gpio, 1 << gpio);
+
+ return 0;
+}
+
+static void ssb_gpio_chipco_free(struct gpio_chip *chip, unsigned gpio)
+{
+ struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+ /* clear pullup */
+ ssb_chipco_gpio_pullup(&bus->chipco, 1 << gpio, 0);
+}
+
+static int ssb_gpio_chipco_init(struct ssb_bus *bus)
+{
+ struct gpio_chip *chip = &bus->gpio;
+
+ chip->label = "ssb_chipco_gpio";
+ chip->owner = THIS_MODULE;
+ chip->request = ssb_gpio_chipco_request;
+ chip->free = ssb_gpio_chipco_free;
+ chip->get = ssb_gpio_chipco_get_value;
+ chip->set = ssb_gpio_chipco_set_value;
+ chip->direction_input = ssb_gpio_chipco_direction_input;
+ chip->direction_output = ssb_gpio_chipco_direction_output;
+ chip->ngpio = 16;
+ if (bus->bustype == SSB_BUSTYPE_SSB)
+ chip->base = 0;
+ else
+ chip->base = -1;
+
+ return gpiochip_add(chip);
+}
+
+#ifdef CONFIG_SSB_DRIVER_EXTIF
+
+static int ssb_gpio_extif_get_value(struct gpio_chip *chip, unsigned gpio)
+{
+ struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+ return !!ssb_extif_gpio_in(&bus->extif, 1 << gpio);
+}
+
+static void ssb_gpio_extif_set_value(struct gpio_chip *chip, unsigned gpio,
+ int value)
+{
+ struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+ ssb_extif_gpio_out(&bus->extif, 1 << gpio, value ? 1 << gpio : 0);
+}
+
+static int ssb_gpio_extif_direction_input(struct gpio_chip *chip,
+ unsigned gpio)
+{
+ struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+ ssb_extif_gpio_outen(&bus->extif, 1 << gpio, 0);
+ return 0;
+}
+
+static int ssb_gpio_extif_direction_output(struct gpio_chip *chip,
+ unsigned gpio, int value)
+{
+ struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+ ssb_extif_gpio_outen(&bus->extif, 1 << gpio, 1 << gpio);
+ ssb_extif_gpio_out(&bus->extif, 1 << gpio, value ? 1 << gpio : 0);
+ return 0;
+}
+
+static int ssb_gpio_extif_init(struct ssb_bus *bus)
+{
+ struct gpio_chip *chip = &bus->gpio;
+
+ chip->label = "ssb_extif_gpio";
+ chip->owner = THIS_MODULE;
+ chip->get = ssb_gpio_extif_get_value;
+ chip->set = ssb_gpio_extif_set_value;
+ chip->direction_input = ssb_gpio_extif_direction_input;
+ chip->direction_output = ssb_gpio_extif_direction_output;
+ chip->ngpio = 5;
+ if (bus->bustype == SSB_BUSTYPE_SSB)
+ chip->base = 0;
+ else
+ chip->base = -1;
+
+ return gpiochip_add(chip);
+}
+
+#else
+static int ssb_gpio_extif_init(struct ssb_bus *bus)
+{
+ return 0;
+}
+#endif
+
+int ssb_gpio_init(struct ssb_bus *bus)
+{
+ if (ssb_chipco_available(&bus->chipco))
+ return ssb_gpio_chipco_init(bus);
+ else if (ssb_extif_available(&bus->extif))
+ return ssb_gpio_extif_init(bus);
+ else
+ SSB_WARN_ON(1);
+
+ return -1;
+}
diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c
index 6fe2d10..6ac1ca3 100644
--- a/drivers/ssb/main.c
+++ b/drivers/ssb/main.c
@@ -798,6 +798,7 @@ static int __devinit ssb_bus_register(struct ssb_bus *bus,
ssb_chipcommon_init(&bus->chipco);
ssb_extif_init(&bus->extif);
ssb_mipscore_init(&bus->mipscore);
+ ssb_gpio_init(bus);
err = ssb_fetch_invariants(bus, get_invariants);
if (err) {
ssb_bus_may_powerdown(bus);
diff --git a/drivers/ssb/ssb_private.h b/drivers/ssb/ssb_private.h
index d6a1ba9..8497397 100644
--- a/drivers/ssb/ssb_private.h
+++ b/drivers/ssb/ssb_private.h
@@ -219,4 +219,13 @@ static inline void ssb_extif_init(struct ssb_extif *extif)
}
#endif

+#ifdef CONFIG_SSB_DRIVER_GPIO
+extern int ssb_gpio_init(struct ssb_bus *bus);
+#else /* CONFIG_SSB_DRIVER_GPIO */
+static inline int ssb_gpio_init(struct ssb_bus *bus);
+{
+ return 0;
+}
+#endif /* CONFIG_SSB_DRIVER_GPIO */
+
#endif /* LINUX_SSB_PRIVATE_H_ */
diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h
index bb674c0..3862a5b 100644
--- a/include/linux/ssb/ssb.h
+++ b/include/linux/ssb/ssb.h
@@ -6,6 +6,7 @@
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/pci.h>
+#include <linux/gpio.h>
#include <linux/mod_devicetable.h>
#include <linux/dma-mapping.h>

@@ -433,6 +434,9 @@ struct ssb_bus {
/* Lock for GPIO register access. */
spinlock_t gpio_lock;
#endif /* EMBEDDED */
+#ifdef CONFIG_SSB_DRIVER_GPIO
+ struct gpio_chip gpio;
+#endif /* DRIVER_GPIO */

/* Internal-only stuff follows. Do not touch. */
struct list_head list;
--
1.7.10.4


2012-11-20 21:05:36

by Hauke Mehrtens

[permalink] [raw]
Subject: Re: [PATCH 1/8] bcma: add locking around GPIO register accesses

On 11/20/2012 09:10 AM, John Crispin wrote:
> Hi Hauke
>
>> u32 bcma_chipco_gpio_in(struct bcma_drv_cc *cc, u32 mask)
>> {
>> - return bcma_cc_read32(cc, BCMA_CC_GPIOIN)& mask;
>> + unsigned long flags;
>> + u32 res;
>> +
>> + spin_lock_irqsave(&cc->gpio_lock, flags);
>> + res = bcma_cc_read32(cc, BCMA_CC_GPIOIN)& mask;
>> + spin_unlock_irqrestore(&cc->gpio_lock, flags);
>> +
>> + return res;
>> }
>>
>
> Hi Hauke,
>
> do you need to lock the read access ?
>
> if bcma_cc_read32() is a simple memory read wrapper you most likely wont
> need the lock
>
> John
Yes you are right, I do not need it here, it is just needed for the
masked write to the other GPIO registers.

Hauke

2012-11-19 22:58:42

by Hauke Mehrtens

[permalink] [raw]
Subject: [PATCH 2/8] bcma: add bcma_chipco_gpio_pull{up,down}

Add functions to access the GPIO registers for pullup and pulldown.
These are needed for handling gpio registration.

Signed-off-by: Hauke Mehrtens <[email protected]>
---
drivers/bcma/driver_chipcommon.c | 30 +++++++++++++++++++++++++++
include/linux/bcma/bcma_driver_chipcommon.h | 2 ++
2 files changed, 32 insertions(+)

diff --git a/drivers/bcma/driver_chipcommon.c b/drivers/bcma/driver_chipcommon.c
index 9b34316..f3c736a 100644
--- a/drivers/bcma/driver_chipcommon.c
+++ b/drivers/bcma/driver_chipcommon.c
@@ -152,6 +152,36 @@ u32 bcma_chipco_gpio_polarity(struct bcma_drv_cc *cc, u32 mask, u32 value)
return res;
}

+u32 bcma_chipco_gpio_pullup(struct bcma_drv_cc *cc, u32 mask, u32 value)
+{
+ unsigned long flags;
+ u32 res;
+
+ if (cc->core->id.rev < 20)
+ return 0;
+
+ spin_lock_irqsave(&cc->gpio_lock, flags);
+ res = bcma_cc_write32_masked(cc, BCMA_CC_GPIOPULLUP, mask, value);
+ spin_unlock_irqrestore(&cc->gpio_lock, flags);
+
+ return res;
+}
+
+u32 bcma_chipco_gpio_pulldown(struct bcma_drv_cc *cc, u32 mask, u32 value)
+{
+ unsigned long flags;
+ u32 res;
+
+ if (cc->core->id.rev < 20)
+ return 0;
+
+ spin_lock_irqsave(&cc->gpio_lock, flags);
+ res = bcma_cc_write32_masked(cc, BCMA_CC_GPIOPULLDOWN, mask, value);
+ spin_unlock_irqrestore(&cc->gpio_lock, flags);
+
+ return res;
+}
+
#ifdef CONFIG_BCMA_DRIVER_MIPS
void bcma_chipco_serial_init(struct bcma_drv_cc *cc)
{
diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h
index a085d98..2567026 100644
--- a/include/linux/bcma/bcma_driver_chipcommon.h
+++ b/include/linux/bcma/bcma_driver_chipcommon.h
@@ -606,6 +606,8 @@ u32 bcma_chipco_gpio_outen(struct bcma_drv_cc *cc, u32 mask, u32 value);
u32 bcma_chipco_gpio_control(struct bcma_drv_cc *cc, u32 mask, u32 value);
u32 bcma_chipco_gpio_intmask(struct bcma_drv_cc *cc, u32 mask, u32 value);
u32 bcma_chipco_gpio_polarity(struct bcma_drv_cc *cc, u32 mask, u32 value);
+u32 bcma_chipco_gpio_pullup(struct bcma_drv_cc *cc, u32 mask, u32 value);
+u32 bcma_chipco_gpio_pulldown(struct bcma_drv_cc *cc, u32 mask, u32 value);

/* PMU support */
extern void bcma_pmu_init(struct bcma_drv_cc *cc);
--
1.7.10.4


2012-11-20 08:11:52

by John Crispin

[permalink] [raw]
Subject: Re: [PATCH 1/8] bcma: add locking around GPIO register accesses

Hi Hauke

> u32 bcma_chipco_gpio_in(struct bcma_drv_cc *cc, u32 mask)
> {
> - return bcma_cc_read32(cc, BCMA_CC_GPIOIN)& mask;
> + unsigned long flags;
> + u32 res;
> +
> + spin_lock_irqsave(&cc->gpio_lock, flags);
> + res = bcma_cc_read32(cc, BCMA_CC_GPIOIN)& mask;
> + spin_unlock_irqrestore(&cc->gpio_lock, flags);
> +
> + return res;
> }
>

Hi Hauke,

do you need to lock the read access ?

if bcma_cc_read32() is a simple memory read wrapper you most likely wont
need the lock

John

2012-11-19 22:58:32

by Hauke Mehrtens

[permalink] [raw]
Subject: [PATCH 1/8] bcma: add locking around GPIO register accesses

The GPIOs are access through some registers in the chip common core.
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 <[email protected]>
---
drivers/bcma/driver_chipcommon.c | 56 ++++++++++++++++++++++++---
include/linux/bcma/bcma_driver_chipcommon.h | 3 ++
2 files changed, 53 insertions(+), 6 deletions(-)

diff --git a/drivers/bcma/driver_chipcommon.c b/drivers/bcma/driver_chipcommon.c
index a4c3ebc..9b34316 100644
--- a/drivers/bcma/driver_chipcommon.c
+++ b/drivers/bcma/driver_chipcommon.c
@@ -30,6 +30,8 @@ void bcma_core_chipcommon_init(struct bcma_drv_cc *cc)
if (cc->setup_done)
return;

+ spin_lock_init(&cc->gpio_lock);
+
if (cc->core->id.rev >= 11)
cc->status = bcma_cc_read32(cc, BCMA_CC_CHIPSTAT);
cc->capabilities = bcma_cc_read32(cc, BCMA_CC_CAP);
@@ -79,33 +81,75 @@ u32 bcma_chipco_irq_status(struct bcma_drv_cc *cc, u32 mask)

u32 bcma_chipco_gpio_in(struct bcma_drv_cc *cc, u32 mask)
{
- return bcma_cc_read32(cc, BCMA_CC_GPIOIN) & mask;
+ unsigned long flags;
+ u32 res;
+
+ spin_lock_irqsave(&cc->gpio_lock, flags);
+ res = bcma_cc_read32(cc, BCMA_CC_GPIOIN) & mask;
+ spin_unlock_irqrestore(&cc->gpio_lock, flags);
+
+ return res;
}

u32 bcma_chipco_gpio_out(struct bcma_drv_cc *cc, u32 mask, u32 value)
{
- return bcma_cc_write32_masked(cc, BCMA_CC_GPIOOUT, mask, value);
+ unsigned long flags;
+ u32 res;
+
+ spin_lock_irqsave(&cc->gpio_lock, flags);
+ res = bcma_cc_write32_masked(cc, BCMA_CC_GPIOOUT, mask, value);
+ spin_unlock_irqrestore(&cc->gpio_lock, flags);
+
+ return res;
}

u32 bcma_chipco_gpio_outen(struct bcma_drv_cc *cc, u32 mask, u32 value)
{
- return bcma_cc_write32_masked(cc, BCMA_CC_GPIOOUTEN, mask, value);
+ unsigned long flags;
+ u32 res;
+
+ spin_lock_irqsave(&cc->gpio_lock, flags);
+ res = bcma_cc_write32_masked(cc, BCMA_CC_GPIOOUTEN, mask, value);
+ spin_unlock_irqrestore(&cc->gpio_lock, flags);
+
+ return res;
}

u32 bcma_chipco_gpio_control(struct bcma_drv_cc *cc, u32 mask, u32 value)
{
- return bcma_cc_write32_masked(cc, BCMA_CC_GPIOCTL, mask, value);
+ unsigned long flags;
+ u32 res;
+
+ spin_lock_irqsave(&cc->gpio_lock, flags);
+ res = bcma_cc_write32_masked(cc, BCMA_CC_GPIOCTL, mask, value);
+ spin_unlock_irqrestore(&cc->gpio_lock, flags);
+
+ return res;
}
EXPORT_SYMBOL_GPL(bcma_chipco_gpio_control);

u32 bcma_chipco_gpio_intmask(struct bcma_drv_cc *cc, u32 mask, u32 value)
{
- return bcma_cc_write32_masked(cc, BCMA_CC_GPIOIRQ, mask, value);
+ unsigned long flags;
+ u32 res;
+
+ spin_lock_irqsave(&cc->gpio_lock, flags);
+ res = bcma_cc_write32_masked(cc, BCMA_CC_GPIOIRQ, mask, value);
+ spin_unlock_irqrestore(&cc->gpio_lock, flags);
+
+ return res;
}

u32 bcma_chipco_gpio_polarity(struct bcma_drv_cc *cc, u32 mask, u32 value)
{
- return bcma_cc_write32_masked(cc, BCMA_CC_GPIOPOL, mask, value);
+ unsigned long flags;
+ u32 res;
+
+ spin_lock_irqsave(&cc->gpio_lock, flags);
+ res = bcma_cc_write32_masked(cc, BCMA_CC_GPIOPOL, mask, value);
+ spin_unlock_irqrestore(&cc->gpio_lock, flags);
+
+ return res;
}

#ifdef CONFIG_BCMA_DRIVER_MIPS
diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h
index 1cf1749..a085d98 100644
--- a/include/linux/bcma/bcma_driver_chipcommon.h
+++ b/include/linux/bcma/bcma_driver_chipcommon.h
@@ -567,6 +567,9 @@ struct bcma_drv_cc {
int nr_serial_ports;
struct bcma_serial_port serial_ports[4];
#endif /* CONFIG_BCMA_DRIVER_MIPS */
+
+ /* Lock for GPIO register access. */
+ spinlock_t gpio_lock;
};

/* Register access */
--
1.7.10.4


2012-11-20 22:33:26

by Hauke Mehrtens

[permalink] [raw]
Subject: Re: [PATCH 4/8] bcma: add GPIO driver

On 11/20/2012 10:42 AM, Florian Fainelli wrote:
> Hi Hauke,
>
> This driver looks good to me, a couple of minor comments below.
>
> On Monday 19 November 2012 23:57:53 Hauke Mehrtens wrote:
>> Register a GPIO driver to access the GPIOs provided by the chip.
>> The GPIOs of the SoC should always start at 0 and the other GPIOs could
>> start at a random position. There is just one SoC in a system and when
>> they start at 0 the number is predictable.
>>
>> Signed-off-by: Hauke Mehrtens <[email protected]>
>> ---
> [snip]
>> +#ifdef CONFIG_BCMA_DRIVER_GPIO
>> +/* driver_gpio.c */
>> +int bcma_gpio_init(struct bcma_drv_cc *cc);
>> +#else
>> +static inline int bcma_gpio_init(struct bcma_drv_cc *cc)
>> +{
>> + return 0;
>> +}
>> +#endif /* CONFIG_BCMA_DRIVER_GPIO */
>
> I wonder if it would not make more sense here to return -ENODEV or -ENOTSUPP
> so we can identify a kernel not being built with BCMA GPIO support.

I added that and changed the logging for such a message to debug level,
but I do not know if this would confuse people just using bcma/ssb for
their wireless pcie cards.

>> +
>> #endif
>> diff --git a/drivers/bcma/driver_gpio.c b/drivers/bcma/driver_gpio.c
>> new file mode 100644
>> index 0000000..2b9e404
>> --- /dev/null
>> +++ b/drivers/bcma/driver_gpio.c
>> @@ -0,0 +1,95 @@
>> +/*
>> + * Broadcom specific AMBA
>> + * GPIO driver
>> + *
>> + * Copyright 2011, Broadcom Corporation
>> + * Copyright 2012, Hauke Mehrtens <[email protected]>
>> + *
>> + * Licensed under the GNU/GPL. See COPYING for details.
>> + */
>> +
>> +#include <linux/gpio.h>
>> +#include <linux/export.h>
>> +#include <linux/bcma/bcma.h>
>> +
>> +#include "bcma_private.h"
>> +
>> +static inline struct bcma_drv_cc *bcma_gpio_get_cc(struct gpio_chip *chip)
>> +{
>> + return container_of(chip, struct bcma_drv_cc, gpio);
>> +}
>> +
>> +static int bcma_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
>> +{
>> + struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip);
>> +
>> + return !!bcma_chipco_gpio_in(cc, 1 << gpio);
>> +}
>> +
>> +static void bcma_gpio_set_value(struct gpio_chip *chip, unsigned gpio,
>> + int value)
>> +{
>> + struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip);
>> +
>> + bcma_chipco_gpio_out(cc, 1 << gpio, value ? 1 << gpio : 0);
>
> This is a little confusing at first, because most GPIO "drivers" actually just
> pass the value directly.

The bcma_chipco_gpio API exposes the raw registers, and the conversion
of the generic GPIO API to these registers is done here. Should I change
something here?

> [snip]
>
>> +int bcma_gpio_init(struct bcma_drv_cc *cc)
>> +{
>> + struct gpio_chip *chip = &cc->gpio;
>> +
>> + chip->label = "bcma_gpio";
>> + chip->owner = THIS_MODULE;
>> + chip->request = bcma_gpio_request;
>> + chip->free = bcma_gpio_free;
>> + chip->get = bcma_gpio_get_value;
>> + chip->set = bcma_gpio_set_value;
>> + chip->direction_input = bcma_gpio_direction_input;
>> + chip->direction_output = bcma_gpio_direction_output;
>> + chip->ngpio = 16;
>> + if (cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC)
>> + chip->base = 0;
>> + else
>> + chip->base = -1;
>
> You might want to add a comment to explain why base auto-assignment is not used
> when the host type is SOC.

I added a comment.

> --
> Florian
>


2012-11-20 09:46:08

by Florian Fainelli

[permalink] [raw]
Subject: Re: [PATCH 8/8] MIPS: BCM47XX: remove GPIO driver

On Monday 19 November 2012 23:57:57 Hauke Mehrtens wrote:
> Instated of providing an own GPIO driver use the one provided by ssb and
> bcma.
>
> Signed-off-by: Hauke Mehrtens <[email protected]>

Acked-by: Florian Fainelli <[email protected]>

Thanks Hauke!
--
Florian

2012-11-20 09:44:18

by Florian Fainelli

[permalink] [raw]
Subject: Re: [PATCH 4/8] bcma: add GPIO driver

Hi Hauke,

This driver looks good to me, a couple of minor comments below.

On Monday 19 November 2012 23:57:53 Hauke Mehrtens wrote:
> Register a GPIO driver to access the GPIOs provided by the chip.
> The GPIOs of the SoC should always start at 0 and the other GPIOs could
> start at a random position. There is just one SoC in a system and when
> they start at 0 the number is predictable.
>
> Signed-off-by: Hauke Mehrtens <[email protected]>
> ---
[snip]
> +#ifdef CONFIG_BCMA_DRIVER_GPIO
> +/* driver_gpio.c */
> +int bcma_gpio_init(struct bcma_drv_cc *cc);
> +#else
> +static inline int bcma_gpio_init(struct bcma_drv_cc *cc)
> +{
> + return 0;
> +}
> +#endif /* CONFIG_BCMA_DRIVER_GPIO */

I wonder if it would not make more sense here to return -ENODEV or -ENOTSUPP
so we can identify a kernel not being built with BCMA GPIO support.

> +
> #endif
> diff --git a/drivers/bcma/driver_gpio.c b/drivers/bcma/driver_gpio.c
> new file mode 100644
> index 0000000..2b9e404
> --- /dev/null
> +++ b/drivers/bcma/driver_gpio.c
> @@ -0,0 +1,95 @@
> +/*
> + * Broadcom specific AMBA
> + * GPIO driver
> + *
> + * Copyright 2011, Broadcom Corporation
> + * Copyright 2012, Hauke Mehrtens <[email protected]>
> + *
> + * Licensed under the GNU/GPL. See COPYING for details.
> + */
> +
> +#include <linux/gpio.h>
> +#include <linux/export.h>
> +#include <linux/bcma/bcma.h>
> +
> +#include "bcma_private.h"
> +
> +static inline struct bcma_drv_cc *bcma_gpio_get_cc(struct gpio_chip *chip)
> +{
> + return container_of(chip, struct bcma_drv_cc, gpio);
> +}
> +
> +static int bcma_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
> +{
> + struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip);
> +
> + return !!bcma_chipco_gpio_in(cc, 1 << gpio);
> +}
> +
> +static void bcma_gpio_set_value(struct gpio_chip *chip, unsigned gpio,
> + int value)
> +{
> + struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip);
> +
> + bcma_chipco_gpio_out(cc, 1 << gpio, value ? 1 << gpio : 0);

This is a little confusing at first, because most GPIO "drivers" actually just
pass the value directly.

[snip]

> +int bcma_gpio_init(struct bcma_drv_cc *cc)
> +{
> + struct gpio_chip *chip = &cc->gpio;
> +
> + chip->label = "bcma_gpio";
> + chip->owner = THIS_MODULE;
> + chip->request = bcma_gpio_request;
> + chip->free = bcma_gpio_free;
> + chip->get = bcma_gpio_get_value;
> + chip->set = bcma_gpio_set_value;
> + chip->direction_input = bcma_gpio_direction_input;
> + chip->direction_output = bcma_gpio_direction_output;
> + chip->ngpio = 16;
> + if (cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC)
> + chip->base = 0;
> + else
> + chip->base = -1;

You might want to add a comment to explain why base auto-assignment is not used
when the host type is SOC.
--
Florian

2012-11-20 21:07:53

by Hauke Mehrtens

[permalink] [raw]
Subject: Re: [PATCH 7/8] ssb: add GPIO driver

On 11/20/2012 09:12 AM, John Crispin wrote:
> Hi Hauke,
>
>
>> +#ifdef CONFIG_SSB_DRIVER_EXTIF
>
> ...
>
>> +}
>> +
>> +#else
>> +static int ssb_gpio_extif_init(struct ssb_bus *bus)
>> +{
>> + return 0;
>> +}
>> +#endif
>
> ssb_gpio_extif_init() is also defined as a static inline stub in the
> header files. you should drop this definition from the code file
>
> John
No, ssb_gpio_extif_init() is only defined in drivers/ssb/driver_gpio.c
and not ssb_private.h, ssb_gpio_init() is defined in ssb_private.h.

Hauke

2012-11-19 22:58:58

by Hauke Mehrtens

[permalink] [raw]
Subject: [PATCH 6/8] ssb: add locking around gpio register accesses

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 <[email protected]>
---
drivers/ssb/driver_chipcommon.c | 75 ++++++++++++++++++++++++++---
drivers/ssb/driver_extif.c | 52 ++++++++++++++++++--
drivers/ssb/main.c | 1 +
drivers/ssb/ssb_private.h | 8 +++
include/linux/ssb/ssb_driver_chipcommon.h | 1 +
include/linux/ssb/ssb_driver_extif.h | 1 +
6 files changed, 125 insertions(+), 13 deletions(-)

diff --git a/drivers/ssb/driver_chipcommon.c b/drivers/ssb/driver_chipcommon.c
index 4df4926..3b1c5c7 100644
--- a/drivers/ssb/driver_chipcommon.c
+++ b/drivers/ssb/driver_chipcommon.c
@@ -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);
@@ -413,49 +416,105 @@ u32 ssb_chipco_irq_status(struct ssb_chipcommon *cc, u32 mask)

u32 ssb_chipco_gpio_in(struct ssb_chipcommon *cc, u32 mask)
{
- return chipco_read32(cc, SSB_CHIPCO_GPIOIN) & mask;
+ unsigned long flags;
+ u32 res = 0;
+
+ spin_lock_irqsave(&cc->gpio_lock, flags);
+ res = chipco_read32(cc, SSB_CHIPCO_GPIOIN) & mask;
+ spin_unlock_irqrestore(&cc->gpio_lock, flags);
+
+ return res;
}

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
diff --git a/drivers/ssb/driver_extif.c b/drivers/ssb/driver_extif.c
index dc47f30..c323579 100644
--- a/drivers/ssb/driver_extif.c
+++ b/drivers/ssb/driver_extif.c
@@ -118,29 +118,71 @@ 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;
+ unsigned long flags;
+ u32 res = 0;
+
+ spin_lock_irqsave(&extif->gpio_lock, flags);
+ res = extif_read32(extif, SSB_EXTIF_GPIO_IN) & mask;
+ spin_unlock_irqrestore(&extif->gpio_lock, flags);
+
+ return res;
}

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;
}
diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c
index df0f145..6fe2d10 100644
--- a/drivers/ssb/main.c
+++ b/drivers/ssb/main.c
@@ -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) {
diff --git a/drivers/ssb/ssb_private.h b/drivers/ssb/ssb_private.h
index a305550..d6a1ba9 100644
--- a/drivers/ssb/ssb_private.h
+++ b/drivers/ssb/ssb_private.h
@@ -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_ */
diff --git a/include/linux/ssb/ssb_driver_chipcommon.h b/include/linux/ssb/ssb_driver_chipcommon.h
index c8d07c9..30b6943 100644
--- a/include/linux/ssb/ssb_driver_chipcommon.h
+++ b/include/linux/ssb/ssb_driver_chipcommon.h
@@ -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;
};

diff --git a/include/linux/ssb/ssb_driver_extif.h b/include/linux/ssb/ssb_driver_extif.h
index 91161f0..bd23068 100644
--- a/include/linux/ssb/ssb_driver_extif.h
+++ b/include/linux/ssb/ssb_driver_extif.h
@@ -158,6 +158,7 @@

struct ssb_extif {
struct ssb_device *dev;
+ spinlock_t gpio_lock;
};

static inline bool ssb_extif_available(struct ssb_extif *extif)
--
1.7.10.4


2012-11-19 22:58:50

by Hauke Mehrtens

[permalink] [raw]
Subject: [PATCH 5/8] ssb: add ssb_chipco_gpio_pull{up,down}

Add functions to access the GPIO registers for pullup and pulldown.
These are needed for handling gpio registration.

Signed-off-by: Hauke Mehrtens <[email protected]>
---
drivers/ssb/driver_chipcommon.c | 16 ++++++++++++++++
include/linux/ssb/ssb_driver_chipcommon.h | 2 ++
2 files changed, 18 insertions(+)

diff --git a/drivers/ssb/driver_chipcommon.c b/drivers/ssb/driver_chipcommon.c
index e9d2ca1..4df4926 100644
--- a/drivers/ssb/driver_chipcommon.c
+++ b/drivers/ssb/driver_chipcommon.c
@@ -442,6 +442,22 @@ u32 ssb_chipco_gpio_polarity(struct ssb_chipcommon *cc, u32 mask, u32 value)
return chipco_write32_masked(cc, SSB_CHIPCO_GPIOPOL, mask, value);
}

+u32 ssb_chipco_gpio_pullup(struct ssb_chipcommon *cc, u32 mask, u32 value)
+{
+ if (cc->dev->id.revision < 20)
+ return 0xffffffff;
+
+ return chipco_write32_masked(cc, SSB_CHIPCO_GPIOPULLUP, mask, value);
+}
+
+u32 ssb_chipco_gpio_pulldown(struct ssb_chipcommon *cc, u32 mask, u32 value)
+{
+ if (cc->dev->id.revision < 20)
+ return 0xffffffff;
+
+ return chipco_write32_masked(cc, SSB_CHIPCO_GPIOPULLDOWN, mask, value);
+}
+
#ifdef CONFIG_SSB_SERIAL
int ssb_chipco_serial_init(struct ssb_chipcommon *cc,
struct ssb_serial_port *ports)
diff --git a/include/linux/ssb/ssb_driver_chipcommon.h b/include/linux/ssb/ssb_driver_chipcommon.h
index c2b02a5..c8d07c9 100644
--- a/include/linux/ssb/ssb_driver_chipcommon.h
+++ b/include/linux/ssb/ssb_driver_chipcommon.h
@@ -644,6 +644,8 @@ u32 ssb_chipco_gpio_outen(struct ssb_chipcommon *cc, u32 mask, u32 value);
u32 ssb_chipco_gpio_control(struct ssb_chipcommon *cc, u32 mask, u32 value);
u32 ssb_chipco_gpio_intmask(struct ssb_chipcommon *cc, u32 mask, u32 value);
u32 ssb_chipco_gpio_polarity(struct ssb_chipcommon *cc, u32 mask, u32 value);
+u32 ssb_chipco_gpio_pullup(struct ssb_chipcommon *cc, u32 mask, u32 value);
+u32 ssb_chipco_gpio_pulldown(struct ssb_chipcommon *cc, u32 mask, u32 value);

#ifdef CONFIG_SSB_SERIAL
extern int ssb_chipco_serial_init(struct ssb_chipcommon *cc,
--
1.7.10.4


2012-11-19 23:19:01

by Hauke Mehrtens

[permalink] [raw]
Subject: [PATCH v2 7/8] ssb: add GPIO driver

Register a GPIO driver to access the GPIOs provided by the chip.
The GPIOs of the SoC should always start at 0 and the other GPIOs could
start at a random position. There is just one SoC in a system and when
they start at 0 the number is predictable.

Signed-off-by: Hauke Mehrtens <[email protected]>
---

v2: fix compile problem in drivers/ssb/ssb_private.h

drivers/ssb/Kconfig | 9 +++
drivers/ssb/Makefile | 1 +
drivers/ssb/driver_gpio.c | 170 +++++++++++++++++++++++++++++++++++++++++++++
drivers/ssb/main.c | 1 +
drivers/ssb/ssb_private.h | 9 +++
include/linux/ssb/ssb.h | 4 ++
6 files changed, 194 insertions(+)
create mode 100644 drivers/ssb/driver_gpio.c

diff --git a/drivers/ssb/Kconfig b/drivers/ssb/Kconfig
index 42cdaa9..8b5460f 100644
--- a/drivers/ssb/Kconfig
+++ b/drivers/ssb/Kconfig
@@ -160,4 +160,13 @@ config SSB_DRIVER_GIGE

If unsure, say N

+config SSB_DRIVER_GPIO
+ bool
+ depends on SSB
+ select GPIOLIB
+ help
+ Driver to provide access to the GPIO pins.
+
+ If unsure, say N
+
endmenu
diff --git a/drivers/ssb/Makefile b/drivers/ssb/Makefile
index 656e58b..9159ba7 100644
--- a/drivers/ssb/Makefile
+++ b/drivers/ssb/Makefile
@@ -15,6 +15,7 @@ ssb-$(CONFIG_SSB_DRIVER_MIPS) += driver_mipscore.o
ssb-$(CONFIG_SSB_DRIVER_EXTIF) += driver_extif.o
ssb-$(CONFIG_SSB_DRIVER_PCICORE) += driver_pcicore.o
ssb-$(CONFIG_SSB_DRIVER_GIGE) += driver_gige.o
+ssb-$(CONFIG_SSB_DRIVER_GPIO) += driver_gpio.o

# b43 pci-ssb-bridge driver
# Not strictly a part of SSB, but kept here for convenience
diff --git a/drivers/ssb/driver_gpio.c b/drivers/ssb/driver_gpio.c
new file mode 100644
index 0000000..e4ab9f6
--- /dev/null
+++ b/drivers/ssb/driver_gpio.c
@@ -0,0 +1,170 @@
+/*
+ * Sonics Silicon Backplane
+ * GPIO driver
+ *
+ * Copyright 2011, Broadcom Corporation
+ * Copyright 2012, Hauke Mehrtens <[email protected]>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include <linux/gpio.h>
+#include <linux/export.h>
+#include <linux/ssb/ssb.h>
+
+#include "ssb_private.h"
+
+static struct ssb_bus *ssb_gpio_get_bus(struct gpio_chip *chip)
+{
+ return container_of(chip, struct ssb_bus, gpio);
+}
+
+static int ssb_gpio_chipco_get_value(struct gpio_chip *chip, unsigned gpio)
+{
+ struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+ return !!ssb_chipco_gpio_in(&bus->chipco, 1 << gpio);
+}
+
+static void ssb_gpio_chipco_set_value(struct gpio_chip *chip, unsigned gpio,
+ int value)
+{
+ struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+ ssb_chipco_gpio_out(&bus->chipco, 1 << gpio, value ? 1 << gpio : 0);
+}
+
+static int ssb_gpio_chipco_direction_input(struct gpio_chip *chip,
+ unsigned gpio)
+{
+ struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+ ssb_chipco_gpio_outen(&bus->chipco, 1 << gpio, 0);
+ return 0;
+}
+
+static int ssb_gpio_chipco_direction_output(struct gpio_chip *chip,
+ unsigned gpio, int value)
+{
+ struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+ ssb_chipco_gpio_outen(&bus->chipco, 1 << gpio, 1 << gpio);
+ ssb_chipco_gpio_out(&bus->chipco, 1 << gpio, value ? 1 << gpio : 0);
+ return 0;
+}
+
+static int ssb_gpio_chipco_request(struct gpio_chip *chip, unsigned gpio)
+{
+ struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+ ssb_chipco_gpio_control(&bus->chipco, 1 << gpio, 0);
+ /* clear pulldown */
+ ssb_chipco_gpio_pulldown(&bus->chipco, 1 << gpio, 0);
+ /* Set pullup */
+ ssb_chipco_gpio_pullup(&bus->chipco, 1 << gpio, 1 << gpio);
+
+ return 0;
+}
+
+static void ssb_gpio_chipco_free(struct gpio_chip *chip, unsigned gpio)
+{
+ struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+ /* clear pullup */
+ ssb_chipco_gpio_pullup(&bus->chipco, 1 << gpio, 0);
+}
+
+static int ssb_gpio_chipco_init(struct ssb_bus *bus)
+{
+ struct gpio_chip *chip = &bus->gpio;
+
+ chip->label = "ssb_chipco_gpio";
+ chip->owner = THIS_MODULE;
+ chip->request = ssb_gpio_chipco_request;
+ chip->free = ssb_gpio_chipco_free;
+ chip->get = ssb_gpio_chipco_get_value;
+ chip->set = ssb_gpio_chipco_set_value;
+ chip->direction_input = ssb_gpio_chipco_direction_input;
+ chip->direction_output = ssb_gpio_chipco_direction_output;
+ chip->ngpio = 16;
+ if (bus->bustype == SSB_BUSTYPE_SSB)
+ chip->base = 0;
+ else
+ chip->base = -1;
+
+ return gpiochip_add(chip);
+}
+
+#ifdef CONFIG_SSB_DRIVER_EXTIF
+
+static int ssb_gpio_extif_get_value(struct gpio_chip *chip, unsigned gpio)
+{
+ struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+ return !!ssb_extif_gpio_in(&bus->extif, 1 << gpio);
+}
+
+static void ssb_gpio_extif_set_value(struct gpio_chip *chip, unsigned gpio,
+ int value)
+{
+ struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+ ssb_extif_gpio_out(&bus->extif, 1 << gpio, value ? 1 << gpio : 0);
+}
+
+static int ssb_gpio_extif_direction_input(struct gpio_chip *chip,
+ unsigned gpio)
+{
+ struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+ ssb_extif_gpio_outen(&bus->extif, 1 << gpio, 0);
+ return 0;
+}
+
+static int ssb_gpio_extif_direction_output(struct gpio_chip *chip,
+ unsigned gpio, int value)
+{
+ struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+ ssb_extif_gpio_outen(&bus->extif, 1 << gpio, 1 << gpio);
+ ssb_extif_gpio_out(&bus->extif, 1 << gpio, value ? 1 << gpio : 0);
+ return 0;
+}
+
+static int ssb_gpio_extif_init(struct ssb_bus *bus)
+{
+ struct gpio_chip *chip = &bus->gpio;
+
+ chip->label = "ssb_extif_gpio";
+ chip->owner = THIS_MODULE;
+ chip->get = ssb_gpio_extif_get_value;
+ chip->set = ssb_gpio_extif_set_value;
+ chip->direction_input = ssb_gpio_extif_direction_input;
+ chip->direction_output = ssb_gpio_extif_direction_output;
+ chip->ngpio = 5;
+ if (bus->bustype == SSB_BUSTYPE_SSB)
+ chip->base = 0;
+ else
+ chip->base = -1;
+
+ return gpiochip_add(chip);
+}
+
+#else
+static int ssb_gpio_extif_init(struct ssb_bus *bus)
+{
+ return 0;
+}
+#endif
+
+int ssb_gpio_init(struct ssb_bus *bus)
+{
+ if (ssb_chipco_available(&bus->chipco))
+ return ssb_gpio_chipco_init(bus);
+ else if (ssb_extif_available(&bus->extif))
+ return ssb_gpio_extif_init(bus);
+ else
+ SSB_WARN_ON(1);
+
+ return -1;
+}
diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c
index 6fe2d10..6ac1ca3 100644
--- a/drivers/ssb/main.c
+++ b/drivers/ssb/main.c
@@ -798,6 +798,7 @@ static int __devinit ssb_bus_register(struct ssb_bus *bus,
ssb_chipcommon_init(&bus->chipco);
ssb_extif_init(&bus->extif);
ssb_mipscore_init(&bus->mipscore);
+ ssb_gpio_init(bus);
err = ssb_fetch_invariants(bus, get_invariants);
if (err) {
ssb_bus_may_powerdown(bus);
diff --git a/drivers/ssb/ssb_private.h b/drivers/ssb/ssb_private.h
index d6a1ba9..f86eeae 100644
--- a/drivers/ssb/ssb_private.h
+++ b/drivers/ssb/ssb_private.h
@@ -219,4 +219,13 @@ static inline void ssb_extif_init(struct ssb_extif *extif)
}
#endif

+#ifdef CONFIG_SSB_DRIVER_GPIO
+extern int ssb_gpio_init(struct ssb_bus *bus);
+#else /* CONFIG_SSB_DRIVER_GPIO */
+static inline int ssb_gpio_init(struct ssb_bus *bus)
+{
+ return 0;
+}
+#endif /* CONFIG_SSB_DRIVER_GPIO */
+
#endif /* LINUX_SSB_PRIVATE_H_ */
diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h
index bb674c0..3862a5b 100644
--- a/include/linux/ssb/ssb.h
+++ b/include/linux/ssb/ssb.h
@@ -6,6 +6,7 @@
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/pci.h>
+#include <linux/gpio.h>
#include <linux/mod_devicetable.h>
#include <linux/dma-mapping.h>

@@ -433,6 +434,9 @@ struct ssb_bus {
/* Lock for GPIO register access. */
spinlock_t gpio_lock;
#endif /* EMBEDDED */
+#ifdef CONFIG_SSB_DRIVER_GPIO
+ struct gpio_chip gpio;
+#endif /* DRIVER_GPIO */

/* Internal-only stuff follows. Do not touch. */
struct list_head list;
--
1.7.10.4


2012-11-20 08:13:40

by John Crispin

[permalink] [raw]
Subject: Re: [PATCH 7/8] ssb: add GPIO driver

Hi Hauke,


> +#ifdef CONFIG_SSB_DRIVER_EXTIF

...

> +}
> +
> +#else
> +static int ssb_gpio_extif_init(struct ssb_bus *bus)
> +{
> + return 0;
> +}
> +#endif

ssb_gpio_extif_init() is also defined as a static inline stub in the
header files. you should drop this definition from the code file

John