* [PATCH 0/4] gpio: aspeed: Fixes and support for sharing with co-processor
@ 2018-06-12 0:10 Benjamin Herrenschmidt
2018-06-12 0:10 ` [PATCH 1/4] gpio: aspeed: Rework register type accessors Benjamin Herrenschmidt
` (4 more replies)
0 siblings, 5 replies; 18+ messages in thread
From: Benjamin Herrenschmidt @ 2018-06-12 0:10 UTC (permalink / raw)
To: linux-aspeed
This series cleans up the register accessors a bit, adds missing ones,
fixes the access to the write latch and finally adds an interface
that a co-processor driver can use to change the owner of some of
the GPIO lines and arbitrate access to shared GPIO banks.
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 1/4] gpio: aspeed: Rework register type accessors
2018-06-12 0:10 [PATCH 0/4] gpio: aspeed: Fixes and support for sharing with co-processor Benjamin Herrenschmidt
@ 2018-06-12 0:10 ` Benjamin Herrenschmidt
2018-06-12 4:49 ` Benjamin Herrenschmidt
2018-06-12 0:10 ` [PATCH 2/4] gpio: aspeed: Add "Read Data" register to read the write latch Benjamin Herrenschmidt
` (3 subsequent siblings)
4 siblings, 1 reply; 18+ messages in thread
From: Benjamin Herrenschmidt @ 2018-06-12 0:10 UTC (permalink / raw)
To: linux-aspeed
Use a single accessor function for all register types instead
of several spread around. This will make it easier/cleaner
to introduce new registers and keep the mechanism in one
place.
The big switch/case is optimized at compile time since the
switch value is a constant.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
drivers/gpio/gpio-aspeed.c | 118 ++++++++++++++++++++++---------------
1 file changed, 69 insertions(+), 49 deletions(-)
diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
index a62cbf3ed4a8..42aadd52f77d 100644
--- a/drivers/gpio/gpio-aspeed.c
+++ b/drivers/gpio/gpio-aspeed.c
@@ -127,12 +127,21 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
},
};
-#define GPIO_BANK(x) ((x) >> 5)
-#define GPIO_OFFSET(x) ((x) & 0x1f)
-#define GPIO_BIT(x) BIT(GPIO_OFFSET(x))
+enum aspeed_gpio_reg {
+ reg_val,
+ reg_dir,
+ reg_irq_enable,
+ reg_irq_type0,
+ reg_irq_type1,
+ reg_irq_type2,
+ reg_irq_status,
+ reg_debounce_sel1,
+ reg_debounce_sel2,
+ reg_tolerance,
+};
-#define GPIO_DATA 0x00
-#define GPIO_DIR 0x04
+#define GPIO_VAL_VALUE 0x00
+#define GPIO_VAL_DIR 0x04
#define GPIO_IRQ_ENABLE 0x00
#define GPIO_IRQ_TYPE0 0x04
@@ -143,6 +152,40 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
#define GPIO_DEBOUNCE_SEL1 0x00
#define GPIO_DEBOUNCE_SEL2 0x04
+/* This will be resolved at compile time */
+static inline void __iomem *bank_reg(struct aspeed_gpio *gpio,
+ const struct aspeed_gpio_bank *bank,
+ const enum aspeed_gpio_reg reg)
+{
+ switch (reg) {
+ case reg_val:
+ return gpio->base + bank->val_regs + GPIO_VAL_VALUE;
+ case reg_dir:
+ return gpio->base + bank->val_regs + GPIO_VAL_DIR;
+ case reg_irq_enable:
+ return gpio->base + bank->irq_regs + GPIO_IRQ_ENABLE;
+ case reg_irq_type0:
+ return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE0;
+ case reg_irq_type1:
+ return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE1;
+ case reg_irq_type2:
+ return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE2;
+ case reg_irq_status:
+ return gpio->base + bank->irq_regs + GPIO_IRQ_STATUS;
+ case reg_debounce_sel1:
+ return gpio->base + bank->debounce_regs + GPIO_DEBOUNCE_SEL1;
+ case reg_debounce_sel2:
+ return gpio->base + bank->debounce_regs + GPIO_DEBOUNCE_SEL2;
+ case reg_tolerance:
+ return gpio->base + bank->tolerance_regs;
+ }
+ BUG_ON(1);
+}
+
+#define GPIO_BANK(x) ((x) >> 5)
+#define GPIO_OFFSET(x) ((x) & 0x1f)
+#define GPIO_BIT(x) BIT(GPIO_OFFSET(x))
+
#define _GPIO_SET_DEBOUNCE(t, o, i) ((!!((t) & BIT(i))) << GPIO_OFFSET(o))
#define GPIO_SET_DEBOUNCE1(t, o) _GPIO_SET_DEBOUNCE(t, o, 1)
#define GPIO_SET_DEBOUNCE2(t, o) _GPIO_SET_DEBOUNCE(t, o, 0)
@@ -201,27 +244,12 @@ static inline bool have_output(struct aspeed_gpio *gpio, unsigned int offset)
return !props || (props->output & GPIO_BIT(offset));
}
-static void __iomem *bank_val_reg(struct aspeed_gpio *gpio,
- const struct aspeed_gpio_bank *bank,
- unsigned int reg)
-{
- return gpio->base + bank->val_regs + reg;
-}
-
-static void __iomem *bank_irq_reg(struct aspeed_gpio *gpio,
- const struct aspeed_gpio_bank *bank,
- unsigned int reg)
-{
- return gpio->base + bank->irq_regs + reg;
-}
-
static int aspeed_gpio_get(struct gpio_chip *gc, unsigned int offset)
{
struct aspeed_gpio *gpio = gpiochip_get_data(gc);
const struct aspeed_gpio_bank *bank = to_bank(offset);
- return !!(ioread32(bank_val_reg(gpio, bank, GPIO_DATA))
- & GPIO_BIT(offset));
+ return !!(ioread32(bank_reg(gpio, bank, reg_val)) & GPIO_BIT(offset));
}
static void __aspeed_gpio_set(struct gpio_chip *gc, unsigned int offset,
@@ -232,7 +260,7 @@ static void __aspeed_gpio_set(struct gpio_chip *gc, unsigned int offset,
void __iomem *addr;
u32 reg;
- addr = bank_val_reg(gpio, bank, GPIO_DATA);
+ addr = bank_reg(gpio, bank, reg_val);
reg = gpio->dcache[GPIO_BANK(offset)];
if (val)
@@ -269,8 +297,8 @@ static int aspeed_gpio_dir_in(struct gpio_chip *gc, unsigned int offset)
spin_lock_irqsave(&gpio->lock, flags);
- reg = ioread32(bank_val_reg(gpio, bank, GPIO_DIR));
- iowrite32(reg & ~GPIO_BIT(offset), bank_val_reg(gpio, bank, GPIO_DIR));
+ reg = ioread32(bank_reg(gpio, bank, reg_dir));
+ iowrite32(reg & ~GPIO_BIT(offset), bank_reg(gpio, bank, reg_dir));
spin_unlock_irqrestore(&gpio->lock, flags);
@@ -291,8 +319,8 @@ static int aspeed_gpio_dir_out(struct gpio_chip *gc,
spin_lock_irqsave(&gpio->lock, flags);
__aspeed_gpio_set(gc, offset, val);
- reg = ioread32(bank_val_reg(gpio, bank, GPIO_DIR));
- iowrite32(reg | GPIO_BIT(offset), bank_val_reg(gpio, bank, GPIO_DIR));
+ reg = ioread32(bank_reg(gpio, bank, reg_dir));
+ iowrite32(reg | GPIO_BIT(offset), bank_reg(gpio, bank, reg_dir));
spin_unlock_irqrestore(&gpio->lock, flags);
@@ -314,7 +342,7 @@ static int aspeed_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
spin_lock_irqsave(&gpio->lock, flags);
- val = ioread32(bank_val_reg(gpio, bank, GPIO_DIR)) & GPIO_BIT(offset);
+ val = ioread32(bank_reg(gpio, bank, reg_dir)) & GPIO_BIT(offset);
spin_unlock_irqrestore(&gpio->lock, flags);
@@ -358,7 +386,7 @@ static void aspeed_gpio_irq_ack(struct irq_data *d)
if (rc)
return;
- status_addr = bank_irq_reg(gpio, bank, GPIO_IRQ_STATUS);
+ status_addr = bank_reg(gpio, bank, reg_irq_status);
spin_lock_irqsave(&gpio->lock, flags);
iowrite32(bit, status_addr);
@@ -378,7 +406,7 @@ static void aspeed_gpio_irq_set_mask(struct irq_data *d, bool set)
if (rc)
return;
- addr = bank_irq_reg(gpio, bank, GPIO_IRQ_ENABLE);
+ addr = bank_reg(gpio, bank, reg_irq_enable);
spin_lock_irqsave(&gpio->lock, flags);
@@ -439,17 +467,17 @@ static int aspeed_gpio_set_type(struct irq_data *d, unsigned int type)
spin_lock_irqsave(&gpio->lock, flags);
- addr = bank_irq_reg(gpio, bank, GPIO_IRQ_TYPE0);
+ addr = bank_reg(gpio, bank, reg_irq_type0);
reg = ioread32(addr);
reg = (reg & ~bit) | type0;
iowrite32(reg, addr);
- addr = bank_irq_reg(gpio, bank, GPIO_IRQ_TYPE1);
+ addr = bank_reg(gpio, bank, reg_irq_type1);
reg = ioread32(addr);
reg = (reg & ~bit) | type1;
iowrite32(reg, addr);
- addr = bank_irq_reg(gpio, bank, GPIO_IRQ_TYPE2);
+ addr = bank_reg(gpio, bank, reg_irq_type2);
reg = ioread32(addr);
reg = (reg & ~bit) | type2;
iowrite32(reg, addr);
@@ -474,7 +502,7 @@ static void aspeed_gpio_irq_handler(struct irq_desc *desc)
for (i = 0; i < ARRAY_SIZE(aspeed_gpio_banks); i++) {
const struct aspeed_gpio_bank *bank = &aspeed_gpio_banks[i];
- reg = ioread32(bank_irq_reg(data, bank, GPIO_IRQ_STATUS));
+ reg = ioread32(bank_reg(data, bank, reg_irq_status));
for_each_set_bit(p, ®, 32) {
girq = irq_find_mapping(gc->irqdomain, i * 32 + p);
@@ -546,21 +574,21 @@ static int aspeed_gpio_reset_tolerance(struct gpio_chip *chip,
unsigned int offset, bool enable)
{
struct aspeed_gpio *gpio = gpiochip_get_data(chip);
- const struct aspeed_gpio_bank *bank;
unsigned long flags;
+ void __iomem *treg;
u32 val;
- bank = to_bank(offset);
+ treg = bank_reg(gpio, to_bank(offset), reg_tolerance);
spin_lock_irqsave(&gpio->lock, flags);
- val = readl(gpio->base + bank->tolerance_regs);
+ val = readl(treg);
if (enable)
val |= GPIO_BIT(offset);
else
val &= ~GPIO_BIT(offset);
- writel(val, gpio->base + bank->tolerance_regs);
+ writel(val, treg);
spin_unlock_irqrestore(&gpio->lock, flags);
return 0;
@@ -579,13 +607,6 @@ static void aspeed_gpio_free(struct gpio_chip *chip, unsigned int offset)
pinctrl_free_gpio(chip->base + offset);
}
-static inline void __iomem *bank_debounce_reg(struct aspeed_gpio *gpio,
- const struct aspeed_gpio_bank *bank,
- unsigned int reg)
-{
- return gpio->base + bank->debounce_regs + reg;
-}
-
static int usecs_to_cycles(struct aspeed_gpio *gpio, unsigned long usecs,
u32 *cycles)
{
@@ -663,11 +684,11 @@ static void configure_timer(struct aspeed_gpio *gpio, unsigned int offset,
void __iomem *addr;
u32 val;
- addr = bank_debounce_reg(gpio, bank, GPIO_DEBOUNCE_SEL1);
+ addr = bank_reg(gpio, bank, reg_debounce_sel1);
val = ioread32(addr);
iowrite32((val & ~mask) | GPIO_SET_DEBOUNCE1(timer, offset), addr);
- addr = bank_debounce_reg(gpio, bank, GPIO_DEBOUNCE_SEL2);
+ addr = bank_reg(gpio, bank, reg_debounce_sel2);
val = ioread32(addr);
iowrite32((val & ~mask) | GPIO_SET_DEBOUNCE2(timer, offset), addr);
}
@@ -901,9 +922,8 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
/* Populate it with initial values read from the HW */
for (i = 0; i < banks; i++) {
- const struct aspeed_gpio_bank *bank = &aspeed_gpio_banks[i];
- gpio->dcache[i] = ioread32(gpio->base + bank->val_regs +
- GPIO_DATA);
+ void __iomem *addr = bank_reg(gpio, &aspeed_gpio_banks[i], reg_val);
+ gpio->dcache[i] = ioread32(addr);
}
rc = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio);
--
2.17.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH 2/4] gpio: aspeed: Add "Read Data" register to read the write latch
2018-06-12 0:10 [PATCH 0/4] gpio: aspeed: Fixes and support for sharing with co-processor Benjamin Herrenschmidt
2018-06-12 0:10 ` [PATCH 1/4] gpio: aspeed: Rework register type accessors Benjamin Herrenschmidt
@ 2018-06-12 0:10 ` Benjamin Herrenschmidt
2018-06-12 5:38 ` Joel Stanley
2018-06-12 0:10 ` [PATCH 3/4] gpio: aspeed: Add command source registers Benjamin Herrenschmidt
` (2 subsequent siblings)
4 siblings, 1 reply; 18+ messages in thread
From: Benjamin Herrenschmidt @ 2018-06-12 0:10 UTC (permalink / raw)
To: linux-aspeed
The Aspeed GPIO hardware has a quirk: the value register, for an
output GPIO, doesn't contain the last value written (the write
latch content) but the sampled input value.
This means that when reading back shortly after writing, you can
get an incorrect value as the input value is delayed by a few
synchronizers.
The HW supports a separate read-only register "Data Read Register"
which allows you to read the write latch instead.
This adds the definition for it, and uses it for the initial
population of the GPIO value cache. It will be used more in
subsequent patches.
can be
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
drivers/gpio/gpio-aspeed.c | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
index 42aadd52f77d..210c3fcc7a40 100644
--- a/drivers/gpio/gpio-aspeed.c
+++ b/drivers/gpio/gpio-aspeed.c
@@ -59,7 +59,10 @@ struct aspeed_gpio {
};
struct aspeed_gpio_bank {
- uint16_t val_regs;
+ uint16_t val_regs; /* 0: Wr: set write latch, Rd: read input value (*)
+ * 4: Direction (0=in, 1=out)
+ */
+ uint16_t rdata_reg; /* Wr: <none> Rd: read write latch */
uint16_t irq_regs;
uint16_t debounce_regs;
uint16_t tolerance_regs;
@@ -71,6 +74,7 @@ static const int debounce_timers[4] = { 0x00, 0x50, 0x54, 0x58 };
static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
{
.val_regs = 0x0000,
+ .rdata_reg = 0x00c0,
.irq_regs = 0x0008,
.debounce_regs = 0x0040,
.tolerance_regs = 0x001c,
@@ -78,6 +82,7 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
},
{
.val_regs = 0x0020,
+ .rdata_reg = 0x00c4,
.irq_regs = 0x0028,
.debounce_regs = 0x0048,
.tolerance_regs = 0x003c,
@@ -85,6 +90,7 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
},
{
.val_regs = 0x0070,
+ .rdata_reg = 0x00c8,
.irq_regs = 0x0098,
.debounce_regs = 0x00b0,
.tolerance_regs = 0x00ac,
@@ -92,6 +98,7 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
},
{
.val_regs = 0x0078,
+ .rdata_reg = 0x00d0,
.irq_regs = 0x00e8,
.debounce_regs = 0x0100,
.tolerance_regs = 0x00fc,
@@ -106,6 +113,7 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
},
{
.val_regs = 0x0088,
+ .rdata_reg = 0x00d4,
.irq_regs = 0x0148,
.debounce_regs = 0x0160,
.tolerance_regs = 0x015c,
@@ -113,6 +121,7 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
},
{
.val_regs = 0x01E0,
+ .rdata_reg = 0x00d4,
.irq_regs = 0x0178,
.debounce_regs = 0x0190,
.tolerance_regs = 0x018c,
@@ -120,6 +129,7 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
},
{
.val_regs = 0x01e8,
+ .rdata_reg = 0x00d8,
.irq_regs = 0x01a8,
.debounce_regs = 0x01c0,
.tolerance_regs = 0x01bc,
@@ -129,6 +139,7 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
enum aspeed_gpio_reg {
reg_val,
+ reg_rdata,
reg_dir,
reg_irq_enable,
reg_irq_type0,
@@ -160,6 +171,8 @@ static inline void __iomem *bank_reg(struct aspeed_gpio *gpio,
switch (reg) {
case reg_val:
return gpio->base + bank->val_regs + GPIO_VAL_VALUE;
+ case reg_rdata:
+ return gpio->base + bank->rdata_reg;
case reg_dir:
return gpio->base + bank->val_regs + GPIO_VAL_DIR;
case reg_irq_enable:
@@ -922,7 +935,7 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
/* Populate it with initial values read from the HW */
for (i = 0; i < banks; i++) {
- void __iomem *addr = bank_reg(gpio, &aspeed_gpio_banks[i], reg_val);
+ void __iomem *addr = bank_reg(gpio, &aspeed_gpio_banks[i], reg_rdata);
gpio->dcache[i] = ioread32(addr);
}
--
2.17.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH 3/4] gpio: aspeed: Add command source registers
2018-06-12 0:10 [PATCH 0/4] gpio: aspeed: Fixes and support for sharing with co-processor Benjamin Herrenschmidt
2018-06-12 0:10 ` [PATCH 1/4] gpio: aspeed: Rework register type accessors Benjamin Herrenschmidt
2018-06-12 0:10 ` [PATCH 2/4] gpio: aspeed: Add "Read Data" register to read the write latch Benjamin Herrenschmidt
@ 2018-06-12 0:10 ` Benjamin Herrenschmidt
2018-06-12 5:38 ` Joel Stanley
2018-06-12 0:10 ` [PATCH 4/4] gpio: aspeed: Add interfaces for co-processor to grab GPIOs Benjamin Herrenschmidt
2018-06-14 9:01 ` [PATCH 0/4] gpio: aspeed: Fixes and support for sharing with co-processor Linus Walleij
4 siblings, 1 reply; 18+ messages in thread
From: Benjamin Herrenschmidt @ 2018-06-12 0:10 UTC (permalink / raw)
To: linux-aspeed
This adds the definitions for the command source registers
and a helper to set them.
Those registers allow to control which bus master on the
SoC is allowed to modify a given bank of GPIOs and will
be used by subsequent patches.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
drivers/gpio/gpio-aspeed.c | 55 +++++++++++++++++++++++++++++++++++---
1 file changed, 52 insertions(+), 3 deletions(-)
diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
index 210c3fcc7a40..06510634ddd6 100644
--- a/drivers/gpio/gpio-aspeed.c
+++ b/drivers/gpio/gpio-aspeed.c
@@ -66,6 +66,7 @@ struct aspeed_gpio_bank {
uint16_t irq_regs;
uint16_t debounce_regs;
uint16_t tolerance_regs;
+ uint16_t cmdsrc_regs;
const char names[4][3];
};
@@ -78,6 +79,7 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
.irq_regs = 0x0008,
.debounce_regs = 0x0040,
.tolerance_regs = 0x001c,
+ .cmdsrc_regs = 0x0060,
.names = { "A", "B", "C", "D" },
},
{
@@ -86,6 +88,7 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
.irq_regs = 0x0028,
.debounce_regs = 0x0048,
.tolerance_regs = 0x003c,
+ .cmdsrc_regs = 0x0068,
.names = { "E", "F", "G", "H" },
},
{
@@ -94,21 +97,25 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
.irq_regs = 0x0098,
.debounce_regs = 0x00b0,
.tolerance_regs = 0x00ac,
+ .cmdsrc_regs = 0x0090,
.names = { "I", "J", "K", "L" },
},
{
.val_regs = 0x0078,
- .rdata_reg = 0x00d0,
+ .rdata_reg = 0x00cc,
.irq_regs = 0x00e8,
.debounce_regs = 0x0100,
.tolerance_regs = 0x00fc,
+ .cmdsrc_regs = 0x00e0,
.names = { "M", "N", "O", "P" },
},
{
.val_regs = 0x0080,
+ .rdata_reg = 0x00d0,
.irq_regs = 0x0118,
.debounce_regs = 0x0130,
.tolerance_regs = 0x012c,
+ .cmdsrc_regs = 0x0110,
.names = { "Q", "R", "S", "T" },
},
{
@@ -117,22 +124,25 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
.irq_regs = 0x0148,
.debounce_regs = 0x0160,
.tolerance_regs = 0x015c,
+ .cmdsrc_regs = 0x0140,
.names = { "U", "V", "W", "X" },
},
{
.val_regs = 0x01E0,
- .rdata_reg = 0x00d4,
+ .rdata_reg = 0x00d8,
.irq_regs = 0x0178,
.debounce_regs = 0x0190,
.tolerance_regs = 0x018c,
+ .cmdsrc_regs = 0x0170,
.names = { "Y", "Z", "AA", "AB" },
},
{
.val_regs = 0x01e8,
- .rdata_reg = 0x00d8,
+ .rdata_reg = 0x00dc,
.irq_regs = 0x01a8,
.debounce_regs = 0x01c0,
.tolerance_regs = 0x01bc,
+ .cmdsrc_regs = 0x01a0,
.names = { "AC", "", "", "" },
},
};
@@ -149,6 +159,8 @@ enum aspeed_gpio_reg {
reg_debounce_sel1,
reg_debounce_sel2,
reg_tolerance,
+ reg_cmdsrc0,
+ reg_cmdsrc1,
};
#define GPIO_VAL_VALUE 0x00
@@ -163,6 +175,12 @@ enum aspeed_gpio_reg {
#define GPIO_DEBOUNCE_SEL1 0x00
#define GPIO_DEBOUNCE_SEL2 0x04
+#define GPIO_CMDSRC_0 0x00
+#define GPIO_CMDSRC_1 0x04
+#define GPIO_CMDSRC_ARM 0
+#define GPIO_CMDSRC_LPC 1
+#define GPIO_CMDSRC_COLDFIRE 2
+
/* This will be resolved at compile time */
static inline void __iomem *bank_reg(struct aspeed_gpio *gpio,
const struct aspeed_gpio_bank *bank,
@@ -191,6 +209,10 @@ static inline void __iomem *bank_reg(struct aspeed_gpio *gpio,
return gpio->base + bank->debounce_regs + GPIO_DEBOUNCE_SEL2;
case reg_tolerance:
return gpio->base + bank->tolerance_regs;
+ case reg_cmdsrc0:
+ return gpio->base + bank->cmdsrc_regs + GPIO_CMDSRC_0;
+ case reg_cmdsrc1:
+ return gpio->base + bank->cmdsrc_regs + GPIO_CMDSRC_1;
}
BUG_ON(1);
}
@@ -257,6 +279,33 @@ static inline bool have_output(struct aspeed_gpio *gpio, unsigned int offset)
return !props || (props->output & GPIO_BIT(offset));
}
+static void aspeed_gpio_change_cmd_source(struct aspeed_gpio *gpio,
+ const struct aspeed_gpio_bank *bank,
+ int bindex, int cmdsrc)
+{
+ void __iomem *c0 = bank_reg(gpio, bank, reg_cmdsrc0);
+ void __iomem *c1 = bank_reg(gpio, bank, reg_cmdsrc1);
+ u32 bit, reg;
+
+ bit = 1u << ((bindex & 3) << 3);
+
+ /* Source 1 first to avoid illegal 11 combination */
+ reg = ioread32(c1);
+ if (cmdsrc & 2)
+ reg |= bit;
+ else
+ reg &= ~bit;
+ iowrite32(reg, c1);
+
+ /* Then Source 0 */
+ reg = ioread32(c0);
+ if (cmdsrc & 1)
+ reg |= bit;
+ else
+ reg &= ~bit;
+ iowrite32(reg, c0);
+}
+
static int aspeed_gpio_get(struct gpio_chip *gc, unsigned int offset)
{
struct aspeed_gpio *gpio = gpiochip_get_data(gc);
--
2.17.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH 4/4] gpio: aspeed: Add interfaces for co-processor to grab GPIOs
2018-06-12 0:10 [PATCH 0/4] gpio: aspeed: Fixes and support for sharing with co-processor Benjamin Herrenschmidt
` (2 preceding siblings ...)
2018-06-12 0:10 ` [PATCH 3/4] gpio: aspeed: Add command source registers Benjamin Herrenschmidt
@ 2018-06-12 0:10 ` Benjamin Herrenschmidt
2018-06-12 5:37 ` Joel Stanley
2018-06-14 8:59 ` Linus Walleij
2018-06-14 9:01 ` [PATCH 0/4] gpio: aspeed: Fixes and support for sharing with co-processor Linus Walleij
4 siblings, 2 replies; 18+ messages in thread
From: Benjamin Herrenschmidt @ 2018-06-12 0:10 UTC (permalink / raw)
To: linux-aspeed
On the Aspeed chip, the GPIOs can be under control of the ARM
chip or of the ColdFire coprocessor. (There's a third command
source, the LPC bus, which we don't use or support yet).
The control of which master is allowed to modify a given
GPIO is per-bank (8 GPIOs).
Unfortunately, systems already exist for which we want to
use GPIOs of both sources in the same bank.
This provides an API exported by the gpio-aspeed driver
that an aspeed coprocessor driver can use to "grab" some
GPIOs for use by the coprocessor, and allow the coprocessor
driver to provide callbacks for arbitrating access.
Once at least one GPIO of a given bank has been "grabbed"
by the coprocessor, the entire bank is marked as being
under coprocessor control. It's command source is switched
to the coprocessor.
If the ARM then tries to write to a GPIO in such a marked bank,
the provided callbacks are used to request access from the
coprocessor driver, which is responsible to doing whatever
is necessary to "pause" the coprocessor or prevent it from
trying to use the GPIOs while the ARM is doing its accesses.
During that time, the command source for the bank is temporarily
switched back to the ARM.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
drivers/gpio/gpio-aspeed.c | 235 +++++++++++++++++++++++++++++++++---
include/linux/gpio/aspeed.h | 14 +++
2 files changed, 229 insertions(+), 20 deletions(-)
create mode 100644 include/linux/gpio/aspeed.h
diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
index 06510634ddd6..323b885dd019 100644
--- a/drivers/gpio/gpio-aspeed.c
+++ b/drivers/gpio/gpio-aspeed.c
@@ -12,6 +12,8 @@
#include <asm/div64.h>
#include <linux/clk.h>
#include <linux/gpio/driver.h>
+#include <linux/gpio/aspeed.h>
+#include <linux/gpio/consumer.h>
#include <linux/hashtable.h>
#include <linux/init.h>
#include <linux/io.h>
@@ -22,6 +24,8 @@
#include <linux/spinlock.h>
#include <linux/string.h>
+#include "gpiolib.h"
+
struct aspeed_bank_props {
unsigned int bank;
u32 input;
@@ -56,6 +60,7 @@ struct aspeed_gpio {
struct clk *clk;
u32 *dcache;
+ u8 *cf_copro_bankmap;
};
struct aspeed_gpio_bank {
@@ -72,6 +77,9 @@ struct aspeed_gpio_bank {
static const int debounce_timers[4] = { 0x00, 0x50, 0x54, 0x58 };
+static const struct aspeed_gpio_copro_ops *copro_ops;
+static void *copro_data;
+
static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
{
.val_regs = 0x0000,
@@ -306,6 +314,50 @@ static void aspeed_gpio_change_cmd_source(struct aspeed_gpio *gpio,
iowrite32(reg, c0);
}
+static bool aspeed_gpio_copro_request(struct aspeed_gpio *gpio,
+ unsigned int offset)
+{
+ const struct aspeed_gpio_bank *bank = to_bank(offset);
+
+ if (!copro_ops || !gpio->cf_copro_bankmap)
+ return false;
+ if (!gpio->cf_copro_bankmap[offset >> 3])
+ return false;
+ if (!copro_ops->request_access)
+ return false;
+
+ /* Pause the coprocessor */
+ copro_ops->request_access(copro_data);
+
+ /* Change command source back to ARM */
+ aspeed_gpio_change_cmd_source(gpio, bank, offset >> 3, GPIO_CMDSRC_ARM);
+
+ /* Update cache */
+ gpio->dcache[GPIO_BANK(offset)] = ioread32(bank_reg(gpio, bank, reg_rdata));
+
+ return true;
+}
+
+static void aspeed_gpio_copro_release(struct aspeed_gpio *gpio,
+ unsigned int offset)
+{
+ const struct aspeed_gpio_bank *bank = to_bank(offset);
+
+ if (!copro_ops || !gpio->cf_copro_bankmap)
+ return;
+ if (!gpio->cf_copro_bankmap[offset >> 3])
+ return;
+ if (!copro_ops->release_access)
+ return;
+
+ /* Change command source back to ColdFire */
+ aspeed_gpio_change_cmd_source(gpio, bank, offset >> 3,
+ GPIO_CMDSRC_COLDFIRE);
+
+ /* Restart the coprocessor */
+ copro_ops->release_access(copro_data);
+}
+
static int aspeed_gpio_get(struct gpio_chip *gc, unsigned int offset)
{
struct aspeed_gpio *gpio = gpiochip_get_data(gc);
@@ -339,11 +391,15 @@ static void aspeed_gpio_set(struct gpio_chip *gc, unsigned int offset,
{
struct aspeed_gpio *gpio = gpiochip_get_data(gc);
unsigned long flags;
+ bool copro;
spin_lock_irqsave(&gpio->lock, flags);
+ copro = aspeed_gpio_copro_request(gpio, offset);
__aspeed_gpio_set(gc, offset, val);
+ if (copro)
+ aspeed_gpio_copro_release(gpio, offset);
spin_unlock_irqrestore(&gpio->lock, flags);
}
@@ -351,7 +407,9 @@ static int aspeed_gpio_dir_in(struct gpio_chip *gc, unsigned int offset)
{
struct aspeed_gpio *gpio = gpiochip_get_data(gc);
const struct aspeed_gpio_bank *bank = to_bank(offset);
+ void __iomem *addr = bank_reg(gpio, bank, reg_dir);
unsigned long flags;
+ bool copro;
u32 reg;
if (!have_input(gpio, offset))
@@ -359,8 +417,13 @@ static int aspeed_gpio_dir_in(struct gpio_chip *gc, unsigned int offset)
spin_lock_irqsave(&gpio->lock, flags);
- reg = ioread32(bank_reg(gpio, bank, reg_dir));
- iowrite32(reg & ~GPIO_BIT(offset), bank_reg(gpio, bank, reg_dir));
+ reg = ioread32(addr);
+ reg &= ~GPIO_BIT(offset);
+
+ copro = aspeed_gpio_copro_request(gpio, offset);
+ iowrite32(reg, addr);
+ if (copro)
+ aspeed_gpio_copro_release(gpio, offset);
spin_unlock_irqrestore(&gpio->lock, flags);
@@ -372,7 +435,9 @@ static int aspeed_gpio_dir_out(struct gpio_chip *gc,
{
struct aspeed_gpio *gpio = gpiochip_get_data(gc);
const struct aspeed_gpio_bank *bank = to_bank(offset);
+ void __iomem *addr = bank_reg(gpio, bank, reg_dir);
unsigned long flags;
+ bool copro;
u32 reg;
if (!have_output(gpio, offset))
@@ -380,10 +445,15 @@ static int aspeed_gpio_dir_out(struct gpio_chip *gc,
spin_lock_irqsave(&gpio->lock, flags);
+ reg = ioread32(addr);
+ reg |= GPIO_BIT(offset);
+
+ copro = aspeed_gpio_copro_request(gpio, offset);
__aspeed_gpio_set(gc, offset, val);
- reg = ioread32(bank_reg(gpio, bank, reg_dir));
- iowrite32(reg | GPIO_BIT(offset), bank_reg(gpio, bank, reg_dir));
+ iowrite32(reg, addr);
+ if (copro)
+ aspeed_gpio_copro_release(gpio, offset);
spin_unlock_irqrestore(&gpio->lock, flags);
return 0;
@@ -413,24 +483,23 @@ static int aspeed_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
}
static inline int irqd_to_aspeed_gpio_data(struct irq_data *d,
- struct aspeed_gpio **gpio,
- const struct aspeed_gpio_bank **bank,
- u32 *bit)
+ struct aspeed_gpio **gpio,
+ const struct aspeed_gpio_bank **bank,
+ u32 *bit, int *offset)
{
- int offset;
struct aspeed_gpio *internal;
- offset = irqd_to_hwirq(d);
+ *offset = irqd_to_hwirq(d);
internal = irq_data_get_irq_chip_data(d);
/* This might be a bit of a questionable place to check */
- if (!have_irq(internal, offset))
+ if (!have_irq(internal, *offset))
return -ENOTSUPP;
*gpio = internal;
- *bank = to_bank(offset);
- *bit = GPIO_BIT(offset);
+ *bank = to_bank(*offset);
+ *bit = GPIO_BIT(*offset);
return 0;
}
@@ -441,17 +510,23 @@ static void aspeed_gpio_irq_ack(struct irq_data *d)
struct aspeed_gpio *gpio;
unsigned long flags;
void __iomem *status_addr;
+ int rc, offset;
+ bool copro;
u32 bit;
- int rc;
- rc = irqd_to_aspeed_gpio_data(d, &gpio, &bank, &bit);
+ rc = irqd_to_aspeed_gpio_data(d, &gpio, &bank, &bit, &offset);
if (rc)
return;
status_addr = bank_reg(gpio, bank, reg_irq_status);
spin_lock_irqsave(&gpio->lock, flags);
+ copro = aspeed_gpio_copro_request(gpio, offset);
+
iowrite32(bit, status_addr);
+
+ if (copro)
+ aspeed_gpio_copro_release(gpio, offset);
spin_unlock_irqrestore(&gpio->lock, flags);
}
@@ -462,15 +537,17 @@ static void aspeed_gpio_irq_set_mask(struct irq_data *d, bool set)
unsigned long flags;
u32 reg, bit;
void __iomem *addr;
- int rc;
+ int rc, offset;
+ bool copro;
- rc = irqd_to_aspeed_gpio_data(d, &gpio, &bank, &bit);
+ rc = irqd_to_aspeed_gpio_data(d, &gpio, &bank, &bit, &offset);
if (rc)
return;
addr = bank_reg(gpio, bank, reg_irq_enable);
spin_lock_irqsave(&gpio->lock, flags);
+ copro = aspeed_gpio_copro_request(gpio, offset);
reg = ioread32(addr);
if (set)
@@ -479,6 +556,8 @@ static void aspeed_gpio_irq_set_mask(struct irq_data *d, bool set)
reg &= bit;
iowrite32(reg, addr);
+ if (copro)
+ aspeed_gpio_copro_release(gpio, offset);
spin_unlock_irqrestore(&gpio->lock, flags);
}
@@ -503,9 +582,10 @@ static int aspeed_gpio_set_type(struct irq_data *d, unsigned int type)
struct aspeed_gpio *gpio;
unsigned long flags;
void __iomem *addr;
- int rc;
+ int rc, offset;
+ bool copro;
- rc = irqd_to_aspeed_gpio_data(d, &gpio, &bank, &bit);
+ rc = irqd_to_aspeed_gpio_data(d, &gpio, &bank, &bit, &offset);
if (rc)
return -EINVAL;
@@ -528,6 +608,7 @@ static int aspeed_gpio_set_type(struct irq_data *d, unsigned int type)
}
spin_lock_irqsave(&gpio->lock, flags);
+ copro = aspeed_gpio_copro_request(gpio, offset);
addr = bank_reg(gpio, bank, reg_irq_type0);
reg = ioread32(addr);
@@ -544,6 +625,8 @@ static int aspeed_gpio_set_type(struct irq_data *d, unsigned int type)
reg = (reg & ~bit) | type2;
iowrite32(reg, addr);
+ if (copro)
+ aspeed_gpio_copro_release(gpio, offset);
spin_unlock_irqrestore(&gpio->lock, flags);
irq_set_handler_locked(d, handler);
@@ -638,11 +721,14 @@ static int aspeed_gpio_reset_tolerance(struct gpio_chip *chip,
struct aspeed_gpio *gpio = gpiochip_get_data(chip);
unsigned long flags;
void __iomem *treg;
+ bool copro;
u32 val;
treg = bank_reg(gpio, to_bank(offset), reg_tolerance);
spin_lock_irqsave(&gpio->lock, flags);
+ copro = aspeed_gpio_copro_request(gpio, offset);
+
val = readl(treg);
if (enable)
@@ -651,6 +737,9 @@ static int aspeed_gpio_reset_tolerance(struct gpio_chip *chip,
val &= ~GPIO_BIT(offset);
writel(val, treg);
+
+ if (copro)
+ aspeed_gpio_copro_release(gpio, offset);
spin_unlock_irqrestore(&gpio->lock, flags);
return 0;
@@ -746,6 +835,9 @@ static void configure_timer(struct aspeed_gpio *gpio, unsigned int offset,
void __iomem *addr;
u32 val;
+ /* Note: Debounce timer isn't under control of the command
+ * source registers, so no need to sync with the coprocessor
+ */
addr = bank_reg(gpio, bank, reg_debounce_sel1);
val = ioread32(addr);
iowrite32((val & ~mask) | GPIO_SET_DEBOUNCE1(timer, offset), addr);
@@ -892,6 +984,101 @@ static int aspeed_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
return -ENOTSUPP;
}
+/**
+ * aspeed_gpio_copro_set_ops - Sets the callbacks used for handhsaking with
+ * the coprocessor for shared GPIO banks
+ * @ops: The callbacks
+ * @data: Pointer passed back to the callbacks
+ */
+int aspeed_gpio_copro_set_ops(const struct aspeed_gpio_copro_ops *ops, void *data)
+{
+ copro_data = data;
+ copro_ops = ops;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aspeed_gpio_copro_set_ops);
+
+/**
+ * aspeed_gpio_copro_grab_gpio - Mark a GPIO used by the coprocessor. The entire
+ * bank gets marked and any access from the ARM will
+ * result in handshaking via callbacks.
+ * @desc: The GPIO to be marked
+ */
+int aspeed_gpio_copro_grab_gpio(struct gpio_desc *desc)
+{
+ struct gpio_chip *chip = gpiod_to_chip(desc);
+ struct aspeed_gpio *gpio = gpiochip_get_data(chip);
+ int rc = 0, bindex, offset = gpio_chip_hwgpio(desc);
+ const struct aspeed_gpio_bank *bank = to_bank(offset);
+ unsigned long flags;
+
+ if (!gpio->cf_copro_bankmap)
+ gpio->cf_copro_bankmap = kzalloc(gpio->config->nr_gpios >> 3, GFP_KERNEL);
+ if (!gpio->cf_copro_bankmap)
+ return -ENOMEM;
+ if (offset < 0 || offset > gpio->config->nr_gpios)
+ return -EINVAL;
+ bindex = offset >> 3;
+
+ spin_lock_irqsave(&gpio->lock, flags);
+
+ /* Sanity check, this shouldn't happen */
+ if (gpio->cf_copro_bankmap[bindex] == 0xff) {
+ rc = -EIO;
+ goto bail;
+ }
+ gpio->cf_copro_bankmap[bindex]++;
+
+ /* Switch command source */
+ if (gpio->cf_copro_bankmap[bindex] == 1)
+ aspeed_gpio_change_cmd_source(gpio, bank, bindex,
+ GPIO_CMDSRC_COLDFIRE);
+
+ bail:
+ spin_unlock_irqrestore(&gpio->lock, flags);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(aspeed_gpio_copro_grab_gpio);
+
+/**
+ * aspeed_gpio_copro_release_gpio - Unmark a GPIO used by the coprocessor.
+ * @desc: The GPIO to be marked
+ */
+int aspeed_gpio_copro_release_gpio(struct gpio_desc *desc)
+{
+ struct gpio_chip *chip = gpiod_to_chip(desc);
+ struct aspeed_gpio *gpio = gpiochip_get_data(chip);
+ int rc = 0, bindex, offset = gpio_chip_hwgpio(desc);
+ const struct aspeed_gpio_bank *bank = to_bank(offset);
+ unsigned long flags;
+
+ if (!gpio->cf_copro_bankmap)
+ return -ENXIO;
+
+ if (offset < 0 || offset > gpio->config->nr_gpios)
+ return -EINVAL;
+ bindex = offset >> 3;
+
+ spin_lock_irqsave(&gpio->lock, flags);
+
+ /* Sanity check, this shouldn't happen */
+ if (gpio->cf_copro_bankmap[bindex] == 0) {
+ rc = -EIO;
+ goto bail;
+ }
+ gpio->cf_copro_bankmap[bindex]--;
+
+ /* Switch command source */
+ if (gpio->cf_copro_bankmap[bindex] == 0)
+ aspeed_gpio_change_cmd_source(gpio, bank, bindex,
+ GPIO_CMDSRC_ARM);
+ bail:
+ spin_unlock_irqrestore(&gpio->lock, flags);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(aspeed_gpio_copro_release_gpio);
+
/*
* Any banks not specified in a struct aspeed_bank_props array are assumed to
* have the properties:
@@ -982,10 +1169,18 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
if (!gpio->dcache)
return -ENOMEM;
- /* Populate it with initial values read from the HW */
+ /*
+ * Populate it with initial values read from the HW and switch
+ * all command sources to the ARM by default
+ */
for (i = 0; i < banks; i++) {
- void __iomem *addr = bank_reg(gpio, &aspeed_gpio_banks[i], reg_rdata);
+ const struct aspeed_gpio_bank *bank = &aspeed_gpio_banks[i];
+ void __iomem *addr = bank_reg(gpio, bank, reg_rdata);
gpio->dcache[i] = ioread32(addr);
+ aspeed_gpio_change_cmd_source(gpio, bank, 0, GPIO_CMDSRC_ARM);
+ aspeed_gpio_change_cmd_source(gpio, bank, 1, GPIO_CMDSRC_ARM);
+ aspeed_gpio_change_cmd_source(gpio, bank, 2, GPIO_CMDSRC_ARM);
+ aspeed_gpio_change_cmd_source(gpio, bank, 3, GPIO_CMDSRC_ARM);
}
rc = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio);
diff --git a/include/linux/gpio/aspeed.h b/include/linux/gpio/aspeed.h
new file mode 100644
index 000000000000..b6871c9b71f7
--- /dev/null
+++ b/include/linux/gpio/aspeed.h
@@ -0,0 +1,14 @@
+#ifndef __GPIO_ASPEED_H
+#define __GPIO_ASPEED_H
+
+struct aspeed_gpio_copro_ops {
+ int (*request_access)(void *data);
+ int (*release_access)(void *data);
+};
+
+int aspeed_gpio_copro_grab_gpio(struct gpio_desc *desc);
+int aspeed_gpio_copro_release_gpio(struct gpio_desc *desc);
+int aspeed_gpio_copro_set_ops(const struct aspeed_gpio_copro_ops *ops, void *data);
+
+
+#endif /* __GPIO_ASPEED_H */
--
2.17.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH 1/4] gpio: aspeed: Rework register type accessors
2018-06-12 0:10 ` [PATCH 1/4] gpio: aspeed: Rework register type accessors Benjamin Herrenschmidt
@ 2018-06-12 4:49 ` Benjamin Herrenschmidt
0 siblings, 0 replies; 18+ messages in thread
From: Benjamin Herrenschmidt @ 2018-06-12 4:49 UTC (permalink / raw)
To: linux-aspeed
On Tue, 2018-06-12 at 10:10 +1000, Benjamin Herrenschmidt wrote:
> Use a single accessor function for all register types instead
> of several spread around. This will make it easier/cleaner
> to introduce new registers and keep the mechanism in one
> place.
>
> The big switch/case is optimized at compile time since the
> switch value is a constant.
The patches were accidentally sent based on an older internal tree,
not 4.17. I'll send a v2 properly rebased later, that shouldn't
prevent reviews.
Linus, let me know what you think of 4/4, it implements what we
discussed for arbitrating with the coprocessor. This is tested
and solid now, but it does contain some less than savoury bits
such as #include <linux/gpio/consumer.h>...
Cheers,
Ben.
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> ---
> drivers/gpio/gpio-aspeed.c | 118 ++++++++++++++++++++++---------------
> 1 file changed, 69 insertions(+), 49 deletions(-)
>
> diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
> index a62cbf3ed4a8..42aadd52f77d 100644
> --- a/drivers/gpio/gpio-aspeed.c
> +++ b/drivers/gpio/gpio-aspeed.c
> @@ -127,12 +127,21 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
> },
> };
>
> -#define GPIO_BANK(x) ((x) >> 5)
> -#define GPIO_OFFSET(x) ((x) & 0x1f)
> -#define GPIO_BIT(x) BIT(GPIO_OFFSET(x))
> +enum aspeed_gpio_reg {
> + reg_val,
> + reg_dir,
> + reg_irq_enable,
> + reg_irq_type0,
> + reg_irq_type1,
> + reg_irq_type2,
> + reg_irq_status,
> + reg_debounce_sel1,
> + reg_debounce_sel2,
> + reg_tolerance,
> +};
>
> -#define GPIO_DATA 0x00
> -#define GPIO_DIR 0x04
> +#define GPIO_VAL_VALUE 0x00
> +#define GPIO_VAL_DIR 0x04
>
> #define GPIO_IRQ_ENABLE 0x00
> #define GPIO_IRQ_TYPE0 0x04
> @@ -143,6 +152,40 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
> #define GPIO_DEBOUNCE_SEL1 0x00
> #define GPIO_DEBOUNCE_SEL2 0x04
>
> +/* This will be resolved at compile time */
> +static inline void __iomem *bank_reg(struct aspeed_gpio *gpio,
> + const struct aspeed_gpio_bank *bank,
> + const enum aspeed_gpio_reg reg)
> +{
> + switch (reg) {
> + case reg_val:
> + return gpio->base + bank->val_regs + GPIO_VAL_VALUE;
> + case reg_dir:
> + return gpio->base + bank->val_regs + GPIO_VAL_DIR;
> + case reg_irq_enable:
> + return gpio->base + bank->irq_regs + GPIO_IRQ_ENABLE;
> + case reg_irq_type0:
> + return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE0;
> + case reg_irq_type1:
> + return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE1;
> + case reg_irq_type2:
> + return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE2;
> + case reg_irq_status:
> + return gpio->base + bank->irq_regs + GPIO_IRQ_STATUS;
> + case reg_debounce_sel1:
> + return gpio->base + bank->debounce_regs + GPIO_DEBOUNCE_SEL1;
> + case reg_debounce_sel2:
> + return gpio->base + bank->debounce_regs + GPIO_DEBOUNCE_SEL2;
> + case reg_tolerance:
> + return gpio->base + bank->tolerance_regs;
> + }
> + BUG_ON(1);
> +}
> +
> +#define GPIO_BANK(x) ((x) >> 5)
> +#define GPIO_OFFSET(x) ((x) & 0x1f)
> +#define GPIO_BIT(x) BIT(GPIO_OFFSET(x))
> +
> #define _GPIO_SET_DEBOUNCE(t, o, i) ((!!((t) & BIT(i))) << GPIO_OFFSET(o))
> #define GPIO_SET_DEBOUNCE1(t, o) _GPIO_SET_DEBOUNCE(t, o, 1)
> #define GPIO_SET_DEBOUNCE2(t, o) _GPIO_SET_DEBOUNCE(t, o, 0)
> @@ -201,27 +244,12 @@ static inline bool have_output(struct aspeed_gpio *gpio, unsigned int offset)
> return !props || (props->output & GPIO_BIT(offset));
> }
>
> -static void __iomem *bank_val_reg(struct aspeed_gpio *gpio,
> - const struct aspeed_gpio_bank *bank,
> - unsigned int reg)
> -{
> - return gpio->base + bank->val_regs + reg;
> -}
> -
> -static void __iomem *bank_irq_reg(struct aspeed_gpio *gpio,
> - const struct aspeed_gpio_bank *bank,
> - unsigned int reg)
> -{
> - return gpio->base + bank->irq_regs + reg;
> -}
> -
> static int aspeed_gpio_get(struct gpio_chip *gc, unsigned int offset)
> {
> struct aspeed_gpio *gpio = gpiochip_get_data(gc);
> const struct aspeed_gpio_bank *bank = to_bank(offset);
>
> - return !!(ioread32(bank_val_reg(gpio, bank, GPIO_DATA))
> - & GPIO_BIT(offset));
> + return !!(ioread32(bank_reg(gpio, bank, reg_val)) & GPIO_BIT(offset));
> }
>
> static void __aspeed_gpio_set(struct gpio_chip *gc, unsigned int offset,
> @@ -232,7 +260,7 @@ static void __aspeed_gpio_set(struct gpio_chip *gc, unsigned int offset,
> void __iomem *addr;
> u32 reg;
>
> - addr = bank_val_reg(gpio, bank, GPIO_DATA);
> + addr = bank_reg(gpio, bank, reg_val);
> reg = gpio->dcache[GPIO_BANK(offset)];
>
> if (val)
> @@ -269,8 +297,8 @@ static int aspeed_gpio_dir_in(struct gpio_chip *gc, unsigned int offset)
>
> spin_lock_irqsave(&gpio->lock, flags);
>
> - reg = ioread32(bank_val_reg(gpio, bank, GPIO_DIR));
> - iowrite32(reg & ~GPIO_BIT(offset), bank_val_reg(gpio, bank, GPIO_DIR));
> + reg = ioread32(bank_reg(gpio, bank, reg_dir));
> + iowrite32(reg & ~GPIO_BIT(offset), bank_reg(gpio, bank, reg_dir));
>
> spin_unlock_irqrestore(&gpio->lock, flags);
>
> @@ -291,8 +319,8 @@ static int aspeed_gpio_dir_out(struct gpio_chip *gc,
> spin_lock_irqsave(&gpio->lock, flags);
>
> __aspeed_gpio_set(gc, offset, val);
> - reg = ioread32(bank_val_reg(gpio, bank, GPIO_DIR));
> - iowrite32(reg | GPIO_BIT(offset), bank_val_reg(gpio, bank, GPIO_DIR));
> + reg = ioread32(bank_reg(gpio, bank, reg_dir));
> + iowrite32(reg | GPIO_BIT(offset), bank_reg(gpio, bank, reg_dir));
>
> spin_unlock_irqrestore(&gpio->lock, flags);
>
> @@ -314,7 +342,7 @@ static int aspeed_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
>
> spin_lock_irqsave(&gpio->lock, flags);
>
> - val = ioread32(bank_val_reg(gpio, bank, GPIO_DIR)) & GPIO_BIT(offset);
> + val = ioread32(bank_reg(gpio, bank, reg_dir)) & GPIO_BIT(offset);
>
> spin_unlock_irqrestore(&gpio->lock, flags);
>
> @@ -358,7 +386,7 @@ static void aspeed_gpio_irq_ack(struct irq_data *d)
> if (rc)
> return;
>
> - status_addr = bank_irq_reg(gpio, bank, GPIO_IRQ_STATUS);
> + status_addr = bank_reg(gpio, bank, reg_irq_status);
>
> spin_lock_irqsave(&gpio->lock, flags);
> iowrite32(bit, status_addr);
> @@ -378,7 +406,7 @@ static void aspeed_gpio_irq_set_mask(struct irq_data *d, bool set)
> if (rc)
> return;
>
> - addr = bank_irq_reg(gpio, bank, GPIO_IRQ_ENABLE);
> + addr = bank_reg(gpio, bank, reg_irq_enable);
>
> spin_lock_irqsave(&gpio->lock, flags);
>
> @@ -439,17 +467,17 @@ static int aspeed_gpio_set_type(struct irq_data *d, unsigned int type)
>
> spin_lock_irqsave(&gpio->lock, flags);
>
> - addr = bank_irq_reg(gpio, bank, GPIO_IRQ_TYPE0);
> + addr = bank_reg(gpio, bank, reg_irq_type0);
> reg = ioread32(addr);
> reg = (reg & ~bit) | type0;
> iowrite32(reg, addr);
>
> - addr = bank_irq_reg(gpio, bank, GPIO_IRQ_TYPE1);
> + addr = bank_reg(gpio, bank, reg_irq_type1);
> reg = ioread32(addr);
> reg = (reg & ~bit) | type1;
> iowrite32(reg, addr);
>
> - addr = bank_irq_reg(gpio, bank, GPIO_IRQ_TYPE2);
> + addr = bank_reg(gpio, bank, reg_irq_type2);
> reg = ioread32(addr);
> reg = (reg & ~bit) | type2;
> iowrite32(reg, addr);
> @@ -474,7 +502,7 @@ static void aspeed_gpio_irq_handler(struct irq_desc *desc)
> for (i = 0; i < ARRAY_SIZE(aspeed_gpio_banks); i++) {
> const struct aspeed_gpio_bank *bank = &aspeed_gpio_banks[i];
>
> - reg = ioread32(bank_irq_reg(data, bank, GPIO_IRQ_STATUS));
> + reg = ioread32(bank_reg(data, bank, reg_irq_status));
>
> for_each_set_bit(p, ®, 32) {
> girq = irq_find_mapping(gc->irqdomain, i * 32 + p);
> @@ -546,21 +574,21 @@ static int aspeed_gpio_reset_tolerance(struct gpio_chip *chip,
> unsigned int offset, bool enable)
> {
> struct aspeed_gpio *gpio = gpiochip_get_data(chip);
> - const struct aspeed_gpio_bank *bank;
> unsigned long flags;
> + void __iomem *treg;
> u32 val;
>
> - bank = to_bank(offset);
> + treg = bank_reg(gpio, to_bank(offset), reg_tolerance);
>
> spin_lock_irqsave(&gpio->lock, flags);
> - val = readl(gpio->base + bank->tolerance_regs);
> + val = readl(treg);
>
> if (enable)
> val |= GPIO_BIT(offset);
> else
> val &= ~GPIO_BIT(offset);
>
> - writel(val, gpio->base + bank->tolerance_regs);
> + writel(val, treg);
> spin_unlock_irqrestore(&gpio->lock, flags);
>
> return 0;
> @@ -579,13 +607,6 @@ static void aspeed_gpio_free(struct gpio_chip *chip, unsigned int offset)
> pinctrl_free_gpio(chip->base + offset);
> }
>
> -static inline void __iomem *bank_debounce_reg(struct aspeed_gpio *gpio,
> - const struct aspeed_gpio_bank *bank,
> - unsigned int reg)
> -{
> - return gpio->base + bank->debounce_regs + reg;
> -}
> -
> static int usecs_to_cycles(struct aspeed_gpio *gpio, unsigned long usecs,
> u32 *cycles)
> {
> @@ -663,11 +684,11 @@ static void configure_timer(struct aspeed_gpio *gpio, unsigned int offset,
> void __iomem *addr;
> u32 val;
>
> - addr = bank_debounce_reg(gpio, bank, GPIO_DEBOUNCE_SEL1);
> + addr = bank_reg(gpio, bank, reg_debounce_sel1);
> val = ioread32(addr);
> iowrite32((val & ~mask) | GPIO_SET_DEBOUNCE1(timer, offset), addr);
>
> - addr = bank_debounce_reg(gpio, bank, GPIO_DEBOUNCE_SEL2);
> + addr = bank_reg(gpio, bank, reg_debounce_sel2);
> val = ioread32(addr);
> iowrite32((val & ~mask) | GPIO_SET_DEBOUNCE2(timer, offset), addr);
> }
> @@ -901,9 +922,8 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
>
> /* Populate it with initial values read from the HW */
> for (i = 0; i < banks; i++) {
> - const struct aspeed_gpio_bank *bank = &aspeed_gpio_banks[i];
> - gpio->dcache[i] = ioread32(gpio->base + bank->val_regs +
> - GPIO_DATA);
> + void __iomem *addr = bank_reg(gpio, &aspeed_gpio_banks[i], reg_val);
> + gpio->dcache[i] = ioread32(addr);
> }
>
> rc = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio);
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 4/4] gpio: aspeed: Add interfaces for co-processor to grab GPIOs
2018-06-12 0:10 ` [PATCH 4/4] gpio: aspeed: Add interfaces for co-processor to grab GPIOs Benjamin Herrenschmidt
@ 2018-06-12 5:37 ` Joel Stanley
2018-06-12 7:17 ` Benjamin Herrenschmidt
2018-06-15 6:04 ` Benjamin Herrenschmidt
2018-06-14 8:59 ` Linus Walleij
1 sibling, 2 replies; 18+ messages in thread
From: Joel Stanley @ 2018-06-12 5:37 UTC (permalink / raw)
To: linux-aspeed
On 12 June 2018 at 09:40, Benjamin Herrenschmidt
<benh@kernel.crashing.org> wrote:
> On the Aspeed chip, the GPIOs can be under control of the ARM
> chip or of the ColdFire coprocessor. (There's a third command
> source, the LPC bus, which we don't use or support yet).
>
> The control of which master is allowed to modify a given
> GPIO is per-bank (8 GPIOs).
>
> Unfortunately, systems already exist for which we want to
> use GPIOs of both sources in the same bank.
>
> This provides an API exported by the gpio-aspeed driver
> that an aspeed coprocessor driver can use to "grab" some
> GPIOs for use by the coprocessor, and allow the coprocessor
> driver to provide callbacks for arbitrating access.
>
> Once at least one GPIO of a given bank has been "grabbed"
> by the coprocessor, the entire bank is marked as being
> under coprocessor control. It's command source is switched
> to the coprocessor.
>
> If the ARM then tries to write to a GPIO in such a marked bank,
> the provided callbacks are used to request access from the
> coprocessor driver, which is responsible to doing whatever
> is necessary to "pause" the coprocessor or prevent it from
> trying to use the GPIOs while the ARM is doing its accesses.
>
> During that time, the command source for the bank is temporarily
> switched back to the ARM.
>
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Looks okay to me. Just need to deal with the memory allocated.
> +
> +/**
> + * aspeed_gpio_copro_grab_gpio - Mark a GPIO used by the coprocessor. The entire
> + * bank gets marked and any access from the ARM will
> + * result in handshaking via callbacks.
> + * @desc: The GPIO to be marked
> + */
> +int aspeed_gpio_copro_grab_gpio(struct gpio_desc *desc)
> +{
> + struct gpio_chip *chip = gpiod_to_chip(desc);
> + struct aspeed_gpio *gpio = gpiochip_get_data(chip);
> + int rc = 0, bindex, offset = gpio_chip_hwgpio(desc);
> + const struct aspeed_gpio_bank *bank = to_bank(offset);
> + unsigned long flags;
> +
> + if (!gpio->cf_copro_bankmap)
> + gpio->cf_copro_bankmap = kzalloc(gpio->config->nr_gpios >> 3, GFP_KERNEL);
Someone should free this.
> + if (!gpio->cf_copro_bankmap)
> + return -ENOMEM;
> + if (offset < 0 || offset > gpio->config->nr_gpios)
> + return -EINVAL;
> + bindex = offset >> 3;
> +
> + spin_lock_irqsave(&gpio->lock, flags);
> +
> + /* Sanity check, this shouldn't happen */
> + if (gpio->cf_copro_bankmap[bindex] == 0xff) {
> + rc = -EIO;
> + goto bail;
> + }
> + gpio->cf_copro_bankmap[bindex]++;
> +
> + /* Switch command source */
> + if (gpio->cf_copro_bankmap[bindex] == 1)
> + aspeed_gpio_change_cmd_source(gpio, bank, bindex,
> + GPIO_CMDSRC_COLDFIRE);
> +
> + bail:
> + spin_unlock_irqrestore(&gpio->lock, flags);
> + return rc;
> +}
> +EXPORT_SYMBOL_GPL(aspeed_gpio_copro_grab_gpio);
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 2/4] gpio: aspeed: Add "Read Data" register to read the write latch
2018-06-12 0:10 ` [PATCH 2/4] gpio: aspeed: Add "Read Data" register to read the write latch Benjamin Herrenschmidt
@ 2018-06-12 5:38 ` Joel Stanley
2018-06-12 7:18 ` Benjamin Herrenschmidt
0 siblings, 1 reply; 18+ messages in thread
From: Joel Stanley @ 2018-06-12 5:38 UTC (permalink / raw)
To: linux-aspeed
On 12 June 2018 at 09:40, Benjamin Herrenschmidt
<benh@kernel.crashing.org> wrote:
> The Aspeed GPIO hardware has a quirk: the value register, for an
> output GPIO, doesn't contain the last value written (the write
> latch content) but the sampled input value.
>
> This means that when reading back shortly after writing, you can
> get an incorrect value as the input value is delayed by a few
> synchronizers.
>
> The HW supports a separate read-only register "Data Read Register"
> which allows you to read the write latch instead.
>
> This adds the definition for it, and uses it for the initial
> population of the GPIO value cache. It will be used more in
> subsequent patches.
> can be
>
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> ---
> drivers/gpio/gpio-aspeed.c | 17 +++++++++++++++--
> 1 file changed, 15 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
> index 42aadd52f77d..210c3fcc7a40 100644
> --- a/drivers/gpio/gpio-aspeed.c
> +++ b/drivers/gpio/gpio-aspeed.c
> @@ -59,7 +59,10 @@ struct aspeed_gpio {
> };
>
> struct aspeed_gpio_bank {
> - uint16_t val_regs;
> + uint16_t val_regs; /* 0: Wr: set write latch, Rd: read input value (*)
> + * 4: Direction (0=in, 1=out)
> + */
> + uint16_t rdata_reg; /* Wr: <none> Rd: read write latch */
The numbers weren't immediately obvious to me.
> uint16_t irq_regs;
> uint16_t debounce_regs;
> uint16_t tolerance_regs;
> @@ -71,6 +74,7 @@ static const int debounce_timers[4] = { 0x00, 0x50, 0x54, 0x58 };
> static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
> {
> .val_regs = 0x0000,
> + .rdata_reg = 0x00c0,
> .irq_regs = 0x0008,
> .debounce_regs = 0x0040,
> .tolerance_regs = 0x001c,
> @@ -78,6 +82,7 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
> },
> {
> .val_regs = 0x0020,
> + .rdata_reg = 0x00c4,
> .irq_regs = 0x0028,
> .debounce_regs = 0x0048,
> .tolerance_regs = 0x003c,
> @@ -85,6 +90,7 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
> },
> {
> .val_regs = 0x0070,
> + .rdata_reg = 0x00c8,
> .irq_regs = 0x0098,
> .debounce_regs = 0x00b0,
> .tolerance_regs = 0x00ac,
> @@ -92,6 +98,7 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
> },
> {
> .val_regs = 0x0078,
> + .rdata_reg = 0x00d0,
0x00cc?
I was really confused for a second, as I'd applied the series to my
tree for testing, and my source as correct. It looks like you fixed
these in patch 3.
> .irq_regs = 0x00e8,
> .debounce_regs = 0x0100,
> .tolerance_regs = 0x00fc,
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 3/4] gpio: aspeed: Add command source registers
2018-06-12 0:10 ` [PATCH 3/4] gpio: aspeed: Add command source registers Benjamin Herrenschmidt
@ 2018-06-12 5:38 ` Joel Stanley
0 siblings, 0 replies; 18+ messages in thread
From: Joel Stanley @ 2018-06-12 5:38 UTC (permalink / raw)
To: linux-aspeed
On 12 June 2018 at 09:40, Benjamin Herrenschmidt
<benh@kernel.crashing.org> wrote:
> This adds the definitions for the command source registers
> and a helper to set them.
>
> Those registers allow to control which bus master on the
> SoC is allowed to modify a given bank of GPIOs and will
> be used by subsequent patches.
>
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> ---
> drivers/gpio/gpio-aspeed.c | 55 +++++++++++++++++++++++++++++++++++---
> 1 file changed, 52 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
> index 210c3fcc7a40..06510634ddd6 100644
> --- a/drivers/gpio/gpio-aspeed.c
> +++ b/drivers/gpio/gpio-aspeed.c
> @@ -66,6 +66,7 @@ struct aspeed_gpio_bank {
> uint16_t irq_regs;
> uint16_t debounce_regs;
> uint16_t tolerance_regs;
> + uint16_t cmdsrc_regs;
> const char names[4][3];
> };
>
> @@ -78,6 +79,7 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
> .irq_regs = 0x0008,
> .debounce_regs = 0x0040,
> .tolerance_regs = 0x001c,
> + .cmdsrc_regs = 0x0060,
> .names = { "A", "B", "C", "D" },
> },
> {
> @@ -86,6 +88,7 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
> .irq_regs = 0x0028,
> .debounce_regs = 0x0048,
> .tolerance_regs = 0x003c,
> + .cmdsrc_regs = 0x0068,
> .names = { "E", "F", "G", "H" },
> },
> {
> @@ -94,21 +97,25 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
> .irq_regs = 0x0098,
> .debounce_regs = 0x00b0,
> .tolerance_regs = 0x00ac,
> + .cmdsrc_regs = 0x0090,
> .names = { "I", "J", "K", "L" },
> },
> {
> .val_regs = 0x0078,
> - .rdata_reg = 0x00d0,
> + .rdata_reg = 0x00cc,
> .irq_regs = 0x00e8,
> .debounce_regs = 0x0100,
> .tolerance_regs = 0x00fc,
> + .cmdsrc_regs = 0x00e0,
> .names = { "M", "N", "O", "P" },
> },
> {
> .val_regs = 0x0080,
> + .rdata_reg = 0x00d0,
See the comments in patch 2.
> .irq_regs = 0x0118,
> .debounce_regs = 0x0130,
> .tolerance_regs = 0x012c,
> + .cmdsrc_regs = 0x0110,
> .names = { "Q", "R", "S", "T" },
> },
> {
> @@ -117,22 +124,25 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
> .irq_regs = 0x0148,
> .debounce_regs = 0x0160,
> .tolerance_regs = 0x015c,
> + .cmdsrc_regs = 0x0140,
> .names = { "U", "V", "W", "X" },
> },
> {
> .val_regs = 0x01E0,
> - .rdata_reg = 0x00d4,
> + .rdata_reg = 0x00d8,
> .irq_regs = 0x0178,
> .debounce_regs = 0x0190,
> .tolerance_regs = 0x018c,
> + .cmdsrc_regs = 0x0170,
> .names = { "Y", "Z", "AA", "AB" },
> },
> {
> .val_regs = 0x01e8,
> - .rdata_reg = 0x00d8,
> + .rdata_reg = 0x00dc,
> .irq_regs = 0x01a8,
> .debounce_regs = 0x01c0,
> .tolerance_regs = 0x01bc,
> + .cmdsrc_regs = 0x01a0,
> .names = { "AC", "", "", "" },
> },
> };
> @@ -149,6 +159,8 @@ enum aspeed_gpio_reg {
> reg_debounce_sel1,
> reg_debounce_sel2,
> reg_tolerance,
> + reg_cmdsrc0,
> + reg_cmdsrc1,
> };
>
> #define GPIO_VAL_VALUE 0x00
> @@ -163,6 +175,12 @@ enum aspeed_gpio_reg {
> #define GPIO_DEBOUNCE_SEL1 0x00
> #define GPIO_DEBOUNCE_SEL2 0x04
>
> +#define GPIO_CMDSRC_0 0x00
> +#define GPIO_CMDSRC_1 0x04
> +#define GPIO_CMDSRC_ARM 0
> +#define GPIO_CMDSRC_LPC 1
> +#define GPIO_CMDSRC_COLDFIRE 2
Something like this to document that 0b11 is illegal, for people who
don't have the datasheet handy?
#define GPIO_CMDSRC_RESERVED 3
> +
> /* This will be resolved at compile time */
> static inline void __iomem *bank_reg(struct aspeed_gpio *gpio,
> const struct aspeed_gpio_bank *bank,
> @@ -191,6 +209,10 @@ static inline void __iomem *bank_reg(struct aspeed_gpio *gpio,
> return gpio->base + bank->debounce_regs + GPIO_DEBOUNCE_SEL2;
> case reg_tolerance:
> return gpio->base + bank->tolerance_regs;
> + case reg_cmdsrc0:
> + return gpio->base + bank->cmdsrc_regs + GPIO_CMDSRC_0;
> + case reg_cmdsrc1:
> + return gpio->base + bank->cmdsrc_regs + GPIO_CMDSRC_1;
> }
> BUG_ON(1);
> }
> @@ -257,6 +279,33 @@ static inline bool have_output(struct aspeed_gpio *gpio, unsigned int offset)
> return !props || (props->output & GPIO_BIT(offset));
> }
>
> +static void aspeed_gpio_change_cmd_source(struct aspeed_gpio *gpio,
> + const struct aspeed_gpio_bank *bank,
> + int bindex, int cmdsrc)
> +{
> + void __iomem *c0 = bank_reg(gpio, bank, reg_cmdsrc0);
> + void __iomem *c1 = bank_reg(gpio, bank, reg_cmdsrc1);
> + u32 bit, reg;
> +
> + bit = 1u << ((bindex & 3) << 3);
I didn't follow this on first go. If you can think of a way to make it
clearer that the first 3 is just to mask off the first two bits that
would be handy.
And perhaps this?
bit = BIT((bindex & 3) << 3);
> +
> + /* Source 1 first to avoid illegal 11 combination */
> + reg = ioread32(c1);
> + if (cmdsrc & 2)
> + reg |= bit;
> + else
> + reg &= ~bit;
> + iowrite32(reg, c1);
> +
> + /* Then Source 0 */
> + reg = ioread32(c0);
> + if (cmdsrc & 1)
> + reg |= bit;
> + else
> + reg &= ~bit;
> + iowrite32(reg, c0);
> +}
> +
> static int aspeed_gpio_get(struct gpio_chip *gc, unsigned int offset)
> {
> struct aspeed_gpio *gpio = gpiochip_get_data(gc);
> --
> 2.17.0
>
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 4/4] gpio: aspeed: Add interfaces for co-processor to grab GPIOs
2018-06-12 5:37 ` Joel Stanley
@ 2018-06-12 7:17 ` Benjamin Herrenschmidt
2018-06-15 6:04 ` Benjamin Herrenschmidt
1 sibling, 0 replies; 18+ messages in thread
From: Benjamin Herrenschmidt @ 2018-06-12 7:17 UTC (permalink / raw)
To: linux-aspeed
On Tue, 2018-06-12 at 15:07 +0930, Joel Stanley wrote:
> On 12 June 2018 at 09:40, Benjamin Herrenschmidt
> <benh@kernel.crashing.org> wrote:
> > On the Aspeed chip, the GPIOs can be under control of the ARM
> > chip or of the ColdFire coprocessor. (There's a third command
> > source, the LPC bus, which we don't use or support yet).
> >
> > The control of which master is allowed to modify a given
> > GPIO is per-bank (8 GPIOs).
> >
> > Unfortunately, systems already exist for which we want to
> > use GPIOs of both sources in the same bank.
> >
> > This provides an API exported by the gpio-aspeed driver
> > that an aspeed coprocessor driver can use to "grab" some
> > GPIOs for use by the coprocessor, and allow the coprocessor
> > driver to provide callbacks for arbitrating access.
> >
> > Once at least one GPIO of a given bank has been "grabbed"
> > by the coprocessor, the entire bank is marked as being
> > under coprocessor control. It's command source is switched
> > to the coprocessor.
> >
> > If the ARM then tries to write to a GPIO in such a marked bank,
> > the provided callbacks are used to request access from the
> > coprocessor driver, which is responsible to doing whatever
> > is necessary to "pause" the coprocessor or prevent it from
> > trying to use the GPIOs while the ARM is doing its accesses.
> >
> > During that time, the command source for the bank is temporarily
> > switched back to the ARM.
> >
> > Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
>
> Looks okay to me. Just need to deal with the memory allocated.
>
> > +
> > +/**
> > + * aspeed_gpio_copro_grab_gpio - Mark a GPIO used by the coprocessor. The entire
> > + * bank gets marked and any access from the ARM will
> > + * result in handshaking via callbacks.
> > + * @desc: The GPIO to be marked
> > + */
> > +int aspeed_gpio_copro_grab_gpio(struct gpio_desc *desc)
> > +{
> > + struct gpio_chip *chip = gpiod_to_chip(desc);
> > + struct aspeed_gpio *gpio = gpiochip_get_data(chip);
> > + int rc = 0, bindex, offset = gpio_chip_hwgpio(desc);
> > + const struct aspeed_gpio_bank *bank = to_bank(offset);
> > + unsigned long flags;
> > +
> > + if (!gpio->cf_copro_bankmap)
> > + gpio->cf_copro_bankmap = kzalloc(gpio->config->nr_gpios >> 3, GFP_KERNEL);
>
> Someone should free this.
Right, I'll probably just make it a devm_kzalloc
> > + if (!gpio->cf_copro_bankmap)
> > + return -ENOMEM;
> > + if (offset < 0 || offset > gpio->config->nr_gpios)
> > + return -EINVAL;
> > + bindex = offset >> 3;
> > +
> > + spin_lock_irqsave(&gpio->lock, flags);
> > +
> > + /* Sanity check, this shouldn't happen */
> > + if (gpio->cf_copro_bankmap[bindex] == 0xff) {
> > + rc = -EIO;
> > + goto bail;
> > + }
> > + gpio->cf_copro_bankmap[bindex]++;
> > +
> > + /* Switch command source */
> > + if (gpio->cf_copro_bankmap[bindex] == 1)
> > + aspeed_gpio_change_cmd_source(gpio, bank, bindex,
> > + GPIO_CMDSRC_COLDFIRE);
> > +
> > + bail:
> > + spin_unlock_irqrestore(&gpio->lock, flags);
> > + return rc;
> > +}
> > +EXPORT_SYMBOL_GPL(aspeed_gpio_copro_grab_gpio);
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 2/4] gpio: aspeed: Add "Read Data" register to read the write latch
2018-06-12 5:38 ` Joel Stanley
@ 2018-06-12 7:18 ` Benjamin Herrenschmidt
0 siblings, 0 replies; 18+ messages in thread
From: Benjamin Herrenschmidt @ 2018-06-12 7:18 UTC (permalink / raw)
To: linux-aspeed
On Tue, 2018-06-12 at 15:08 +0930, Joel Stanley wrote:
> On 12 June 2018 at 09:40, Benjamin Herrenschmidt
> <benh@kernel.crashing.org> wrote:
> > The Aspeed GPIO hardware has a quirk: the value register, for an
> > output GPIO, doesn't contain the last value written (the write
> > latch content) but the sampled input value.
> >
> > This means that when reading back shortly after writing, you can
> > get an incorrect value as the input value is delayed by a few
> > synchronizers.
> >
> > The HW supports a separate read-only register "Data Read Register"
> > which allows you to read the write latch instead.
> >
> > This adds the definition for it, and uses it for the initial
> > population of the GPIO value cache. It will be used more in
> > subsequent patches.
> > can be
> >
> > Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> > ---
> > drivers/gpio/gpio-aspeed.c | 17 +++++++++++++++--
> > 1 file changed, 15 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
> > index 42aadd52f77d..210c3fcc7a40 100644
> > --- a/drivers/gpio/gpio-aspeed.c
> > +++ b/drivers/gpio/gpio-aspeed.c
> > @@ -59,7 +59,10 @@ struct aspeed_gpio {
> > };
> >
> > struct aspeed_gpio_bank {
> > - uint16_t val_regs;
> > + uint16_t val_regs; /* 0: Wr: set write latch, Rd: read input value (*)
> > + * 4: Direction (0=in, 1=out)
> > + */
> > + uint16_t rdata_reg; /* Wr: <none> Rd: read write latch */
>
> The numbers weren't immediately obvious to me.
>
> > uint16_t irq_regs;
> > uint16_t debounce_regs;
> > uint16_t tolerance_regs;
> > @@ -71,6 +74,7 @@ static const int debounce_timers[4] = { 0x00, 0x50, 0x54, 0x58 };
> > static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
> > {
> > .val_regs = 0x0000,
> > + .rdata_reg = 0x00c0,
> > .irq_regs = 0x0008,
> > .debounce_regs = 0x0040,
> > .tolerance_regs = 0x001c,
> > @@ -78,6 +82,7 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
> > },
> > {
> > .val_regs = 0x0020,
> > + .rdata_reg = 0x00c4,
> > .irq_regs = 0x0028,
> > .debounce_regs = 0x0048,
> > .tolerance_regs = 0x003c,
> > @@ -85,6 +90,7 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
> > },
> > {
> > .val_regs = 0x0070,
> > + .rdata_reg = 0x00c8,
> > .irq_regs = 0x0098,
> > .debounce_regs = 0x00b0,
> > .tolerance_regs = 0x00ac,
> > @@ -92,6 +98,7 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
> > },
> > {
> > .val_regs = 0x0078,
> > + .rdata_reg = 0x00d0,
>
> 0x00cc?
>
> I was really confused for a second, as I'd applied the series to my
> tree for testing, and my source as correct. It looks like you fixed
> these in patch 3.
Ah yes, patch splitting mistake. I'll fix that up, thanks.
> > .irq_regs = 0x00e8,
> > .debounce_regs = 0x0100,
> > .tolerance_regs = 0x00fc,
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 4/4] gpio: aspeed: Add interfaces for co-processor to grab GPIOs
2018-06-12 0:10 ` [PATCH 4/4] gpio: aspeed: Add interfaces for co-processor to grab GPIOs Benjamin Herrenschmidt
2018-06-12 5:37 ` Joel Stanley
@ 2018-06-14 8:59 ` Linus Walleij
2018-06-14 23:40 ` Benjamin Herrenschmidt
1 sibling, 1 reply; 18+ messages in thread
From: Linus Walleij @ 2018-06-14 8:59 UTC (permalink / raw)
To: linux-aspeed
On Tue, Jun 12, 2018 at 2:10 AM, Benjamin Herrenschmidt
<benh@kernel.crashing.org> wrote:
> On the Aspeed chip, the GPIOs can be under control of the ARM
> chip or of the ColdFire coprocessor. (There's a third command
> source, the LPC bus, which we don't use or support yet).
>
> The control of which master is allowed to modify a given
> GPIO is per-bank (8 GPIOs).
>
> Unfortunately, systems already exist for which we want to
> use GPIOs of both sources in the same bank.
>
> This provides an API exported by the gpio-aspeed driver
> that an aspeed coprocessor driver can use to "grab" some
> GPIOs for use by the coprocessor, and allow the coprocessor
> driver to provide callbacks for arbitrating access.
>
> Once at least one GPIO of a given bank has been "grabbed"
> by the coprocessor, the entire bank is marked as being
> under coprocessor control. It's command source is switched
> to the coprocessor.
>
> If the ARM then tries to write to a GPIO in such a marked bank,
> the provided callbacks are used to request access from the
> coprocessor driver, which is responsible to doing whatever
> is necessary to "pause" the coprocessor or prevent it from
> trying to use the GPIOs while the ARM is doing its accesses.
>
> During that time, the command source for the bank is temporarily
> switched back to the ARM.
>
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Overall looks fine!
You definately need something like this for handling this special case.
> +#include <linux/gpio/consumer.h>
Why do you need this?
I don't see that you use any functions from it.
> +#include "gpiolib.h"
I'm not so happy about this either, what is this needed for?
It seems to me you can remove both includes, but admittedly I miss
fine details all the time.
Yours,
Linus Walleij
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 0/4] gpio: aspeed: Fixes and support for sharing with co-processor
2018-06-12 0:10 [PATCH 0/4] gpio: aspeed: Fixes and support for sharing with co-processor Benjamin Herrenschmidt
` (3 preceding siblings ...)
2018-06-12 0:10 ` [PATCH 4/4] gpio: aspeed: Add interfaces for co-processor to grab GPIOs Benjamin Herrenschmidt
@ 2018-06-14 9:01 ` Linus Walleij
2018-06-14 23:42 ` Benjamin Herrenschmidt
4 siblings, 1 reply; 18+ messages in thread
From: Linus Walleij @ 2018-06-14 9:01 UTC (permalink / raw)
To: linux-aspeed
On Tue, Jun 12, 2018 at 2:10 AM, Benjamin Herrenschmidt
<benh@kernel.crashing.org> wrote:
> This series cleans up the register accessors a bit, adds missing ones,
> fixes the access to the write latch and finally adds an interface
> that a co-processor driver can use to change the owner of some of
> the GPIO lines and arbitrate access to shared GPIO banks.
I'm pretty happy with the series as long as they have Joel's consent/ACK
which seems doable. See comment about using gpiolib internals which I
do not think is necessary.
Yours,
Linus Walleij
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 4/4] gpio: aspeed: Add interfaces for co-processor to grab GPIOs
2018-06-14 8:59 ` Linus Walleij
@ 2018-06-14 23:40 ` Benjamin Herrenschmidt
2018-06-28 13:42 ` Linus Walleij
0 siblings, 1 reply; 18+ messages in thread
From: Benjamin Herrenschmidt @ 2018-06-14 23:40 UTC (permalink / raw)
To: linux-aspeed
On Thu, 2018-06-14 at 10:59 +0200, Linus Walleij wrote:
>
> Overall looks fine!
>
> You definately need something like this for handling this special case.
>
> > +#include <linux/gpio/consumer.h>
>
> Why do you need this?
>
> I don't see that you use any functions from it.
>
> > +#include "gpiolib.h"
>
> I'm not so happy about this either, what is this needed for?
>
> It seems to me you can remove both includes, but admittedly I miss
> fine details all the time.
I wish I could :-) This is the main wart in there to be honest.
consumer.h is needed to build gpiolib.h
gpiolib.h is needed for gpio_chip_hwgpio() which needs the definition
of gpio_desc, etc...
I didn't find a simple way out if it... the "API" I expose to the copro
driver takes a gpio_desc which is the clean thing to do but I can't get
to the underlying GPIO number without gpiolib.
Cheers,
Ben.
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 0/4] gpio: aspeed: Fixes and support for sharing with co-processor
2018-06-14 9:01 ` [PATCH 0/4] gpio: aspeed: Fixes and support for sharing with co-processor Linus Walleij
@ 2018-06-14 23:42 ` Benjamin Herrenschmidt
0 siblings, 0 replies; 18+ messages in thread
From: Benjamin Herrenschmidt @ 2018-06-14 23:42 UTC (permalink / raw)
To: linux-aspeed
On Thu, 2018-06-14 at 11:01 +0200, Linus Walleij wrote:
> On Tue, Jun 12, 2018 at 2:10 AM, Benjamin Herrenschmidt
> <benh@kernel.crashing.org> wrote:
>
> > This series cleans up the register accessors a bit, adds missing ones,
> > fixes the access to the write latch and finally adds an interface
> > that a co-processor driver can use to change the owner of some of
> > the GPIO lines and arbitrate access to shared GPIO banks.
>
> I'm pretty happy with the series as long as they have Joel's consent/ACK
> which seems doable. See comment about using gpiolib internals which I
> do not think is necessary.
Thanks. Yes I need to address Joel's comments, I was waiting for your
review before posting a v2.
As for the gpiolib internals, it's unfortunate, see my other email, I'm
happy to change that if you can suggest an alternative approach...
Cheers,
Ben.
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 4/4] gpio: aspeed: Add interfaces for co-processor to grab GPIOs
2018-06-12 5:37 ` Joel Stanley
2018-06-12 7:17 ` Benjamin Herrenschmidt
@ 2018-06-15 6:04 ` Benjamin Herrenschmidt
1 sibling, 0 replies; 18+ messages in thread
From: Benjamin Herrenschmidt @ 2018-06-15 6:04 UTC (permalink / raw)
To: linux-aspeed
> > +
> > +/**
> > + * aspeed_gpio_copro_grab_gpio - Mark a GPIO used by the coprocessor. The entire
> > + * bank gets marked and any access from the ARM will
> > + * result in handshaking via callbacks.
> > + * @desc: The GPIO to be marked
> > + */
> > +int aspeed_gpio_copro_grab_gpio(struct gpio_desc *desc)
> > +{
> > + struct gpio_chip *chip = gpiod_to_chip(desc);
> > + struct aspeed_gpio *gpio = gpiochip_get_data(chip);
> > + int rc = 0, bindex, offset = gpio_chip_hwgpio(desc);
> > + const struct aspeed_gpio_bank *bank = to_bank(offset);
> > + unsigned long flags;
> > +
> > + if (!gpio->cf_copro_bankmap)
> > + gpio->cf_copro_bankmap = kzalloc(gpio->config->nr_gpios >> 3, GFP_KERNEL);
>
> Someone should free this.
Actually no. The driver doesn't have a remove(), so it doesn't.
Cheers,
Ben.
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 4/4] gpio: aspeed: Add interfaces for co-processor to grab GPIOs
2018-06-14 23:40 ` Benjamin Herrenschmidt
@ 2018-06-28 13:42 ` Linus Walleij
2018-06-28 23:53 ` Benjamin Herrenschmidt
0 siblings, 1 reply; 18+ messages in thread
From: Linus Walleij @ 2018-06-28 13:42 UTC (permalink / raw)
To: linux-aspeed
On Fri, Jun 15, 2018 at 1:41 AM Benjamin Herrenschmidt
<benh@kernel.crashing.org> wrote:
> On Thu, 2018-06-14 at 10:59 +0200, Linus Walleij wrote:
> >
> > Overall looks fine!
> >
> > You definately need something like this for handling this special case.
> >
> > > +#include <linux/gpio/consumer.h>
> >
> > Why do you need this?
> >
> > I don't see that you use any functions from it.
> >
> > > +#include "gpiolib.h"
> >
> > I'm not so happy about this either, what is this needed for?
> >
> > It seems to me you can remove both includes, but admittedly I miss
> > fine details all the time.
>
> I wish I could :-) This is the main wart in there to be honest.
>
> consumer.h is needed to build gpiolib.h
>
> gpiolib.h is needed for gpio_chip_hwgpio() which needs the definition
> of gpio_desc, etc...
Aha I see, you need that because while normally the gpiolib
does the conversion of desc -> offset this is needed here
in a driver.
Maybe we should just name it gpiod_to_offset() or something
and make it a public API.
But this is actually OK, you definately need it here, toss in some
comment or so about what is going on and why this is included
so we know in the future.
Yours,
Linus Walleij
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 4/4] gpio: aspeed: Add interfaces for co-processor to grab GPIOs
2018-06-28 13:42 ` Linus Walleij
@ 2018-06-28 23:53 ` Benjamin Herrenschmidt
0 siblings, 0 replies; 18+ messages in thread
From: Benjamin Herrenschmidt @ 2018-06-28 23:53 UTC (permalink / raw)
To: linux-aspeed
On Thu, 2018-06-28 at 15:42 +0200, Linus Walleij wrote:
>
> Aha I see, you need that because while normally the gpiolib
> does the conversion of desc -> offset this is needed here
> in a driver.
>
> Maybe we should just name it gpiod_to_offset() or something
> and make it a public API.
>
> But this is actually OK, you definately need it here, toss in some
> comment or so about what is going on and why this is included
> so we know in the future.
Ok, I'll spin a v4 with the interface addition I need to pass the
offsets to the coprocessor and will add that comment some time today.
Cheers,
Ben.
^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2018-06-28 23:53 UTC | newest]
Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-06-12 0:10 [PATCH 0/4] gpio: aspeed: Fixes and support for sharing with co-processor Benjamin Herrenschmidt
2018-06-12 0:10 ` [PATCH 1/4] gpio: aspeed: Rework register type accessors Benjamin Herrenschmidt
2018-06-12 4:49 ` Benjamin Herrenschmidt
2018-06-12 0:10 ` [PATCH 2/4] gpio: aspeed: Add "Read Data" register to read the write latch Benjamin Herrenschmidt
2018-06-12 5:38 ` Joel Stanley
2018-06-12 7:18 ` Benjamin Herrenschmidt
2018-06-12 0:10 ` [PATCH 3/4] gpio: aspeed: Add command source registers Benjamin Herrenschmidt
2018-06-12 5:38 ` Joel Stanley
2018-06-12 0:10 ` [PATCH 4/4] gpio: aspeed: Add interfaces for co-processor to grab GPIOs Benjamin Herrenschmidt
2018-06-12 5:37 ` Joel Stanley
2018-06-12 7:17 ` Benjamin Herrenschmidt
2018-06-15 6:04 ` Benjamin Herrenschmidt
2018-06-14 8:59 ` Linus Walleij
2018-06-14 23:40 ` Benjamin Herrenschmidt
2018-06-28 13:42 ` Linus Walleij
2018-06-28 23:53 ` Benjamin Herrenschmidt
2018-06-14 9:01 ` [PATCH 0/4] gpio: aspeed: Fixes and support for sharing with co-processor Linus Walleij
2018-06-14 23:42 ` Benjamin Herrenschmidt
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).