linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 0/6] Add Aspeed G7 gpio support
@ 2024-09-19  9:43 Billy Tsai
  2024-09-19  9:43 ` [PATCH v4 1/6] dt-bindings: gpio: aspeed,ast2400-gpio: Support ast2700 Billy Tsai
                   ` (5 more replies)
  0 siblings, 6 replies; 16+ messages in thread
From: Billy Tsai @ 2024-09-19  9:43 UTC (permalink / raw)
  To: linus.walleij, brgl, robh, krzk+dt, conor+dt, joel, andrew,
	linux-gpio, devicetree, linux-arm-kernel, linux-aspeed,
	linux-kernel, BMC-SW, Peter.Yin, Jay_Zhang

The Aspeed 7th generation SoC features two GPIO controllers: one with 12
GPIO pins and another with 216 GPIO pins. The main difference from the
previous generation is that the control logic has been updated to support
per-pin control, allowing each pin to have its own 32-bit register for
configuring value, direction, interrupt type, and more.
This patch serial also add low-level operations (llops) to abstract the
register access for GPIO registers and the coprocessor request/release in
gpio-aspeed.c making it easier to extend the driver to support different
hardware register layouts.

Change since v3:
- Add `privilege_ctrl` and `privilege_init` callback
- Use `bool aspeed_gpio_support_copro()` api to replace the
`cmd_source_supoort` flag
- Add the `dcache_require` flag and move the dcache usage into the
reg_bit_set callback
- `reg_bits_set` -> `reg_bit_set` and `reg_bits_read` -> `reg_bits_get`
- `bool copro = 0` -> `bool copro = false`
- `if (!gpio->config->llops->reg_bit_set || 
!gpio->config->llops->reg_bits_get) return -EINVAL;`
- Correct the access of reg_irq_status
- Remove __init attribute to fix the compiler warning

Change since v2:
- Correct minItems for gpio-line names
- Remove the example for ast2700, because it’s the same as the AST2600
- Fix the sparse warning which is reported by the test robot
- Remove the version and use the match data to replace it.
- Add another two patches one for deferred probe one for flush write.

Changes since v1:
- Merge the gpio-aspeed-g7.c into the gpio-aspeed.c.
- Create the llops in gpio-aspeed.c for flexibility.

Billy Tsai (6):
  dt-bindings: gpio: aspeed,ast2400-gpio: Support ast2700
  gpio: aspeed: Remove the name for bank array
  gpio: aspeed: Create llops to handle hardware access
  gpio: aspeed: Support G7 Aspeed gpio controller
  gpio: aspeed: Change the macro to support deferred probe
  gpio: aspeed: Add the flush write to ensure the write complete.

 .../bindings/gpio/aspeed,ast2400-gpio.yaml    |  19 +-
 drivers/gpio/gpio-aspeed.c                    | 565 +++++++++++-------
 2 files changed, 358 insertions(+), 226 deletions(-)

-- 
2.25.1



^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v4 1/6] dt-bindings: gpio: aspeed,ast2400-gpio: Support ast2700
  2024-09-19  9:43 [PATCH v4 0/6] Add Aspeed G7 gpio support Billy Tsai
@ 2024-09-19  9:43 ` Billy Tsai
  2024-09-19  9:43 ` [PATCH v4 2/6] gpio: aspeed: Remove the name for bank array Billy Tsai
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 16+ messages in thread
From: Billy Tsai @ 2024-09-19  9:43 UTC (permalink / raw)
  To: linus.walleij, brgl, robh, krzk+dt, conor+dt, joel, andrew,
	linux-gpio, devicetree, linux-arm-kernel, linux-aspeed,
	linux-kernel, BMC-SW, Peter.Yin, Jay_Zhang
  Cc: Conor Dooley

The AST2700 is the 7th generation SoC from Aspeed, featuring two GPIO
controllers: one with 12 GPIO pins and another with 216 GPIO pins.

Acked-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Billy Tsai <billy_tsai@aspeedtech.com>
---
 .../bindings/gpio/aspeed,ast2400-gpio.yaml    | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/gpio/aspeed,ast2400-gpio.yaml b/Documentation/devicetree/bindings/gpio/aspeed,ast2400-gpio.yaml
index cf11aa7ec8c7..b9afd07a9d24 100644
--- a/Documentation/devicetree/bindings/gpio/aspeed,ast2400-gpio.yaml
+++ b/Documentation/devicetree/bindings/gpio/aspeed,ast2400-gpio.yaml
@@ -15,6 +15,7 @@ properties:
       - aspeed,ast2400-gpio
       - aspeed,ast2500-gpio
       - aspeed,ast2600-gpio
+      - aspeed,ast2700-gpio
 
   reg:
     maxItems: 1
@@ -25,7 +26,7 @@ properties:
 
   gpio-controller: true
   gpio-line-names:
-    minItems: 36
+    minItems: 12
     maxItems: 232
 
   gpio-ranges: true
@@ -42,7 +43,7 @@ properties:
     const: 2
 
   ngpios:
-    minimum: 36
+    minimum: 12
     maximum: 232
 
 required:
@@ -93,6 +94,20 @@ allOf:
           enum: [ 36, 208 ]
       required:
         - ngpios
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: aspeed,ast2700-gpio
+    then:
+      properties:
+        gpio-line-names:
+          minItems: 12
+          maxItems: 216
+        ngpios:
+          enum: [ 12, 216 ]
+      required:
+        - ngpios
 
 additionalProperties: false
 
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v4 2/6] gpio: aspeed: Remove the name for bank array
  2024-09-19  9:43 [PATCH v4 0/6] Add Aspeed G7 gpio support Billy Tsai
  2024-09-19  9:43 ` [PATCH v4 1/6] dt-bindings: gpio: aspeed,ast2400-gpio: Support ast2700 Billy Tsai
@ 2024-09-19  9:43 ` Billy Tsai
  2024-09-19  9:43 ` [PATCH v4 3/6] gpio: aspeed: Create llops to handle hardware access Billy Tsai
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 16+ messages in thread
From: Billy Tsai @ 2024-09-19  9:43 UTC (permalink / raw)
  To: linus.walleij, brgl, robh, krzk+dt, conor+dt, joel, andrew,
	linux-gpio, devicetree, linux-arm-kernel, linux-aspeed,
	linux-kernel, BMC-SW, Peter.Yin, Jay_Zhang

The bank array name is only used to determine if the GPIO offset is valid,
and this condition can be replaced by checking if the offset exceeds the
ngpio property.

Reviewed-by: Andrew Jeffery <andrew@codeconstruct.com.au>
Signed-off-by: Billy Tsai <billy_tsai@aspeedtech.com>
---
 drivers/gpio/gpio-aspeed.c | 17 ++++-------------
 1 file changed, 4 insertions(+), 13 deletions(-)

diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
index 04c03402db6d..d20e15b2079d 100644
--- a/drivers/gpio/gpio-aspeed.c
+++ b/drivers/gpio/gpio-aspeed.c
@@ -77,7 +77,6 @@ struct aspeed_gpio_bank {
 	uint16_t	debounce_regs;
 	uint16_t	tolerance_regs;
 	uint16_t	cmdsrc_regs;
-	const char	names[4][3];
 };
 
 /*
@@ -104,7 +103,6 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
 		.debounce_regs = 0x0040,
 		.tolerance_regs = 0x001c,
 		.cmdsrc_regs = 0x0060,
-		.names = { "A", "B", "C", "D" },
 	},
 	{
 		.val_regs = 0x0020,
@@ -113,7 +111,6 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
 		.debounce_regs = 0x0048,
 		.tolerance_regs = 0x003c,
 		.cmdsrc_regs = 0x0068,
-		.names = { "E", "F", "G", "H" },
 	},
 	{
 		.val_regs = 0x0070,
@@ -122,7 +119,6 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
 		.debounce_regs = 0x00b0,
 		.tolerance_regs = 0x00ac,
 		.cmdsrc_regs = 0x0090,
-		.names = { "I", "J", "K", "L" },
 	},
 	{
 		.val_regs = 0x0078,
@@ -131,7 +127,6 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
 		.debounce_regs = 0x0100,
 		.tolerance_regs = 0x00fc,
 		.cmdsrc_regs = 0x00e0,
-		.names = { "M", "N", "O", "P" },
 	},
 	{
 		.val_regs = 0x0080,
@@ -140,7 +135,6 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
 		.debounce_regs = 0x0130,
 		.tolerance_regs = 0x012c,
 		.cmdsrc_regs = 0x0110,
-		.names = { "Q", "R", "S", "T" },
 	},
 	{
 		.val_regs = 0x0088,
@@ -149,7 +143,6 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
 		.debounce_regs = 0x0160,
 		.tolerance_regs = 0x015c,
 		.cmdsrc_regs = 0x0140,
-		.names = { "U", "V", "W", "X" },
 	},
 	{
 		.val_regs = 0x01E0,
@@ -158,7 +151,6 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
 		.debounce_regs = 0x0190,
 		.tolerance_regs = 0x018c,
 		.cmdsrc_regs = 0x0170,
-		.names = { "Y", "Z", "AA", "AB" },
 	},
 	{
 		.val_regs = 0x01e8,
@@ -167,7 +159,6 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
 		.debounce_regs = 0x01c0,
 		.tolerance_regs = 0x01bc,
 		.cmdsrc_regs = 0x01a0,
-		.names = { "AC", "", "", "" },
 	},
 };
 
@@ -280,11 +271,11 @@ static inline const struct aspeed_bank_props *find_bank_props(
 static inline bool have_gpio(struct aspeed_gpio *gpio, unsigned int offset)
 {
 	const struct aspeed_bank_props *props = find_bank_props(gpio, offset);
-	const struct aspeed_gpio_bank *bank = to_bank(offset);
-	unsigned int group = GPIO_OFFSET(offset) / 8;
 
-	return bank->names[group][0] != '\0' &&
-		(!props || ((props->input | props->output) & GPIO_BIT(offset)));
+	if (offset >= gpio->chip.ngpio)
+		return false;
+
+	return (!props || ((props->input | props->output) & GPIO_BIT(offset)));
 }
 
 static inline bool have_input(struct aspeed_gpio *gpio, unsigned int offset)
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v4 3/6] gpio: aspeed: Create llops to handle hardware access
  2024-09-19  9:43 [PATCH v4 0/6] Add Aspeed G7 gpio support Billy Tsai
  2024-09-19  9:43 ` [PATCH v4 1/6] dt-bindings: gpio: aspeed,ast2400-gpio: Support ast2700 Billy Tsai
  2024-09-19  9:43 ` [PATCH v4 2/6] gpio: aspeed: Remove the name for bank array Billy Tsai
@ 2024-09-19  9:43 ` Billy Tsai
  2024-09-20  2:56   ` Andrew Jeffery
  2024-09-19  9:43 ` [PATCH v4 4/6] gpio: aspeed: Support G7 Aspeed gpio controller Billy Tsai
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 16+ messages in thread
From: Billy Tsai @ 2024-09-19  9:43 UTC (permalink / raw)
  To: linus.walleij, brgl, robh, krzk+dt, conor+dt, joel, andrew,
	linux-gpio, devicetree, linux-arm-kernel, linux-aspeed,
	linux-kernel, BMC-SW, Peter.Yin, Jay_Zhang

Add low-level operations (llops) to abstract the register access for GPIO
registers and the coprocessor request/release. With this abstraction
layer, the driver can separate the hardware and software logic, making it
easier to extend the driver to support different hardware register
layouts.

Signed-off-by: Billy Tsai <billy_tsai@aspeedtech.com>
---
 drivers/gpio/gpio-aspeed.c | 429 +++++++++++++++++++------------------
 1 file changed, 220 insertions(+), 209 deletions(-)

diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
index d20e15b2079d..8b334ce7b60a 100644
--- a/drivers/gpio/gpio-aspeed.c
+++ b/drivers/gpio/gpio-aspeed.c
@@ -39,6 +39,10 @@ struct aspeed_bank_props {
 struct aspeed_gpio_config {
 	unsigned int nr_gpios;
 	const struct aspeed_bank_props *props;
+	const struct aspeed_gpio_llops *llops;
+	const int *debounce_timers_array;
+	int debounce_timers_num;
+	bool dcache_require;
 };
 
 /*
@@ -178,6 +182,17 @@ enum aspeed_gpio_reg {
 	reg_cmdsrc1,
 };
 
+struct aspeed_gpio_llops {
+	bool (*copro_request)(struct aspeed_gpio *gpio, unsigned int offset);
+	void (*copro_release)(struct aspeed_gpio *gpio, unsigned int offset);
+	void (*reg_bit_set)(struct aspeed_gpio *gpio, unsigned int offset,
+			    const enum aspeed_gpio_reg reg, bool val);
+	u32 (*reg_bits_get)(struct aspeed_gpio *gpio, unsigned int offset,
+			    const enum aspeed_gpio_reg reg);
+	void (*privilege_ctrl)(struct aspeed_gpio *gpio, unsigned int offset, int owner);
+	void (*privilege_init)(struct aspeed_gpio *gpio);
+};
+
 #define GPIO_VAL_VALUE	0x00
 #define GPIO_VAL_DIR	0x04
 
@@ -237,10 +252,6 @@ static inline void __iomem *bank_reg(struct aspeed_gpio *gpio,
 #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)
-
 static const struct aspeed_gpio_bank *to_bank(unsigned int offset)
 {
 	unsigned int bank = GPIO_BANK(offset);
@@ -296,107 +307,48 @@ static inline bool have_output(struct aspeed_gpio *gpio, unsigned int offset)
 }
 
 static void aspeed_gpio_change_cmd_source(struct aspeed_gpio *gpio,
-					  const struct aspeed_gpio_bank *bank,
-					  int bindex, int cmdsrc)
+					  unsigned int offset,
+					  int cmdsrc)
 {
-	void __iomem *c0 = bank_reg(gpio, bank, reg_cmdsrc0);
-	void __iomem *c1 = bank_reg(gpio, bank, reg_cmdsrc1);
-	u32 bit, reg;
-
-	/*
-	 * Each register controls 4 banks, so take the bottom 2
-	 * bits of the bank index, and use them to select the
-	 * right control bit (0, 8, 16 or 24).
-	 */
-	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);
+	if (gpio->config->llops->privilege_ctrl)
+		gpio->config->llops->privilege_ctrl(gpio, offset, cmdsrc);
 }
 
 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);
+	if (gpio->config->llops->copro_request)
+		return gpio->config->llops->copro_request(gpio, offset);
 
-	/* 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;
+	return false;
 }
 
 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);
+	if (gpio->config->llops->copro_release)
+		gpio->config->llops->copro_release(gpio, offset);
+}
 
-	/* Restart the coprocessor */
-	copro_ops->release_access(copro_data);
+static bool aspeed_gpio_support_copro(struct aspeed_gpio *gpio)
+{
+	return gpio->config->llops->copro_request && gpio->config->llops->copro_release &&
+	       gpio->config->llops->privilege_ctrl && gpio->config->llops->privilege_init;
 }
 
 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_reg(gpio, bank, reg_val)) & GPIO_BIT(offset));
+	return gpio->config->llops->reg_bits_get(gpio, offset, reg_val);
 }
 
 static void __aspeed_gpio_set(struct gpio_chip *gc, unsigned int offset,
 			      int val)
 {
 	struct aspeed_gpio *gpio = gpiochip_get_data(gc);
-	const struct aspeed_gpio_bank *bank = to_bank(offset);
-	void __iomem *addr;
-	u32 reg;
-
-	addr = bank_reg(gpio, bank, reg_val);
-	reg = gpio->dcache[GPIO_BANK(offset)];
-
-	if (val)
-		reg |= GPIO_BIT(offset);
-	else
-		reg &= ~GPIO_BIT(offset);
-	gpio->dcache[GPIO_BANK(offset)] = reg;
 
-	iowrite32(reg, addr);
+	gpio->config->llops->reg_bit_set(gpio, offset, reg_val, val);
 }
 
 static void aspeed_gpio_set(struct gpio_chip *gc, unsigned int offset,
@@ -404,7 +356,7 @@ 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;
+	bool copro = false;
 
 	raw_spin_lock_irqsave(&gpio->lock, flags);
 	copro = aspeed_gpio_copro_request(gpio, offset);
@@ -419,22 +371,16 @@ static void aspeed_gpio_set(struct gpio_chip *gc, unsigned int offset,
 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;
+	bool copro = false;
 
 	if (!have_input(gpio, offset))
 		return -ENOTSUPP;
 
 	raw_spin_lock_irqsave(&gpio->lock, flags);
 
-	reg = ioread32(addr);
-	reg &= ~GPIO_BIT(offset);
-
 	copro = aspeed_gpio_copro_request(gpio, offset);
-	iowrite32(reg, addr);
+	gpio->config->llops->reg_bit_set(gpio, offset, reg_dir, 0);
 	if (copro)
 		aspeed_gpio_copro_release(gpio, offset);
 
@@ -447,23 +393,17 @@ static int aspeed_gpio_dir_out(struct gpio_chip *gc,
 			       unsigned int offset, int val)
 {
 	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;
+	bool copro = false;
 
 	if (!have_output(gpio, offset))
 		return -ENOTSUPP;
 
 	raw_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);
-	iowrite32(reg, addr);
+	gpio->config->llops->reg_bit_set(gpio, offset, reg_dir, 1);
 
 	if (copro)
 		aspeed_gpio_copro_release(gpio, offset);
@@ -475,7 +415,6 @@ static int aspeed_gpio_dir_out(struct gpio_chip *gc,
 static int aspeed_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
 {
 	struct aspeed_gpio *gpio = gpiochip_get_data(gc);
-	const struct aspeed_gpio_bank *bank = to_bank(offset);
 	unsigned long flags;
 	u32 val;
 
@@ -487,7 +426,7 @@ static int aspeed_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
 
 	raw_spin_lock_irqsave(&gpio->lock, flags);
 
-	val = ioread32(bank_reg(gpio, bank, reg_dir)) & GPIO_BIT(offset);
+	val = gpio->config->llops->reg_bits_get(gpio, offset, reg_dir);
 
 	raw_spin_unlock_irqrestore(&gpio->lock, flags);
 
@@ -496,8 +435,7 @@ 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, int *offset)
+					   int *offset)
 {
 	struct aspeed_gpio *internal;
 
@@ -510,32 +448,25 @@ static inline int irqd_to_aspeed_gpio_data(struct irq_data *d,
 		return -ENOTSUPP;
 
 	*gpio = internal;
-	*bank = to_bank(*offset);
-	*bit = GPIO_BIT(*offset);
 
 	return 0;
 }
 
 static void aspeed_gpio_irq_ack(struct irq_data *d)
 {
-	const struct aspeed_gpio_bank *bank;
 	struct aspeed_gpio *gpio;
 	unsigned long flags;
-	void __iomem *status_addr;
 	int rc, offset;
-	bool copro;
-	u32 bit;
+	bool copro = false;
 
-	rc = irqd_to_aspeed_gpio_data(d, &gpio, &bank, &bit, &offset);
+	rc = irqd_to_aspeed_gpio_data(d, &gpio, &offset);
 	if (rc)
 		return;
 
-	status_addr = bank_reg(gpio, bank, reg_irq_status);
-
 	raw_spin_lock_irqsave(&gpio->lock, flags);
 	copro = aspeed_gpio_copro_request(gpio, offset);
 
-	iowrite32(bit, status_addr);
+	gpio->config->llops->reg_bit_set(gpio, offset, reg_irq_status, 1);
 
 	if (copro)
 		aspeed_gpio_copro_release(gpio, offset);
@@ -544,20 +475,15 @@ static void aspeed_gpio_irq_ack(struct irq_data *d)
 
 static void aspeed_gpio_irq_set_mask(struct irq_data *d, bool set)
 {
-	const struct aspeed_gpio_bank *bank;
 	struct aspeed_gpio *gpio;
 	unsigned long flags;
-	u32 reg, bit;
-	void __iomem *addr;
 	int rc, offset;
-	bool copro;
+	bool copro = false;
 
-	rc = irqd_to_aspeed_gpio_data(d, &gpio, &bank, &bit, &offset);
+	rc = irqd_to_aspeed_gpio_data(d, &gpio, &offset);
 	if (rc)
 		return;
 
-	addr = bank_reg(gpio, bank, reg_irq_enable);
-
 	/* Unmasking the IRQ */
 	if (set)
 		gpiochip_enable_irq(&gpio->chip, irqd_to_hwirq(d));
@@ -565,12 +491,7 @@ static void aspeed_gpio_irq_set_mask(struct irq_data *d, bool set)
 	raw_spin_lock_irqsave(&gpio->lock, flags);
 	copro = aspeed_gpio_copro_request(gpio, offset);
 
-	reg = ioread32(addr);
-	if (set)
-		reg |= bit;
-	else
-		reg &= ~bit;
-	iowrite32(reg, addr);
+	gpio->config->llops->reg_bit_set(gpio, offset, reg_irq_enable, set);
 
 	if (copro)
 		aspeed_gpio_copro_release(gpio, offset);
@@ -596,34 +517,31 @@ static int aspeed_gpio_set_type(struct irq_data *d, unsigned int type)
 	u32 type0 = 0;
 	u32 type1 = 0;
 	u32 type2 = 0;
-	u32 bit, reg;
-	const struct aspeed_gpio_bank *bank;
 	irq_flow_handler_t handler;
 	struct aspeed_gpio *gpio;
 	unsigned long flags;
-	void __iomem *addr;
 	int rc, offset;
-	bool copro;
+	bool copro = false;
 
-	rc = irqd_to_aspeed_gpio_data(d, &gpio, &bank, &bit, &offset);
+	rc = irqd_to_aspeed_gpio_data(d, &gpio, &offset);
 	if (rc)
 		return -EINVAL;
 
 	switch (type & IRQ_TYPE_SENSE_MASK) {
 	case IRQ_TYPE_EDGE_BOTH:
-		type2 |= bit;
+		type2 = 1;
 		fallthrough;
 	case IRQ_TYPE_EDGE_RISING:
-		type0 |= bit;
+		type0 = 1;
 		fallthrough;
 	case IRQ_TYPE_EDGE_FALLING:
 		handler = handle_edge_irq;
 		break;
 	case IRQ_TYPE_LEVEL_HIGH:
-		type0 |= bit;
+		type0 = 1;
 		fallthrough;
 	case IRQ_TYPE_LEVEL_LOW:
-		type1 |= bit;
+		type1 = 1;
 		handler = handle_level_irq;
 		break;
 	default:
@@ -633,20 +551,9 @@ static int aspeed_gpio_set_type(struct irq_data *d, unsigned int type)
 	raw_spin_lock_irqsave(&gpio->lock, flags);
 	copro = aspeed_gpio_copro_request(gpio, offset);
 
-	addr = bank_reg(gpio, bank, reg_irq_type0);
-	reg = ioread32(addr);
-	reg = (reg & ~bit) | type0;
-	iowrite32(reg, addr);
-
-	addr = bank_reg(gpio, bank, reg_irq_type1);
-	reg = ioread32(addr);
-	reg = (reg & ~bit) | type1;
-	iowrite32(reg, addr);
-
-	addr = bank_reg(gpio, bank, reg_irq_type2);
-	reg = ioread32(addr);
-	reg = (reg & ~bit) | type2;
-	iowrite32(reg, addr);
+	gpio->config->llops->reg_bit_set(gpio, offset, reg_irq_type0, type0);
+	gpio->config->llops->reg_bit_set(gpio, offset, reg_irq_type1, type1);
+	gpio->config->llops->reg_bit_set(gpio, offset, reg_irq_type2, type2);
 
 	if (copro)
 		aspeed_gpio_copro_release(gpio, offset);
@@ -661,7 +568,6 @@ static void aspeed_gpio_irq_handler(struct irq_desc *desc)
 {
 	struct gpio_chip *gc = irq_desc_get_handler_data(desc);
 	struct irq_chip *ic = irq_desc_get_chip(desc);
-	struct aspeed_gpio *data = gpiochip_get_data(gc);
 	unsigned int i, p, banks;
 	unsigned long reg;
 	struct aspeed_gpio *gpio = gpiochip_get_data(gc);
@@ -670,9 +576,7 @@ static void aspeed_gpio_irq_handler(struct irq_desc *desc)
 
 	banks = DIV_ROUND_UP(gpio->chip.ngpio, 32);
 	for (i = 0; i < banks; i++) {
-		const struct aspeed_gpio_bank *bank = &aspeed_gpio_banks[i];
-
-		reg = ioread32(bank_reg(data, bank, reg_irq_status));
+		reg = gpio->config->llops->reg_bits_get(gpio, i * 32, reg_irq_status);
 
 		for_each_set_bit(p, &reg, 32)
 			generic_handle_domain_irq(gc->irq.domain, i * 32 + p);
@@ -711,23 +615,12 @@ 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);
+	bool copro = false;
 
 	raw_spin_lock_irqsave(&gpio->lock, flags);
 	copro = aspeed_gpio_copro_request(gpio, offset);
 
-	val = readl(treg);
-
-	if (enable)
-		val |= GPIO_BIT(offset);
-	else
-		val &= ~GPIO_BIT(offset);
-
-	writel(val, treg);
+	gpio->config->llops->reg_bit_set(gpio, offset, reg_tolerance, enable);
 
 	if (copro)
 		aspeed_gpio_copro_release(gpio, offset);
@@ -821,21 +714,11 @@ static inline bool timer_allocation_registered(struct aspeed_gpio *gpio,
 static void configure_timer(struct aspeed_gpio *gpio, unsigned int offset,
 		unsigned int timer)
 {
-	const struct aspeed_gpio_bank *bank = to_bank(offset);
-	const u32 mask = GPIO_BIT(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);
-
-	addr = bank_reg(gpio, bank, reg_debounce_sel2);
-	val = ioread32(addr);
-	iowrite32((val & ~mask) | GPIO_SET_DEBOUNCE2(timer, offset), addr);
+	gpio->config->llops->reg_bit_set(gpio, offset, reg_debounce_sel1, !!(timer & BIT(1)));
+	gpio->config->llops->reg_bit_set(gpio, offset, reg_debounce_sel2, !!(timer & BIT(0)));
 }
 
 static int enable_debounce(struct gpio_chip *chip, unsigned int offset,
@@ -866,15 +749,15 @@ static int enable_debounce(struct gpio_chip *chip, unsigned int offset,
 	}
 
 	/* Try to find a timer already configured for the debounce period */
-	for (i = 1; i < ARRAY_SIZE(debounce_timers); i++) {
+	for (i = 1; i < gpio->config->debounce_timers_num; i++) {
 		u32 cycles;
 
-		cycles = ioread32(gpio->base + debounce_timers[i]);
+		cycles = ioread32(gpio->base + gpio->config->debounce_timers_array[i]);
 		if (requested_cycles == cycles)
 			break;
 	}
 
-	if (i == ARRAY_SIZE(debounce_timers)) {
+	if (i == gpio->config->debounce_timers_num) {
 		int j;
 
 		/*
@@ -888,8 +771,8 @@ static int enable_debounce(struct gpio_chip *chip, unsigned int offset,
 
 		if (j == ARRAY_SIZE(gpio->timer_users)) {
 			dev_warn(chip->parent,
-					"Debounce timers exhausted, cannot debounce for period %luus\n",
-					usecs);
+				 "Debounce timers exhausted, cannot debounce for period %luus\n",
+				 usecs);
 
 			rc = -EPERM;
 
@@ -905,7 +788,7 @@ static int enable_debounce(struct gpio_chip *chip, unsigned int offset,
 
 		i = j;
 
-		iowrite32(requested_cycles, gpio->base + debounce_timers[i]);
+		iowrite32(requested_cycles, gpio->base + gpio->config->debounce_timers_array[i]);
 	}
 
 	if (WARN(i == 0, "Cannot register index of disabled timer\n")) {
@@ -1008,6 +891,9 @@ int aspeed_gpio_copro_grab_gpio(struct gpio_desc *desc,
 	const struct aspeed_gpio_bank *bank = to_bank(offset);
 	unsigned long flags;
 
+	if (!aspeed_gpio_support_copro(gpio))
+		return -EOPNOTSUPP;
+
 	if (!gpio->cf_copro_bankmap)
 		gpio->cf_copro_bankmap = kzalloc(gpio->chip.ngpio >> 3, GFP_KERNEL);
 	if (!gpio->cf_copro_bankmap)
@@ -1027,7 +913,7 @@ int aspeed_gpio_copro_grab_gpio(struct gpio_desc *desc,
 
 	/* Switch command source */
 	if (gpio->cf_copro_bankmap[bindex] == 1)
-		aspeed_gpio_change_cmd_source(gpio, bank, bindex,
+		aspeed_gpio_change_cmd_source(gpio, offset,
 					      GPIO_CMDSRC_COLDFIRE);
 
 	if (vreg_offset)
@@ -1051,9 +937,11 @@ 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 (!aspeed_gpio_support_copro(gpio))
+		return -EOPNOTSUPP;
+
 	if (!gpio->cf_copro_bankmap)
 		return -ENXIO;
 
@@ -1072,7 +960,7 @@ int aspeed_gpio_copro_release_gpio(struct gpio_desc *desc)
 
 	/* Switch command source */
 	if (gpio->cf_copro_bankmap[bindex] == 0)
-		aspeed_gpio_change_cmd_source(gpio, bank, bindex,
+		aspeed_gpio_change_cmd_source(gpio, offset,
 					      GPIO_CMDSRC_ARM);
  bail:
 	raw_spin_unlock_irqrestore(&gpio->lock, flags);
@@ -1082,12 +970,10 @@ EXPORT_SYMBOL_GPL(aspeed_gpio_copro_release_gpio);
 
 static void aspeed_gpio_irq_print_chip(struct irq_data *d, struct seq_file *p)
 {
-	const struct aspeed_gpio_bank *bank;
 	struct aspeed_gpio *gpio;
-	u32 bit;
 	int rc, offset;
 
-	rc = irqd_to_aspeed_gpio_data(d, &gpio, &bank, &bit, &offset);
+	rc = irqd_to_aspeed_gpio_data(d, &gpio, &offset);
 	if (rc)
 		return;
 
@@ -1104,6 +990,111 @@ static const struct irq_chip aspeed_gpio_irq_chip = {
 	GPIOCHIP_IRQ_RESOURCE_HELPERS,
 };
 
+static void aspeed_g4_reg_bit_set(struct aspeed_gpio *gpio, unsigned int offset,
+				  const enum aspeed_gpio_reg reg, bool val)
+{
+	const struct aspeed_gpio_bank *bank = to_bank(offset);
+	void __iomem *addr = bank_reg(gpio, bank, reg);
+	u32 temp;
+
+	if (reg == reg_val && gpio->config->dcache_require)
+		temp = gpio->dcache[GPIO_BANK(offset)];
+	else
+		temp = ioread32(addr);
+
+	if (val)
+		temp |= GPIO_BIT(offset);
+	else
+		temp &= ~GPIO_BIT(offset);
+
+	if (reg == reg_val && gpio->config->dcache_require)
+		gpio->dcache[GPIO_BANK(offset)] = temp;
+	iowrite32(temp, addr);
+}
+
+static u32 aspeed_g4_reg_bits_get(struct aspeed_gpio *gpio, unsigned int offset,
+				  const enum aspeed_gpio_reg reg)
+{
+	const struct aspeed_gpio_bank *bank = to_bank(offset);
+	void __iomem *addr = bank_reg(gpio, bank, reg);
+
+	if (reg == reg_rdata || reg == reg_irq_status)
+		return ioread32(addr);
+	return !!(ioread32(addr) & GPIO_BIT(offset));
+}
+
+static bool aspeed_g4_copro_request(struct aspeed_gpio *gpio, unsigned int 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, offset, GPIO_CMDSRC_ARM);
+
+	if (gpio->config->dcache_require)
+		/* Update cache */
+		gpio->dcache[GPIO_BANK(offset)] =
+			gpio->config->llops->reg_bits_get(gpio, offset, reg_rdata);
+
+	return true;
+}
+
+static void aspeed_g4_copro_release(struct aspeed_gpio *gpio, unsigned int 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, offset, GPIO_CMDSRC_COLDFIRE);
+
+	/* Restart the coprocessor */
+	copro_ops->release_access(copro_data);
+}
+
+static void aspeed_g4_privilege_ctrl(struct aspeed_gpio *gpio, unsigned int offset, int cmdsrc)
+{
+	/*
+	 * The command source register is only valid in bits 0, 8, 16, and 24, so we use
+	 * (offset & ~(0x7)) to ensure that reg_bits_set always targets a valid bit.
+	 */
+	/* Source 1 first to avoid illegal 11 combination */
+	gpio->config->llops->reg_bit_set(gpio, offset & ~(0x7), reg_cmdsrc1, !!(cmdsrc & BIT(1)));
+	/* Then Source 0 */
+	gpio->config->llops->reg_bit_set(gpio, offset & ~(0x7), reg_cmdsrc0, !!(cmdsrc & BIT(0)));
+}
+
+static void aspeed_g4_privilege_init(struct aspeed_gpio *gpio)
+{
+	u32 i;
+
+	/* Switch all command sources to the ARM by default */
+	for (i = 0; i < DIV_ROUND_UP(gpio->chip.ngpio, 32); i++) {
+		aspeed_gpio_change_cmd_source(gpio, (i << 5) + 0, GPIO_CMDSRC_ARM);
+		aspeed_gpio_change_cmd_source(gpio, (i << 5) + 8, GPIO_CMDSRC_ARM);
+		aspeed_gpio_change_cmd_source(gpio, (i << 5) + 16, GPIO_CMDSRC_ARM);
+		aspeed_gpio_change_cmd_source(gpio, (i << 5) + 24, GPIO_CMDSRC_ARM);
+	}
+}
+
+static const struct aspeed_gpio_llops aspeed_g4_llops = {
+	.copro_request = aspeed_g4_copro_request,
+	.copro_release = aspeed_g4_copro_release,
+	.reg_bit_set = aspeed_g4_reg_bit_set,
+	.reg_bits_get = aspeed_g4_reg_bits_get,
+	.privilege_ctrl = aspeed_g4_privilege_ctrl,
+	.privilege_init = aspeed_g4_privilege_init,
+};
 /*
  * Any banks not specified in a struct aspeed_bank_props array are assumed to
  * have the properties:
@@ -1120,7 +1111,14 @@ static const struct aspeed_bank_props ast2400_bank_props[] = {
 
 static const struct aspeed_gpio_config ast2400_config =
 	/* 220 for simplicity, really 216 with two 4-GPIO holes, four at end */
-	{ .nr_gpios = 220, .props = ast2400_bank_props, };
+	{
+		.nr_gpios = 220,
+		.props = ast2400_bank_props,
+		.llops = &aspeed_g4_llops,
+		.debounce_timers_array = debounce_timers,
+		.debounce_timers_num = ARRAY_SIZE(debounce_timers),
+		.dcache_require = true,
+	};
 
 static const struct aspeed_bank_props ast2500_bank_props[] = {
 	/*     input	  output   */
@@ -1132,7 +1130,14 @@ static const struct aspeed_bank_props ast2500_bank_props[] = {
 
 static const struct aspeed_gpio_config ast2500_config =
 	/* 232 for simplicity, actual number is 228 (4-GPIO hole in GPIOAB) */
-	{ .nr_gpios = 232, .props = ast2500_bank_props, };
+	{
+		.nr_gpios = 232,
+		.props = ast2500_bank_props,
+		.llops = &aspeed_g4_llops,
+		.debounce_timers_array = debounce_timers,
+		.debounce_timers_num = ARRAY_SIZE(debounce_timers),
+		.dcache_require = true,
+	};
 
 static const struct aspeed_bank_props ast2600_bank_props[] = {
 	/*     input	  output   */
@@ -1148,7 +1153,14 @@ static const struct aspeed_gpio_config ast2600_config =
 	 * We expect ngpio being set in the device tree and this is a fallback
 	 * option.
 	 */
-	{ .nr_gpios = 208, .props = ast2600_bank_props, };
+	{
+		.nr_gpios = 208,
+		.props = ast2600_bank_props,
+		.llops = &aspeed_g4_llops,
+		.debounce_timers_array = debounce_timers,
+		.debounce_timers_num = ARRAY_SIZE(debounce_timers),
+		.dcache_require = true,
+	};
 
 static const struct of_device_id aspeed_gpio_of_table[] = {
 	{ .compatible = "aspeed,ast2400-gpio", .data = &ast2400_config, },
@@ -1191,6 +1203,9 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
 
 	gpio->config = gpio_id->data;
 
+	if (!gpio->config->llops->reg_bit_set || !gpio->config->llops->reg_bits_get)
+		return -EINVAL;
+
 	gpio->chip.parent = &pdev->dev;
 	err = of_property_read_u32(pdev->dev.of_node, "ngpios", &ngpio);
 	gpio->chip.ngpio = (u16) ngpio;
@@ -1207,27 +1222,23 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
 	gpio->chip.label = dev_name(&pdev->dev);
 	gpio->chip.base = -1;
 
-	/* Allocate a cache of the output registers */
-	banks = DIV_ROUND_UP(gpio->chip.ngpio, 32);
-	gpio->dcache = devm_kcalloc(&pdev->dev,
-				    banks, sizeof(u32), GFP_KERNEL);
-	if (!gpio->dcache)
-		return -ENOMEM;
-
-	/*
-	 * 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++) {
-		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);
+	if (gpio->config->dcache_require) {
+		/* Allocate a cache of the output registers */
+		banks = DIV_ROUND_UP(gpio->chip.ngpio, 32);
+		gpio->dcache = devm_kcalloc(&pdev->dev, banks, sizeof(u32), GFP_KERNEL);
+		if (!gpio->dcache)
+			return -ENOMEM;
+		/*
+		 * Populate it with initial values read from the HW
+		 */
+		for (i = 0; i < banks; i++)
+			gpio->dcache[i] =
+				gpio->config->llops->reg_bits_get(gpio, (i << 5), reg_rdata);
 	}
 
+	if (gpio->config->llops->privilege_init)
+		gpio->config->llops->privilege_init(gpio);
+
 	/* Set up an irqchip */
 	irq = platform_get_irq(pdev, 0);
 	if (irq < 0)
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v4 4/6] gpio: aspeed: Support G7 Aspeed gpio controller
  2024-09-19  9:43 [PATCH v4 0/6] Add Aspeed G7 gpio support Billy Tsai
                   ` (2 preceding siblings ...)
  2024-09-19  9:43 ` [PATCH v4 3/6] gpio: aspeed: Create llops to handle hardware access Billy Tsai
@ 2024-09-19  9:43 ` Billy Tsai
  2024-09-19  9:43 ` [PATCH v4 5/6] gpio: aspeed: Change the macro to support deferred probe Billy Tsai
  2024-09-19  9:43 ` [PATCH v4 6/6] gpio: aspeed: Add the flush write to ensure the write complete Billy Tsai
  5 siblings, 0 replies; 16+ messages in thread
From: Billy Tsai @ 2024-09-19  9:43 UTC (permalink / raw)
  To: linus.walleij, brgl, robh, krzk+dt, conor+dt, joel, andrew,
	linux-gpio, devicetree, linux-arm-kernel, linux-aspeed,
	linux-kernel, BMC-SW, Peter.Yin, Jay_Zhang

In the 7th generation of the SoC from Aspeed, the control logic of the
GPIO controller has been updated to support per-pin control. Each pin now
has its own 32-bit register, allowing for individual control of the pin’s
value, direction, interrupt type, and other settings. The permission for
coprocessor access is supported by the hardware but hasn’t been
implemented in the current patch.

Signed-off-by: Billy Tsai <billy_tsai@aspeedtech.com>
---
 drivers/gpio/gpio-aspeed.c | 112 +++++++++++++++++++++++++++++++++++++
 1 file changed, 112 insertions(+)

diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
index 8b334ce7b60a..060c0225cb99 100644
--- a/drivers/gpio/gpio-aspeed.c
+++ b/drivers/gpio/gpio-aspeed.c
@@ -30,6 +30,23 @@
 #include <linux/gpio/consumer.h>
 #include "gpiolib.h"
 
+#define GPIO_G7_IRQ_STS_BASE 0x100
+#define GPIO_G7_IRQ_STS_OFFSET(x) (GPIO_G7_IRQ_STS_BASE + (x) * 0x4)
+#define GPIO_G7_CTRL_REG_BASE 0x180
+#define GPIO_G7_CTRL_REG_OFFSET(x) (GPIO_G7_CTRL_REG_BASE + (x) * 0x4)
+#define GPIO_G7_CTRL_OUT_DATA BIT(0)
+#define GPIO_G7_CTRL_DIR BIT(1)
+#define GPIO_G7_CTRL_IRQ_EN BIT(2)
+#define GPIO_G7_CTRL_IRQ_TYPE0 BIT(3)
+#define GPIO_G7_CTRL_IRQ_TYPE1 BIT(4)
+#define GPIO_G7_CTRL_IRQ_TYPE2 BIT(5)
+#define GPIO_G7_CTRL_RST_TOLERANCE BIT(6)
+#define GPIO_G7_CTRL_DEBOUNCE_SEL2 BIT(7)
+#define GPIO_G7_CTRL_DEBOUNCE_SEL1 BIT(8)
+#define GPIO_G7_CTRL_INPUT_MASK BIT(9)
+#define GPIO_G7_CTRL_IRQ_STS BIT(12)
+#define GPIO_G7_CTRL_IN_DATA BIT(13)
+
 struct aspeed_bank_props {
 	unsigned int bank;
 	u32 input;
@@ -95,6 +112,7 @@ struct aspeed_gpio_bank {
  */
 
 static const int debounce_timers[4] = { 0x00, 0x50, 0x54, 0x58 };
+static const int g7_debounce_timers[4] = { 0x00, 0x04, 0x00, 0x08 };
 
 static const struct aspeed_gpio_copro_ops *copro_ops;
 static void *copro_data;
@@ -248,6 +266,39 @@ static inline void __iomem *bank_reg(struct aspeed_gpio *gpio,
 	BUG();
 }
 
+static inline u32 reg_mask(const enum aspeed_gpio_reg reg)
+{
+	switch (reg) {
+	case reg_val:
+		return GPIO_G7_CTRL_OUT_DATA;
+	case reg_dir:
+		return GPIO_G7_CTRL_DIR;
+	case reg_irq_enable:
+		return GPIO_G7_CTRL_IRQ_EN;
+	case reg_irq_type0:
+		return GPIO_G7_CTRL_IRQ_TYPE0;
+	case reg_irq_type1:
+		return GPIO_G7_CTRL_IRQ_TYPE1;
+	case reg_irq_type2:
+		return GPIO_G7_CTRL_IRQ_TYPE2;
+	case reg_tolerance:
+		return GPIO_G7_CTRL_RST_TOLERANCE;
+	case reg_debounce_sel1:
+		return GPIO_G7_CTRL_DEBOUNCE_SEL1;
+	case reg_debounce_sel2:
+		return GPIO_G7_CTRL_DEBOUNCE_SEL2;
+	case reg_rdata:
+		return GPIO_G7_CTRL_OUT_DATA;
+	case reg_irq_status:
+		return GPIO_G7_CTRL_IRQ_STS;
+	case reg_cmdsrc0:
+	case reg_cmdsrc1:
+	default:
+		WARN_ON_ONCE(1);
+		return 0;
+	}
+}
+
 #define GPIO_BANK(x)	((x) >> 5)
 #define GPIO_OFFSET(x)	((x) & 0x1f)
 #define GPIO_BIT(x)	BIT(GPIO_OFFSET(x))
@@ -1095,6 +1146,43 @@ static const struct aspeed_gpio_llops aspeed_g4_llops = {
 	.privilege_ctrl = aspeed_g4_privilege_ctrl,
 	.privilege_init = aspeed_g4_privilege_init,
 };
+
+static void aspeed_g7_reg_bit_set(struct aspeed_gpio *gpio, unsigned int offset,
+				  const enum aspeed_gpio_reg reg, bool val)
+{
+	u32 mask = reg_mask(reg);
+	void __iomem *addr = gpio->base + GPIO_G7_CTRL_REG_OFFSET(offset);
+	u32 write_val = (ioread32(addr) & ~(mask)) | (((val) << (ffs(mask) - 1)) & (mask));
+
+	iowrite32(write_val, addr);
+}
+
+static u32 aspeed_g7_reg_bits_get(struct aspeed_gpio *gpio, unsigned int offset,
+				  const enum aspeed_gpio_reg reg)
+{
+	u32 mask = reg_mask(reg);
+	void __iomem *addr;
+
+	if (reg == reg_irq_status) {
+		addr = gpio->base + GPIO_G7_IRQ_STS_OFFSET(offset >> 5);
+		return ioread32(addr);
+	}
+	addr = gpio->base + GPIO_G7_CTRL_REG_OFFSET(offset);
+	if (reg == reg_val)
+		mask = GPIO_G7_CTRL_IN_DATA;
+
+	return (((ioread32(addr)) & (mask)) >> (ffs(mask) - 1));
+}
+
+static const struct aspeed_gpio_llops aspeed_g7_llops = {
+	.copro_request = NULL,
+	.copro_release = NULL,
+	.reg_bit_set = aspeed_g7_reg_bit_set,
+	.reg_bits_get = aspeed_g7_reg_bits_get,
+	.privilege_ctrl = NULL,
+	.privilege_init = NULL,
+};
+
 /*
  * Any banks not specified in a struct aspeed_bank_props array are assumed to
  * have the properties:
@@ -1162,10 +1250,34 @@ static const struct aspeed_gpio_config ast2600_config =
 		.dcache_require = true,
 	};
 
+static const struct aspeed_bank_props ast2700_bank_props[] = {
+	/*     input	  output   */
+	{ 1, 0x0fffffff, 0x0fffffff }, /* E/F/G/H, 4-GPIO hole */
+	{ 6, 0x00ffffff, 0x00ff0000 }, /* Y/Z/AA */
+	{},
+};
+
+static const struct aspeed_gpio_config ast2700_config =
+	/*
+	 * ast2700 has two controllers one with 212 GPIOs and one with 16 GPIOs.
+	 * 216 for simplicity, actual number is 212 (4-GPIO hole in GPIOH)
+	 * We expect ngpio being set in the device tree and this is a fallback
+	 * option.
+	 */
+	{
+		.nr_gpios = 216,
+		.props = ast2700_bank_props,
+		.llops = &aspeed_g7_llops,
+		.debounce_timers_array = g7_debounce_timers,
+		.debounce_timers_num = ARRAY_SIZE(g7_debounce_timers),
+		.dcache_require = false,
+	};
+
 static const struct of_device_id aspeed_gpio_of_table[] = {
 	{ .compatible = "aspeed,ast2400-gpio", .data = &ast2400_config, },
 	{ .compatible = "aspeed,ast2500-gpio", .data = &ast2500_config, },
 	{ .compatible = "aspeed,ast2600-gpio", .data = &ast2600_config, },
+	{ .compatible = "aspeed,ast2700-gpio", .data = &ast2700_config, },
 	{}
 };
 MODULE_DEVICE_TABLE(of, aspeed_gpio_of_table);
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v4 5/6] gpio: aspeed: Change the macro to support deferred probe
  2024-09-19  9:43 [PATCH v4 0/6] Add Aspeed G7 gpio support Billy Tsai
                   ` (3 preceding siblings ...)
  2024-09-19  9:43 ` [PATCH v4 4/6] gpio: aspeed: Support G7 Aspeed gpio controller Billy Tsai
@ 2024-09-19  9:43 ` Billy Tsai
  2024-09-19  9:43 ` [PATCH v4 6/6] gpio: aspeed: Add the flush write to ensure the write complete Billy Tsai
  5 siblings, 0 replies; 16+ messages in thread
From: Billy Tsai @ 2024-09-19  9:43 UTC (permalink / raw)
  To: linus.walleij, brgl, robh, krzk+dt, conor+dt, joel, andrew,
	linux-gpio, devicetree, linux-arm-kernel, linux-aspeed,
	linux-kernel, BMC-SW, Peter.Yin, Jay_Zhang

Use module_platform_driver() to replace module_platform_driver_probe().
The former utilizes platform_driver_register(), which allows the driver to
defer probing when it doesn't acquire the necessary resources due to probe
order. In contrast, the latter uses __platform_driver_probe(), which
includes the comment "Note that this is incompatible with deferred
probing." Since our GPIO driver requires access to the clock resource, the
former is more suitable.

Signed-off-by: Billy Tsai <billy_tsai@aspeedtech.com>
---
 drivers/gpio/gpio-aspeed.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
index 060c0225cb99..c811e84db0b9 100644
--- a/drivers/gpio/gpio-aspeed.c
+++ b/drivers/gpio/gpio-aspeed.c
@@ -1282,7 +1282,7 @@ static const struct of_device_id aspeed_gpio_of_table[] = {
 };
 MODULE_DEVICE_TABLE(of, aspeed_gpio_of_table);
 
-static int __init aspeed_gpio_probe(struct platform_device *pdev)
+static int aspeed_gpio_probe(struct platform_device *pdev)
 {
 	const struct of_device_id *gpio_id;
 	struct gpio_irq_chip *girq;
@@ -1382,13 +1382,14 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
 }
 
 static struct platform_driver aspeed_gpio_driver = {
+	.probe = aspeed_gpio_probe,
 	.driver = {
 		.name = KBUILD_MODNAME,
 		.of_match_table = aspeed_gpio_of_table,
 	},
 };
 
-module_platform_driver_probe(aspeed_gpio_driver, aspeed_gpio_probe);
+module_platform_driver(aspeed_gpio_driver);
 
 MODULE_DESCRIPTION("Aspeed GPIO Driver");
 MODULE_LICENSE("GPL");
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v4 6/6] gpio: aspeed: Add the flush write to ensure the write complete.
  2024-09-19  9:43 [PATCH v4 0/6] Add Aspeed G7 gpio support Billy Tsai
                   ` (4 preceding siblings ...)
  2024-09-19  9:43 ` [PATCH v4 5/6] gpio: aspeed: Change the macro to support deferred probe Billy Tsai
@ 2024-09-19  9:43 ` Billy Tsai
  2024-10-01 14:18   ` Linus Walleij
  5 siblings, 1 reply; 16+ messages in thread
From: Billy Tsai @ 2024-09-19  9:43 UTC (permalink / raw)
  To: linus.walleij, brgl, robh, krzk+dt, conor+dt, joel, andrew,
	linux-gpio, devicetree, linux-arm-kernel, linux-aspeed,
	linux-kernel, BMC-SW, Peter.Yin, Jay_Zhang

Performing a dummy read ensures that the register write operation is fully
completed, mitigating any potential bus delays that could otherwise impact
the frequency of bitbang usage. E.g., if the JTAG application uses GPIO to
control the JTAG pins (TCK, TMS, TDI, TDO, and TRST), and the application
sets the TCK clock to 1 MHz, the GPIO’s high/low transitions will rely on
a delay function to ensure the clock frequency does not exceed 1 MHz.
However, this can lead to rapid toggling of the GPIO because the write
operation is POSTed and does not wait for a bus acknowledgment.

Signed-off-by: Billy Tsai <billy_tsai@aspeedtech.com>
---
 drivers/gpio/gpio-aspeed.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
index c811e84db0b9..daa12e21d946 100644
--- a/drivers/gpio/gpio-aspeed.c
+++ b/drivers/gpio/gpio-aspeed.c
@@ -400,6 +400,8 @@ static void __aspeed_gpio_set(struct gpio_chip *gc, unsigned int offset,
 	struct aspeed_gpio *gpio = gpiochip_get_data(gc);
 
 	gpio->config->llops->reg_bit_set(gpio, offset, reg_val, val);
+	// flush write
+	gpio->config->llops->reg_bits_get(gpio, offset, reg_val);
 }
 
 static void aspeed_gpio_set(struct gpio_chip *gc, unsigned int offset,
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH v4 3/6] gpio: aspeed: Create llops to handle hardware access
  2024-09-19  9:43 ` [PATCH v4 3/6] gpio: aspeed: Create llops to handle hardware access Billy Tsai
@ 2024-09-20  2:56   ` Andrew Jeffery
  2024-09-20  9:19     ` Billy Tsai
  0 siblings, 1 reply; 16+ messages in thread
From: Andrew Jeffery @ 2024-09-20  2:56 UTC (permalink / raw)
  To: Billy Tsai, linus.walleij, brgl, robh, krzk+dt, conor+dt, joel,
	linux-gpio, devicetree, linux-arm-kernel, linux-aspeed,
	linux-kernel, BMC-SW, Peter.Yin, Jay_Zhang

Hi Billy

On Thu, 2024-09-19 at 17:43 +0800, Billy Tsai wrote:
> Add low-level operations (llops) to abstract the register access for GPIO
> registers and the coprocessor request/release. With this abstraction
> layer, the driver can separate the hardware and software logic, making it
> easier to extend the driver to support different hardware register
> layouts.
> 
> Signed-off-by: Billy Tsai <billy_tsai@aspeedtech.com>
> ---
>  drivers/gpio/gpio-aspeed.c | 429 +++++++++++++++++++------------------
>  1 file changed, 220 insertions(+), 209 deletions(-)
> 
> diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
> index d20e15b2079d..8b334ce7b60a 100644
> --- a/drivers/gpio/gpio-aspeed.c
> +++ b/drivers/gpio/gpio-aspeed.c
> @@ -39,6 +39,10 @@ struct aspeed_bank_props {
>  struct aspeed_gpio_config {
>  	unsigned int nr_gpios;
>  	const struct aspeed_bank_props *props;
> +	const struct aspeed_gpio_llops *llops;
> +	const int *debounce_timers_array;
> +	int debounce_timers_num;
> +	bool dcache_require;

Bit of a nitpick, but if we must have it I'd prefer we call this
`require_dcache`.

>  
> +static void aspeed_g4_reg_bit_set(struct aspeed_gpio *gpio, unsigned int offset,
> +				  const enum aspeed_gpio_reg reg, bool val)
> +{
> +	const struct aspeed_gpio_bank *bank = to_bank(offset);
> +	void __iomem *addr = bank_reg(gpio, bank, reg);
> +	u32 temp;
> +
> +	if (reg == reg_val && gpio->config->dcache_require)

We know gpio->config->dcache_require will be true, because this is the
g4 handler, right?

> +		temp = gpio->dcache[GPIO_BANK(offset)];
> +	else
> +		temp = ioread32(addr);
> +
> +	if (val)
> +		temp |= GPIO_BIT(offset);
> +	else
> +		temp &= ~GPIO_BIT(offset);
> +
> +	if (reg == reg_val && gpio->config->dcache_require)
> +		gpio->dcache[GPIO_BANK(offset)] = temp;
> +	iowrite32(temp, addr);
> +}
> +
> +static u32 aspeed_g4_reg_bits_get(struct aspeed_gpio *gpio, unsigned int offset,
> +				  const enum aspeed_gpio_reg reg)
> +{
> +	const struct aspeed_gpio_bank *bank = to_bank(offset);
> +	void __iomem *addr = bank_reg(gpio, bank, reg);
> +
> +	if (reg == reg_rdata || reg == reg_irq_status)
> +		return ioread32(addr);
> +	return !!(ioread32(addr) & GPIO_BIT(offset));

Okay, the semantics here feel a bit concerning. I think we need one
behaviour or the other, not both.

Perhaps we have two callbacks:

1. get_bit()
2. get_bank()

where get_bank() is only defined for reg_rdata and reg_irq_status, and
get_bit() for all registers.

> +}
> +
> +static bool aspeed_g4_copro_request(struct aspeed_gpio *gpio, unsigned int 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, offset, GPIO_CMDSRC_ARM);

I don't think we need the indirection here, this is already a g4-
specific callback implementation, we can directly call
aspeed_g4_privilege_ctrl().

> +
> +	if (gpio->config->dcache_require)
> +		/* Update cache */
> +		gpio->dcache[GPIO_BANK(offset)] =
> +			gpio->config->llops->reg_bits_get(gpio, offset, reg_rdata);
> +
> +	return true;
> +}
> +
> +static void aspeed_g4_copro_release(struct aspeed_gpio *gpio, unsigned int 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, offset, GPIO_CMDSRC_COLDFIRE);

As above for the request implementation, we can call
aspeed_g4_privilege_ctrl() directly here.

> +
> +	/* Restart the coprocessor */
> +	copro_ops->release_access(copro_data);
> +}
> +
> +static void aspeed_g4_privilege_ctrl(struct aspeed_gpio *gpio, unsigned int offset, int cmdsrc)
> +{
> +	/*
> +	 * The command source register is only valid in bits 0, 8, 16, and 24, so we use
> +	 * (offset & ~(0x7)) to ensure that reg_bits_set always targets a valid bit.
> +	 */
> +	/* Source 1 first to avoid illegal 11 combination */
> +	gpio->config->llops->reg_bit_set(gpio, offset & ~(0x7), reg_cmdsrc1, !!(cmdsrc & BIT(1)));
> +	/* Then Source 0 */
> +	gpio->config->llops->reg_bit_set(gpio, offset & ~(0x7), reg_cmdsrc0, !!(cmdsrc & BIT(0)));

Both of these can be direct calls to aspeed_g4_reg_bit_set().

> +}
> +
> +static void aspeed_g4_privilege_init(struct aspeed_gpio *gpio)
> +{
> +	u32 i;
> +
> +	/* Switch all command sources to the ARM by default */
> +	for (i = 0; i < DIV_ROUND_UP(gpio->chip.ngpio, 32); i++) {
> +		aspeed_gpio_change_cmd_source(gpio, (i << 5) + 0, GPIO_CMDSRC_ARM);
> +		aspeed_gpio_change_cmd_source(gpio, (i << 5) + 8, GPIO_CMDSRC_ARM);
> +		aspeed_gpio_change_cmd_source(gpio, (i << 5) + 16, GPIO_CMDSRC_ARM);
> +		aspeed_gpio_change_cmd_source(gpio, (i << 5) + 24, GPIO_CMDSRC_ARM);

Again as this is a g4-specific callback we can directly call
aspeed_g4_privilege_ctrl().

> +	}
> +}
> +
> +static const struct aspeed_gpio_llops aspeed_g4_llops = {
> +	.copro_request = aspeed_g4_copro_request,
> +	.copro_release = aspeed_g4_copro_release,
> +	.reg_bit_set = aspeed_g4_reg_bit_set,
> +	.reg_bits_get = aspeed_g4_reg_bits_get,
> +	.privilege_ctrl = aspeed_g4_privilege_ctrl,
> +	.privilege_init = aspeed_g4_privilege_init,
> +};
>  /*
>   * Any banks not specified in a struct aspeed_bank_props array are assumed to
>   * have the properties:
> @@ -1120,7 +1111,14 @@ static const struct aspeed_bank_props ast2400_bank_props[] = {
>  
>  static const struct aspeed_gpio_config ast2400_config =
>  	/* 220 for simplicity, really 216 with two 4-GPIO holes, four at end */
> -	{ .nr_gpios = 220, .props = ast2400_bank_props, };
> +	{
> +		.nr_gpios = 220,
> +		.props = ast2400_bank_props,
> +		.llops = &aspeed_g4_llops,
> +		.debounce_timers_array = debounce_timers,
> +		.debounce_timers_num = ARRAY_SIZE(debounce_timers),
> +		.dcache_require = true,
> +	};
>  
>  static const struct aspeed_bank_props ast2500_bank_props[] = {
>  	/*     input	  output   */
> @@ -1132,7 +1130,14 @@ static const struct aspeed_bank_props ast2500_bank_props[] = {
>  
>  static const struct aspeed_gpio_config ast2500_config =
>  	/* 232 for simplicity, actual number is 228 (4-GPIO hole in GPIOAB) */
> -	{ .nr_gpios = 232, .props = ast2500_bank_props, };
> +	{
> +		.nr_gpios = 232,
> +		.props = ast2500_bank_props,
> +		.llops = &aspeed_g4_llops,
> +		.debounce_timers_array = debounce_timers,
> +		.debounce_timers_num = ARRAY_SIZE(debounce_timers),
> +		.dcache_require = true,
> +	};
>  
>  static const struct aspeed_bank_props ast2600_bank_props[] = {
>  	/*     input	  output   */
> @@ -1148,7 +1153,14 @@ static const struct aspeed_gpio_config ast2600_config =
>  	 * We expect ngpio being set in the device tree and this is a fallback
>  	 * option.
>  	 */
> -	{ .nr_gpios = 208, .props = ast2600_bank_props, };
> +	{
> +		.nr_gpios = 208,
> +		.props = ast2600_bank_props,
> +		.llops = &aspeed_g4_llops,
> +		.debounce_timers_array = debounce_timers,
> +		.debounce_timers_num = ARRAY_SIZE(debounce_timers),
> +		.dcache_require = true,
> +	};
>  
>  static const struct of_device_id aspeed_gpio_of_table[] = {
>  	{ .compatible = "aspeed,ast2400-gpio", .data = &ast2400_config, },
> @@ -1191,6 +1203,9 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
>  
>  	gpio->config = gpio_id->data;
>  
> +	if (!gpio->config->llops->reg_bit_set || !gpio->config->llops->reg_bits_get)
> +		return -EINVAL;
> +

This will need to clean up gpio->clk. Perhaps you could move it above
the of_clk_get() call instead?

However, looking through the rest it seems we have a few issues with
this leak :/

>  	gpio->chip.parent = &pdev->dev;
>  	err = of_property_read_u32(pdev->dev.of_node, "ngpios", &ngpio);
>  	gpio->chip.ngpio = (u16) ngpio;
> @@ -1207,27 +1222,23 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
>  	gpio->chip.label = dev_name(&pdev->dev);
>  	gpio->chip.base = -1;
>  
> -	/* Allocate a cache of the output registers */
> -	banks = DIV_ROUND_UP(gpio->chip.ngpio, 32);
> -	gpio->dcache = devm_kcalloc(&pdev->dev,
> -				    banks, sizeof(u32), GFP_KERNEL);
> -	if (!gpio->dcache)
> -		return -ENOMEM;
> -
> -	/*
> -	 * 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++) {
> -		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);
> +	if (gpio->config->dcache_require) {
> +		/* Allocate a cache of the output registers */
> +		banks = DIV_ROUND_UP(gpio->chip.ngpio, 32);
> +		gpio->dcache = devm_kcalloc(&pdev->dev, banks, sizeof(u32), GFP_KERNEL);
> +		if (!gpio->dcache)
> +			return -ENOMEM;

This should also clean up gpio->clk. I don't think you can move it to
avoid that.

Andrew

> +		/*
> +		 * Populate it with initial values read from the HW
> +		 */
> +		for (i = 0; i < banks; i++)
> +			gpio->dcache[i] =
> +				gpio->config->llops->reg_bits_get(gpio, (i << 5), reg_rdata);
>  	}
>  
> +	if (gpio->config->llops->privilege_init)
> +		gpio->config->llops->privilege_init(gpio);
> +
>  	/* Set up an irqchip */
>  	irq = platform_get_irq(pdev, 0);
>  	if (irq < 0)



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4 3/6] gpio: aspeed: Create llops to handle hardware access
  2024-09-20  2:56   ` Andrew Jeffery
@ 2024-09-20  9:19     ` Billy Tsai
  2024-09-23  0:34       ` Andrew Jeffery
  0 siblings, 1 reply; 16+ messages in thread
From: Billy Tsai @ 2024-09-20  9:19 UTC (permalink / raw)
  To: Andrew Jeffery, linus.walleij@linaro.org, brgl@bgdev.pl,
	robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
	joel@jms.id.au, linux-gpio@vger.kernel.org,
	devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
	linux-aspeed@lists.ozlabs.org, linux-kernel@vger.kernel.org,
	BMC-SW, Peter.Yin@quantatw.com, Jay_Zhang@wiwynn.com

> > Add low-level operations (llops) to abstract the register access for GPIO
> > registers and the coprocessor request/release. With this abstraction
> > layer, the driver can separate the hardware and software logic, making it
> > easier to extend the driver to support different hardware register
> > layouts.
> >
> > Signed-off-by: Billy Tsai <billy_tsai@aspeedtech.com>
> > ---
> >  drivers/gpio/gpio-aspeed.c | 429 +++++++++++++++++++------------------
> >  1 file changed, 220 insertions(+), 209 deletions(-)
> >
> > diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
> > index d20e15b2079d..8b334ce7b60a 100644
> > --- a/drivers/gpio/gpio-aspeed.c
> > +++ b/drivers/gpio/gpio-aspeed.c
> > @@ -39,6 +39,10 @@ struct aspeed_bank_props {
> >  struct aspeed_gpio_config {
> >       unsigned int nr_gpios;
> >       const struct aspeed_bank_props *props;
> > +     const struct aspeed_gpio_llops *llops;
> > +     const int *debounce_timers_array;
> > +     int debounce_timers_num;
> > +     bool dcache_require;

> Bit of a nitpick, but if we must have it I'd prefer we call this
> `require_dcache`.

Okay.

> >
> > +static void aspeed_g4_reg_bit_set(struct aspeed_gpio *gpio, unsigned int offset,
> > +                               const enum aspeed_gpio_reg reg, bool val)
> > +{
> > +     const struct aspeed_gpio_bank *bank = to_bank(offset);
> > +     void __iomem *addr = bank_reg(gpio, bank, reg);
> > +     u32 temp;
> > +
> > +     if (reg == reg_val && gpio->config->dcache_require)

> We know gpio->config->dcache_require will be true, because this is the
> g4 handler, right?

Yes. I will remove this unnecessary check.

> > +             temp = gpio->dcache[GPIO_BANK(offset)];
> > +     else
> > +             temp = ioread32(addr);
> > +
> > +     if (val)
> > +             temp |= GPIO_BIT(offset);
> > +     else
> > +             temp &= ~GPIO_BIT(offset);
> > +
> > +     if (reg == reg_val && gpio->config->dcache_require)
> > +             gpio->dcache[GPIO_BANK(offset)] = temp;
> > +     iowrite32(temp, addr);
> > +}
> > +
> > +static u32 aspeed_g4_reg_bits_get(struct aspeed_gpio *gpio, unsigned int offset,
> > +                               const enum aspeed_gpio_reg reg)
> > +{
> > +     const struct aspeed_gpio_bank *bank = to_bank(offset);
> > +     void __iomem *addr = bank_reg(gpio, bank, reg);
> > +
> > +     if (reg == reg_rdata || reg == reg_irq_status)
> > +             return ioread32(addr);
> > +     return !!(ioread32(addr) & GPIO_BIT(offset));

> Okay, the semantics here feel a bit concerning. I think we need one
> behaviour or the other, not both.

> Perhaps we have two callbacks:

> 1. get_bit()
> 2. get_bank()

> where get_bank() is only defined for reg_rdata and reg_irq_status, and
> get_bit() for all registers.

Okay, I will add get_bank() callback for this.

> > +}
> > +
> > +static bool aspeed_g4_copro_request(struct aspeed_gpio *gpio, unsigned int 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, offset, GPIO_CMDSRC_ARM);

> I don't think we need the indirection here, this is already a g4-
> specific callback implementation, we can directly call
> aspeed_g4_privilege_ctrl().

Okay, I will replace them to aspeed_g4_privilege_ctrl.

> > +
> > +     if (gpio->config->dcache_require)
> > +             /* Update cache */
> > +             gpio->dcache[GPIO_BANK(offset)] =
> > +                     gpio->config->llops->reg_bits_get(gpio, offset, reg_rdata);
> > +
> > +     return true;
> > +}
> > +
> > +static void aspeed_g4_copro_release(struct aspeed_gpio *gpio, unsigned int 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, offset, GPIO_CMDSRC_COLDFIRE);

> As above for the request implementation, we can call
> aspeed_g4_privilege_ctrl() directly here.

Okay.

> > +
> > +     /* Restart the coprocessor */
> > +     copro_ops->release_access(copro_data);
> > +}
> > +
> > +static void aspeed_g4_privilege_ctrl(struct aspeed_gpio *gpio, unsigned int offset, int cmdsrc)
> > +{
> > +     /*
> > +      * The command source register is only valid in bits 0, 8, 16, and 24, so we use
> > +      * (offset & ~(0x7)) to ensure that reg_bits_set always targets a valid bit.
> > +      */
> > +     /* Source 1 first to avoid illegal 11 combination */
> > +     gpio->config->llops->reg_bit_set(gpio, offset & ~(0x7), reg_cmdsrc1, !!(cmdsrc & BIT(1)));
> > +     /* Then Source 0 */
> > +     gpio->config->llops->reg_bit_set(gpio, offset & ~(0x7), reg_cmdsrc0, !!(cmdsrc & BIT(0)));

> Both of these can be direct calls to aspeed_g4_reg_bit_set().

Okay

> > +}
> > +
> > +static void aspeed_g4_privilege_init(struct aspeed_gpio *gpio)
> > +{
> > +     u32 i;
> > +
> > +     /* Switch all command sources to the ARM by default */
> > +     for (i = 0; i < DIV_ROUND_UP(gpio->chip.ngpio, 32); i++) {
> > +             aspeed_gpio_change_cmd_source(gpio, (i << 5) + 0, GPIO_CMDSRC_ARM);
> > +             aspeed_gpio_change_cmd_source(gpio, (i << 5) + 8, GPIO_CMDSRC_ARM);
> > +             aspeed_gpio_change_cmd_source(gpio, (i << 5) + 16, GPIO_CMDSRC_ARM);
> > +             aspeed_gpio_change_cmd_source(gpio, (i << 5) + 24, GPIO_CMDSRC_ARM);

> Again as this is a g4-specific callback we can directly call
> aspeed_g4_privilege_ctrl().

Okay.

> > +     }
> > +}
> > +
> > +static const struct aspeed_gpio_llops aspeed_g4_llops = {
> > +     .copro_request = aspeed_g4_copro_request,
> > +     .copro_release = aspeed_g4_copro_release,
> > +     .reg_bit_set = aspeed_g4_reg_bit_set,
> > +     .reg_bits_get = aspeed_g4_reg_bits_get,
> > +     .privilege_ctrl = aspeed_g4_privilege_ctrl,
> > +     .privilege_init = aspeed_g4_privilege_init,
> > +};
> >  /*
> >   * Any banks not specified in a struct aspeed_bank_props array are assumed to
> >   * have the properties:
> > @@ -1120,7 +1111,14 @@ static const struct aspeed_bank_props ast2400_bank_props[] = {
> >
> >  static const struct aspeed_gpio_config ast2400_config =
> >       /* 220 for simplicity, really 216 with two 4-GPIO holes, four at end */
> > -     { .nr_gpios = 220, .props = ast2400_bank_props, };
> > +     {
> > +             .nr_gpios = 220,
> > +             .props = ast2400_bank_props,
> > +             .llops = &aspeed_g4_llops,
> > +             .debounce_timers_array = debounce_timers,
> > +             .debounce_timers_num = ARRAY_SIZE(debounce_timers),
> > +             .dcache_require = true,
> > +     };
> >
> >  static const struct aspeed_bank_props ast2500_bank_props[] = {
> >       /*     input      output   */
> > @@ -1132,7 +1130,14 @@ static const struct aspeed_bank_props ast2500_bank_props[] = {
> >
> >  static const struct aspeed_gpio_config ast2500_config =
> >       /* 232 for simplicity, actual number is 228 (4-GPIO hole in GPIOAB) */
> > -     { .nr_gpios = 232, .props = ast2500_bank_props, };
> > +     {
> > +             .nr_gpios = 232,
> > +             .props = ast2500_bank_props,
> > +             .llops = &aspeed_g4_llops,
> > +             .debounce_timers_array = debounce_timers,
> > +             .debounce_timers_num = ARRAY_SIZE(debounce_timers),
> > +             .dcache_require = true,
> > +     };
> >
> >  static const struct aspeed_bank_props ast2600_bank_props[] = {
> >       /*     input      output   */
> > @@ -1148,7 +1153,14 @@ static const struct aspeed_gpio_config ast2600_config =
> >        * We expect ngpio being set in the device tree and this is a fallback
> >        * option.
> >        */
> > -     { .nr_gpios = 208, .props = ast2600_bank_props, };
> > +     {
> > +             .nr_gpios = 208,
> > +             .props = ast2600_bank_props,
> > +             .llops = &aspeed_g4_llops,
> > +             .debounce_timers_array = debounce_timers,
> > +             .debounce_timers_num = ARRAY_SIZE(debounce_timers),
> > +             .dcache_require = true,
> > +     };
> >
> >  static const struct of_device_id aspeed_gpio_of_table[] = {
> >       { .compatible = "aspeed,ast2400-gpio", .data = &ast2400_config, },
> > @@ -1191,6 +1203,9 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
> >
> >       gpio->config = gpio_id->data;
> >
> > +     if (!gpio->config->llops->reg_bit_set || !gpio->config->llops->reg_bits_get)
> > +             return -EINVAL;
> > +

> This will need to clean up gpio->clk. Perhaps you could move it above
> the of_clk_get() call instead?

How about change the `of_clk_get` to `devm_clk_get(&pdev->dev, 0);`?

> However, looking through the rest it seems we have a few issues with
> this leak :/

This gpio driver doesn't have the reset, is it?

>       gpio->chip.parent = &pdev->dev;
>       err = of_property_read_u32(pdev->dev.of_node, "ngpios", &ngpio);
>       gpio->chip.ngpio = (u16) ngpio;
> @@ -1207,27 +1222,23 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
>       gpio->chip.label = dev_name(&pdev->dev);
>       gpio->chip.base = -1;
>
> -     /* Allocate a cache of the output registers */
> -     banks = DIV_ROUND_UP(gpio->chip.ngpio, 32);
> -     gpio->dcache = devm_kcalloc(&pdev->dev,
> -                                 banks, sizeof(u32), GFP_KERNEL);
> -     if (!gpio->dcache)
> -             return -ENOMEM;
> -
> -     /*
> -      * 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++) {
> -             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);
> +     if (gpio->config->dcache_require) {
> +             /* Allocate a cache of the output registers */
> +             banks = DIV_ROUND_UP(gpio->chip.ngpio, 32);
> +             gpio->dcache = devm_kcalloc(&pdev->dev, banks, sizeof(u32), GFP_KERNEL);
> +             if (!gpio->dcache)
> +                     return -ENOMEM;

> This should also clean up gpio->clk. I don't think you can move it to
> avoid that.

I think using devm_clk_get() will also solve this problem.

Billy Tsai

> +             /*
> +              * Populate it with initial values read from the HW
> +              */
> +             for (i = 0; i < banks; i++)
> +                     gpio->dcache[i] =
> +                             gpio->config->llops->reg_bits_get(gpio, (i << 5), reg_rdata);
>       }
>
> +     if (gpio->config->llops->privilege_init)
> +             gpio->config->llops->privilege_init(gpio);
> +
>       /* Set up an irqchip */
>       irq = platform_get_irq(pdev, 0);
>       if (irq < 0)

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4 3/6] gpio: aspeed: Create llops to handle hardware access
  2024-09-20  9:19     ` Billy Tsai
@ 2024-09-23  0:34       ` Andrew Jeffery
  2024-09-23  2:20         ` Billy Tsai
  0 siblings, 1 reply; 16+ messages in thread
From: Andrew Jeffery @ 2024-09-23  0:34 UTC (permalink / raw)
  To: Billy Tsai, linus.walleij@linaro.org, brgl@bgdev.pl,
	robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
	joel@jms.id.au, linux-gpio@vger.kernel.org,
	devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
	linux-aspeed@lists.ozlabs.org, linux-kernel@vger.kernel.org,
	BMC-SW, Peter.Yin@quantatw.com, Jay_Zhang@wiwynn.com

On Fri, 2024-09-20 at 09:19 +0000, Billy Tsai wrote:
> > 
> > > @@ -1191,6 +1203,9 @@ static int __init aspeed_gpio_probe(struct
> > > platform_device *pdev)
> > > 
> > >       gpio->config = gpio_id->data;
> > > 
> > > +     if (!gpio->config->llops->reg_bit_set || !gpio->config-
> > > >llops->reg_bits_get)
> > > +             return -EINVAL;
> > > +
> 
> > This will need to clean up gpio->clk. Perhaps you could move it
> > above
> > the of_clk_get() call instead?
> 
> How about change the `of_clk_get` to `devm_clk_get(&pdev->dev, 0);`?

Yep.

> 
> > However, looking through the rest it seems we have a few issues
> > with
> > this leak :/
> 
> This gpio driver doesn't have the reset, is it?

No, just leaking the resource.

However, I can't see that we prepare/enable (and disable/unprepare) the
clock either :( [1]. Do you mind fixing that as well? It would be best
if debounce didn't work by accident.

Andrew

[1]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/clk.h?h=v6.11#n527





^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4 3/6] gpio: aspeed: Create llops to handle hardware access
  2024-09-23  0:34       ` Andrew Jeffery
@ 2024-09-23  2:20         ` Billy Tsai
  0 siblings, 0 replies; 16+ messages in thread
From: Billy Tsai @ 2024-09-23  2:20 UTC (permalink / raw)
  To: Andrew Jeffery, linus.walleij@linaro.org, brgl@bgdev.pl,
	robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
	joel@jms.id.au, linux-gpio@vger.kernel.org,
	devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
	linux-aspeed@lists.ozlabs.org, linux-kernel@vger.kernel.org,
	BMC-SW, Peter.Yin@quantatw.com, Jay_Zhang@wiwynn.com

> > >
> > > > @@ -1191,6 +1203,9 @@ static int __init aspeed_gpio_probe(struct
> > > > platform_device *pdev)
> > > >
> > > >       gpio->config = gpio_id->data;
> > > >
> > > > +     if (!gpio->config->llops->reg_bit_set || !gpio->config-
> > > > >llops->reg_bits_get)
> > > > +             return -EINVAL;
> > > > +
> >
> > > This will need to clean up gpio->clk. Perhaps you could move it
> > > above
> > > the of_clk_get() call instead?
> >
> > How about change the `of_clk_get` to `devm_clk_get(&pdev->dev, 0);`?

> Yep.

> >
> > > However, looking through the rest it seems we have a few issues
> > > with
> > > this leak :/
> >
> > This gpio driver doesn't have the reset, is it?

> No, just leaking the resource.

> However, I can't see that we prepare/enable (and disable/unprepare) the
> clock either :( [1]. Do you mind fixing that as well? It would be best
> if debounce didn't work by accident.

> Andrew

> [1]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/clk.h?h=v6.11#n527

Okay, I will update the clock API to use the `devm` prefix.

Thanks

Billy Tsai

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4 6/6] gpio: aspeed: Add the flush write to ensure the write complete.
  2024-09-19  9:43 ` [PATCH v4 6/6] gpio: aspeed: Add the flush write to ensure the write complete Billy Tsai
@ 2024-10-01 14:18   ` Linus Walleij
  2024-10-02 10:27     ` Bartosz Golaszewski
  0 siblings, 1 reply; 16+ messages in thread
From: Linus Walleij @ 2024-10-01 14:18 UTC (permalink / raw)
  To: Billy Tsai
  Cc: brgl, robh, krzk+dt, conor+dt, joel, andrew, linux-gpio,
	devicetree, linux-arm-kernel, linux-aspeed, linux-kernel, BMC-SW,
	Peter.Yin, Jay_Zhang

On Thu, Sep 19, 2024 at 11:43 AM Billy Tsai <billy_tsai@aspeedtech.com> wrote:

> Performing a dummy read ensures that the register write operation is fully
> completed, mitigating any potential bus delays that could otherwise impact
> the frequency of bitbang usage. E.g., if the JTAG application uses GPIO to
> control the JTAG pins (TCK, TMS, TDI, TDO, and TRST), and the application
> sets the TCK clock to 1 MHz, the GPIO’s high/low transitions will rely on
> a delay function to ensure the clock frequency does not exceed 1 MHz.
> However, this can lead to rapid toggling of the GPIO because the write
> operation is POSTed and does not wait for a bus acknowledgment.
>
> Signed-off-by: Billy Tsai <billy_tsai@aspeedtech.com>

If this applies cleanly on mainline I think it should go into fixes as-is.

Reviewed-by: Linus Walleij <linus.walleij@linaro.org>

Yours,
Linus Walleij


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4 6/6] gpio: aspeed: Add the flush write to ensure the write complete.
  2024-10-01 14:18   ` Linus Walleij
@ 2024-10-02 10:27     ` Bartosz Golaszewski
  2024-10-02 15:09       ` Billy Tsai
  0 siblings, 1 reply; 16+ messages in thread
From: Bartosz Golaszewski @ 2024-10-02 10:27 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Billy Tsai, robh, krzk+dt, conor+dt, joel, andrew, linux-gpio,
	devicetree, linux-arm-kernel, linux-aspeed, linux-kernel, BMC-SW,
	Peter.Yin, Jay_Zhang

On Tue, Oct 1, 2024 at 4:18 PM Linus Walleij <linus.walleij@linaro.org> wrote:
>
> On Thu, Sep 19, 2024 at 11:43 AM Billy Tsai <billy_tsai@aspeedtech.com> wrote:
>
> > Performing a dummy read ensures that the register write operation is fully
> > completed, mitigating any potential bus delays that could otherwise impact
> > the frequency of bitbang usage. E.g., if the JTAG application uses GPIO to
> > control the JTAG pins (TCK, TMS, TDI, TDO, and TRST), and the application
> > sets the TCK clock to 1 MHz, the GPIO’s high/low transitions will rely on
> > a delay function to ensure the clock frequency does not exceed 1 MHz.
> > However, this can lead to rapid toggling of the GPIO because the write
> > operation is POSTed and does not wait for a bus acknowledgment.
> >
> > Signed-off-by: Billy Tsai <billy_tsai@aspeedtech.com>
>
> If this applies cleanly on mainline I think it should go into fixes as-is.
>
> Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
>
> Yours,
> Linus Walleij

I agree but it doesn't. :(

Billy: please send it separately and - while at it - use a C-style comment.

Bart


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4 6/6] gpio: aspeed: Add the flush write to ensure the write complete.
  2024-10-02 10:27     ` Bartosz Golaszewski
@ 2024-10-02 15:09       ` Billy Tsai
  2024-10-02 15:48         ` Bartosz Golaszewski
  0 siblings, 1 reply; 16+ messages in thread
From: Billy Tsai @ 2024-10-02 15:09 UTC (permalink / raw)
  To: Bartosz Golaszewski, Linus Walleij
  Cc: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
	joel@jms.id.au, andrew@codeconstruct.com.au,
	linux-gpio@vger.kernel.org, devicetree@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org,
	linux-aspeed@lists.ozlabs.org, linux-kernel@vger.kernel.org,
	BMC-SW, Peter.Yin@quantatw.com, Jay_Zhang@wiwynn.com

> >
> > On Thu, Sep 19, 2024 at 11:43 AM Billy Tsai <billy_tsai@aspeedtech.com> wrote:
> >
> > > Performing a dummy read ensures that the register write operation is fully
> > > completed, mitigating any potential bus delays that could otherwise impact
> > > the frequency of bitbang usage. E.g., if the JTAG application uses GPIO to
> > > control the JTAG pins (TCK, TMS, TDI, TDO, and TRST), and the application
> > > sets the TCK clock to 1 MHz, the GPIO’s high/low transitions will rely on
> > > a delay function to ensure the clock frequency does not exceed 1 MHz.
> > > However, this can lead to rapid toggling of the GPIO because the write
> > > operation is POSTed and does not wait for a bus acknowledgment.
> > >
> > > Signed-off-by: Billy Tsai <billy_tsai@aspeedtech.com>
> >
> > If this applies cleanly on mainline I think it should go into fixes as-is.
> >
> > Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
> >
> > Yours,
> > Linus Walleij

> I agree but it doesn't. :(

> Billy: please send it separately and - while at it - use a C-style comment.

> Bart

Hi Linus Walleij and Bart,

Sorry, I don’t quite understand the meaning of “send it separately.” 
Does this mean I need to send this patch individually after the GPIO patch series has been accepted?

Thanks

Billy Tsai

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4 6/6] gpio: aspeed: Add the flush write to ensure the write complete.
  2024-10-02 15:09       ` Billy Tsai
@ 2024-10-02 15:48         ` Bartosz Golaszewski
  2024-10-02 17:02           ` Billy Tsai
  0 siblings, 1 reply; 16+ messages in thread
From: Bartosz Golaszewski @ 2024-10-02 15:48 UTC (permalink / raw)
  To: Billy Tsai
  Cc: Linus Walleij, robh@kernel.org, krzk+dt@kernel.org,
	conor+dt@kernel.org, joel@jms.id.au, andrew@codeconstruct.com.au,
	linux-gpio@vger.kernel.org, devicetree@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org,
	linux-aspeed@lists.ozlabs.org, linux-kernel@vger.kernel.org,
	BMC-SW, Peter.Yin@quantatw.com, Jay_Zhang@wiwynn.com

On Wed, Oct 2, 2024 at 5:09 PM Billy Tsai <billy_tsai@aspeedtech.com> wrote:
>
> > >
> > > On Thu, Sep 19, 2024 at 11:43 AM Billy Tsai <billy_tsai@aspeedtech.com> wrote:
> > >
> > > > Performing a dummy read ensures that the register write operation is fully
> > > > completed, mitigating any potential bus delays that could otherwise impact
> > > > the frequency of bitbang usage. E.g., if the JTAG application uses GPIO to
> > > > control the JTAG pins (TCK, TMS, TDI, TDO, and TRST), and the application
> > > > sets the TCK clock to 1 MHz, the GPIO’s high/low transitions will rely on
> > > > a delay function to ensure the clock frequency does not exceed 1 MHz.
> > > > However, this can lead to rapid toggling of the GPIO because the write
> > > > operation is POSTed and does not wait for a bus acknowledgment.
> > > >
> > > > Signed-off-by: Billy Tsai <billy_tsai@aspeedtech.com>
> > >
> > > If this applies cleanly on mainline I think it should go into fixes as-is.
> > >
> > > Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
> > >
> > > Yours,
> > > Linus Walleij
>
> > I agree but it doesn't. :(
>
> > Billy: please send it separately and - while at it - use a C-style comment.
>
> > Bart
>
> Hi Linus Walleij and Bart,
>
> Sorry, I don’t quite understand the meaning of “send it separately.”
> Does this mean I need to send this patch individually after the GPIO patch series has been accepted?
>

This is a fix, meaning: it should go upstream now and get backported
to stable branches. The other patches from this series will go in the
next merge window in two months or so. So send this as the first patch
in the series with an appropriate Fixes: tag or even send it entirely
independently from the rest.

Bart


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4 6/6] gpio: aspeed: Add the flush write to ensure the write complete.
  2024-10-02 15:48         ` Bartosz Golaszewski
@ 2024-10-02 17:02           ` Billy Tsai
  0 siblings, 0 replies; 16+ messages in thread
From: Billy Tsai @ 2024-10-02 17:02 UTC (permalink / raw)
  To: Bartosz Golaszewski
  Cc: Linus Walleij, robh@kernel.org, krzk+dt@kernel.org,
	conor+dt@kernel.org, joel@jms.id.au, andrew@codeconstruct.com.au,
	linux-gpio@vger.kernel.org, devicetree@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org,
	linux-aspeed@lists.ozlabs.org, linux-kernel@vger.kernel.org,
	BMC-SW, Peter.Yin@quantatw.com, Jay_Zhang@wiwynn.com

> > > >
> > > > On Thu, Sep 19, 2024 at 11:43 AM Billy Tsai <billy_tsai@aspeedtech.com> wrote:
> > > >
> > > > > Performing a dummy read ensures that the register write operation is fully
> > > > > completed, mitigating any potential bus delays that could otherwise impact
> > > > > the frequency of bitbang usage. E.g., if the JTAG application uses GPIO to
> > > > > control the JTAG pins (TCK, TMS, TDI, TDO, and TRST), and the application
> > > > > sets the TCK clock to 1 MHz, the GPIO’s high/low transitions will rely on
> > > > > a delay function to ensure the clock frequency does not exceed 1 MHz.
> > > > > However, this can lead to rapid toggling of the GPIO because the write
> > > > > operation is POSTed and does not wait for a bus acknowledgment.
> > > > >
> > > > > Signed-off-by: Billy Tsai <billy_tsai@aspeedtech.com>
> > > >
> > > > If this applies cleanly on mainline I think it should go into fixes as-is.
> > > >
> > > > Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
> > > >
> > > > Yours,
> > > > Linus Walleij
> >
> > > I agree but it doesn't. :(
> >
> > > Billy: please send it separately and - while at it - use a C-style comment.
> >
> > > Bart
> >
> > Hi Linus Walleij and Bart,
> >
> > Sorry, I don’t quite understand the meaning of “send it separately.”
> > Does this mean I need to send this patch individually after the GPIO patch series has been accepted?
> >

> This is a fix, meaning: it should go upstream now and get backported
> to stable branches. The other patches from this series will go in the
> next merge window in two months or so. So send this as the first patch
> in the series with an appropriate Fixes: tag or even send it entirely
> independently from the rest.

> Bart

Got it. Thanks for your prompt response.
I will include this as the first patch in the next version.

Billy Tsai

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2024-10-02 17:04 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-09-19  9:43 [PATCH v4 0/6] Add Aspeed G7 gpio support Billy Tsai
2024-09-19  9:43 ` [PATCH v4 1/6] dt-bindings: gpio: aspeed,ast2400-gpio: Support ast2700 Billy Tsai
2024-09-19  9:43 ` [PATCH v4 2/6] gpio: aspeed: Remove the name for bank array Billy Tsai
2024-09-19  9:43 ` [PATCH v4 3/6] gpio: aspeed: Create llops to handle hardware access Billy Tsai
2024-09-20  2:56   ` Andrew Jeffery
2024-09-20  9:19     ` Billy Tsai
2024-09-23  0:34       ` Andrew Jeffery
2024-09-23  2:20         ` Billy Tsai
2024-09-19  9:43 ` [PATCH v4 4/6] gpio: aspeed: Support G7 Aspeed gpio controller Billy Tsai
2024-09-19  9:43 ` [PATCH v4 5/6] gpio: aspeed: Change the macro to support deferred probe Billy Tsai
2024-09-19  9:43 ` [PATCH v4 6/6] gpio: aspeed: Add the flush write to ensure the write complete Billy Tsai
2024-10-01 14:18   ` Linus Walleij
2024-10-02 10:27     ` Bartosz Golaszewski
2024-10-02 15:09       ` Billy Tsai
2024-10-02 15:48         ` Bartosz Golaszewski
2024-10-02 17:02           ` Billy Tsai

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).