|  | From 53786a9d6d830cc51a1f7d96f97c19482b0ee898 Mon Sep 17 00:00:00 2001 | 
|  | From: Shao-Chieh Chao <jieh.sc.chao@mail.foxconn.com> | 
|  | Date: Fri, 29 Nov 2024 17:41:35 +0800 | 
|  | Subject: [PATCH] update gpio controller driver | 
|  |  | 
|  | --- | 
|  | drivers/gpio/gpio-aspeed.c | 621 +++++++++++++++++++++++-------------- | 
|  | 1 file changed, 392 insertions(+), 229 deletions(-) | 
|  |  | 
|  | diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c | 
|  | index 58f107194fda..cc3d23e88658 100644 | 
|  | --- a/drivers/gpio/gpio-aspeed.c | 
|  | +++ b/drivers/gpio/gpio-aspeed.c | 
|  | @@ -30,6 +30,27 @@ | 
|  | #include <linux/gpio/consumer.h> | 
|  | #include "gpiolib.h" | 
|  |  | 
|  | +/* Non-constant mask variant of FIELD_GET() and FIELD_PREP() */ | 
|  | +#define field_get(_mask, _reg)	(((_reg) & (_mask)) >> (ffs(_mask) - 1)) | 
|  | +#define field_prep(_mask, _val)	(((_val) << (ffs(_mask) - 1)) & (_mask)) | 
|  | + | 
|  | +#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_SEL1 BIT(7) | 
|  | +#define GPIO_G7_CTRL_DEBOUNCE_SEL2 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; | 
|  | @@ -39,6 +60,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 require_dcache; | 
|  | }; | 
|  |  | 
|  | /* | 
|  | @@ -77,7 +102,6 @@ struct aspeed_gpio_bank { | 
|  | uint16_t	debounce_regs; | 
|  | uint16_t	tolerance_regs; | 
|  | uint16_t	cmdsrc_regs; | 
|  | -	const char	names[4][3]; | 
|  | }; | 
|  |  | 
|  | /* | 
|  | @@ -92,6 +116,22 @@ struct aspeed_gpio_bank { | 
|  | */ | 
|  |  | 
|  | static const int debounce_timers[4] = { 0x00, 0x50, 0x54, 0x58 }; | 
|  | +static const int g7_debounce_timers[4] = { 0x00, 0x00, 0x04, 0x08 }; | 
|  | + | 
|  | +/* | 
|  | + * The debounce timers array is used to configure the debounce timer settings.Here’s how it works: | 
|  | + * Array Value: Indicates the offset for configuring the debounce timer. | 
|  | + * Array Index: Corresponds to the debounce setting register. | 
|  | + * The debounce timers array follows this pattern for configuring the debounce setting registers: | 
|  | + * Array Index 0: No debounce timer is set; | 
|  | + *		  Array Value is irrelevant (don’t care). | 
|  | + * Array Index 1: Debounce setting #2 is set to 1, and debounce setting #1 is set to 0. | 
|  | + *		  Array Value: offset for configuring debounce timer 0 (g4: 0x50, g7: 0x00) | 
|  | + * Array Index 2: Debounce setting #2 is set to 0, and debounce setting #1 is set to 1. | 
|  | + *		  Array Value: offset for configuring debounce timer 1 (g4: 0x54, g7: 0x04) | 
|  | + * Array Index 3: Debounce setting #2 is set to 1, and debounce setting #1 is set to 1. | 
|  | + *		  Array Value: offset for configuring debounce timer 2 (g4: 0x58, g7: 0x8) | 
|  | + */ | 
|  |  | 
|  | static const struct aspeed_gpio_copro_ops *copro_ops; | 
|  | static void *copro_data; | 
|  | @@ -104,7 +144,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 +152,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 +160,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 +168,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 +176,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 +184,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 +192,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 +200,6 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = { | 
|  | .debounce_regs = 0x01c0, | 
|  | .tolerance_regs = 0x01bc, | 
|  | .cmdsrc_regs = 0x01a0, | 
|  | -		.names = { "AC", "", "", "" }, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | @@ -187,6 +219,19 @@ enum aspeed_gpio_reg { | 
|  | reg_cmdsrc1, | 
|  | }; | 
|  |  | 
|  | +struct aspeed_gpio_llops { | 
|  | +	void (*reg_bit_set)(struct aspeed_gpio *gpio, unsigned int offset, | 
|  | +			    const enum aspeed_gpio_reg reg, bool val); | 
|  | +	bool (*reg_bit_get)(struct aspeed_gpio *gpio, unsigned int offset, | 
|  | +			    const enum aspeed_gpio_reg reg); | 
|  | +	int (*reg_bank_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); | 
|  | +	bool (*copro_request)(struct aspeed_gpio *gpio, unsigned int offset); | 
|  | +	void (*copro_release)(struct aspeed_gpio *gpio, unsigned int offset); | 
|  | +}; | 
|  | + | 
|  | #define GPIO_VAL_VALUE	0x00 | 
|  | #define GPIO_VAL_DIR	0x04 | 
|  |  | 
|  | @@ -207,9 +252,9 @@ enum aspeed_gpio_reg { | 
|  | #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, | 
|  | -				     const enum aspeed_gpio_reg reg) | 
|  | +static void __iomem *aspeed_gpio_g4_bank_reg(struct aspeed_gpio *gpio, | 
|  | +					     const struct aspeed_gpio_bank *bank, | 
|  | +					     const enum aspeed_gpio_reg reg) | 
|  | { | 
|  | switch (reg) { | 
|  | case reg_val: | 
|  | @@ -242,14 +287,43 @@ static inline void __iomem *bank_reg(struct aspeed_gpio *gpio, | 
|  | BUG(); | 
|  | } | 
|  |  | 
|  | +static u32 aspeed_gpio_g7_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)) | 
|  |  | 
|  | -#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); | 
|  | @@ -280,11 +354,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) | 
|  | @@ -304,108 +378,49 @@ 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) | 
|  | +static void aspeed_gpio_change_cmd_source(struct aspeed_gpio *gpio, 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); | 
|  | - | 
|  | -	/* 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)); | 
|  | +	if (gpio->config->llops->copro_request) | 
|  | +		return gpio->config->llops->copro_request(gpio, offset); | 
|  |  | 
|  | -	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_bit_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); | 
|  | +	/* Flush write */ | 
|  | +	gpio->config->llops->reg_bit_get(gpio, offset, reg_val); | 
|  | } | 
|  |  | 
|  | static void aspeed_gpio_set(struct gpio_chip *gc, unsigned int offset, | 
|  | @@ -413,7 +428,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); | 
|  | @@ -428,22 +443,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); | 
|  |  | 
|  | @@ -456,23 +465,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); | 
|  | @@ -484,7 +487,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; | 
|  |  | 
|  | @@ -496,7 +498,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_bit_get(gpio, offset, reg_dir); | 
|  |  | 
|  | raw_spin_unlock_irqrestore(&gpio->lock, flags); | 
|  |  | 
|  | @@ -505,8 +507,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; | 
|  |  | 
|  | @@ -519,32 +520,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); | 
|  | @@ -553,20 +547,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)); | 
|  | @@ -574,12 +563,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); | 
|  | @@ -605,34 +589,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: | 
|  | @@ -642,20 +623,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); | 
|  | @@ -670,7 +640,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); | 
|  | @@ -679,9 +648,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_bank_get(gpio, i * 32, reg_irq_status); | 
|  |  | 
|  | for_each_set_bit(p, ®, 32) | 
|  | generic_handle_domain_irq(gc->irq.domain, i * 32 + p); | 
|  | @@ -720,23 +687,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); | 
|  | @@ -830,21 +786,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, | 
|  | @@ -875,15 +821,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; | 
|  |  | 
|  | /* | 
|  | @@ -897,8 +843,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; | 
|  |  | 
|  | @@ -914,7 +860,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")) { | 
|  | @@ -1017,6 +963,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) | 
|  | @@ -1036,7 +985,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) | 
|  | @@ -1060,9 +1009,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; | 
|  |  | 
|  | @@ -1081,7 +1032,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); | 
|  | @@ -1091,12 +1042,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; | 
|  |  | 
|  | @@ -1113,6 +1062,173 @@ 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 = aspeed_gpio_g4_bank_reg(gpio, bank, reg); | 
|  | +	u32 temp; | 
|  | + | 
|  | +	if (reg == reg_val) | 
|  | +		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->dcache[GPIO_BANK(offset)] = temp; | 
|  | +	iowrite32(temp, addr); | 
|  | +} | 
|  | + | 
|  | +static bool aspeed_g4_reg_bit_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 = aspeed_gpio_g4_bank_reg(gpio, bank, reg); | 
|  | + | 
|  | +	return !!(ioread32(addr) & GPIO_BIT(offset)); | 
|  | +} | 
|  | + | 
|  | +static int aspeed_g4_reg_bank_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 = aspeed_gpio_g4_bank_reg(gpio, bank, reg); | 
|  | + | 
|  | +	if (reg == reg_rdata || reg == reg_irq_status) | 
|  | +		return ioread32(addr); | 
|  | +	else | 
|  | +		return -EOPNOTSUPP; | 
|  | +} | 
|  | + | 
|  | +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 */ | 
|  | +	aspeed_g4_reg_bit_set(gpio, offset & ~(0x7), reg_cmdsrc1, !!(cmdsrc & BIT(1))); | 
|  | +	/* Then Source 0 */ | 
|  | +	aspeed_g4_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_g4_privilege_ctrl(gpio, (i << 5) + 0, GPIO_CMDSRC_ARM); | 
|  | +		aspeed_g4_privilege_ctrl(gpio, (i << 5) + 8, GPIO_CMDSRC_ARM); | 
|  | +		aspeed_g4_privilege_ctrl(gpio, (i << 5) + 16, GPIO_CMDSRC_ARM); | 
|  | +		aspeed_g4_privilege_ctrl(gpio, (i << 5) + 24, GPIO_CMDSRC_ARM); | 
|  | +	} | 
|  | +} | 
|  | + | 
|  | +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_g4_privilege_ctrl(gpio, offset, GPIO_CMDSRC_ARM); | 
|  | + | 
|  | +	/* Update cache */ | 
|  | +	gpio->dcache[GPIO_BANK(offset)] = aspeed_g4_reg_bank_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_g4_privilege_ctrl(gpio, offset, GPIO_CMDSRC_COLDFIRE); | 
|  | + | 
|  | +	/* Restart the coprocessor */ | 
|  | +	copro_ops->release_access(copro_data); | 
|  | +} | 
|  | + | 
|  | +static const struct aspeed_gpio_llops aspeed_g4_llops = { | 
|  | +	.reg_bit_set = aspeed_g4_reg_bit_set, | 
|  | +	.reg_bit_get = aspeed_g4_reg_bit_get, | 
|  | +	.reg_bank_get = aspeed_g4_reg_bank_get, | 
|  | +	.privilege_ctrl = aspeed_g4_privilege_ctrl, | 
|  | +	.privilege_init = aspeed_g4_privilege_init, | 
|  | +	.copro_request = aspeed_g4_copro_request, | 
|  | +	.copro_release = aspeed_g4_copro_release, | 
|  | +}; | 
|  | + | 
|  | +static void aspeed_g7_reg_bit_set(struct aspeed_gpio *gpio, unsigned int offset, | 
|  | +				  const enum aspeed_gpio_reg reg, bool val) | 
|  | +{ | 
|  | +	u32 mask = aspeed_gpio_g7_reg_mask(reg); | 
|  | +	void __iomem *addr = gpio->base + GPIO_G7_CTRL_REG_OFFSET(offset); | 
|  | +	u32 write_val; | 
|  | + | 
|  | +	if (mask) { | 
|  | +		write_val = (ioread32(addr) & ~(mask)) | field_prep(mask, val); | 
|  | +		iowrite32(write_val, addr); | 
|  | +	} | 
|  | +} | 
|  | + | 
|  | +static bool aspeed_g7_reg_bit_get(struct aspeed_gpio *gpio, unsigned int offset, | 
|  | +				  const enum aspeed_gpio_reg reg) | 
|  | +{ | 
|  | +	u32 mask = aspeed_gpio_g7_reg_mask(reg); | 
|  | +	void __iomem *addr; | 
|  | + | 
|  | +	addr = gpio->base + GPIO_G7_CTRL_REG_OFFSET(offset); | 
|  | +	if (reg == reg_val) | 
|  | +		mask = GPIO_G7_CTRL_IN_DATA; | 
|  | + | 
|  | +	if (mask) | 
|  | +		return field_get(mask, ioread32(addr)); | 
|  | +	else | 
|  | +		return 0; | 
|  | +} | 
|  | + | 
|  | +static int aspeed_g7_reg_bank_get(struct aspeed_gpio *gpio, unsigned int offset, | 
|  | +				  const enum aspeed_gpio_reg reg) | 
|  | +{ | 
|  | +	void __iomem *addr; | 
|  | + | 
|  | +	if (reg == reg_irq_status) { | 
|  | +		addr = gpio->base + GPIO_G7_IRQ_STS_OFFSET(offset >> 5); | 
|  | +		return ioread32(addr); | 
|  | +	} else { | 
|  | +		return -EOPNOTSUPP; | 
|  | +	} | 
|  | +} | 
|  | + | 
|  | +static const struct aspeed_gpio_llops aspeed_g7_llops = { | 
|  | +	.reg_bit_set = aspeed_g7_reg_bit_set, | 
|  | +	.reg_bit_get = aspeed_g7_reg_bit_get, | 
|  | +	.reg_bank_get = aspeed_g7_reg_bank_get, | 
|  | +	.privilege_ctrl = NULL, | 
|  | +	.privilege_init = NULL, | 
|  | +	.copro_request = NULL, | 
|  | +	.copro_release = NULL, | 
|  | +}; | 
|  | + | 
|  | /* | 
|  | * Any banks not specified in a struct aspeed_bank_props array are assumed to | 
|  | * have the properties: | 
|  | @@ -1129,7 +1245,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), | 
|  | +		.require_dcache = true, | 
|  | +	}; | 
|  |  | 
|  | static const struct aspeed_bank_props ast2500_bank_props[] = { | 
|  | /*     input	  output   */ | 
|  | @@ -1141,7 +1264,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), | 
|  | +		.require_dcache = true, | 
|  | +	}; | 
|  |  | 
|  | static const struct aspeed_bank_props ast2600_bank_props[] = { | 
|  | /*     input	  output   */ | 
|  | @@ -1157,17 +1287,49 @@ 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), | 
|  | +		.require_dcache = true, | 
|  | +	}; | 
|  | + | 
|  | +static const struct aspeed_bank_props ast2700_bank_props[] = { | 
|  | +	/*     input	  output   */ | 
|  | +	{ 1, 0x0fffffff, 0x0fffffff }, /* E/F/G/H, 4-GPIO hole */ | 
|  | +	{ 6, 0xffffffff, 0xffff0000 }, /* Y/Z/AA/AB */ | 
|  | +	{ 7, 0x000fffff, 0x000fffff }, /* AC/AD/AE */ | 
|  | +	{}, | 
|  | +}; | 
|  | + | 
|  | +static const struct aspeed_gpio_config ast2700_config = | 
|  | +	/* | 
|  | +	 * ast2700 has two controllers one with 240 GPIOs and one with 16 GPIOs. | 
|  | +	 * 244 for simplicity, actual number is 240 (4-GPIO hole in GPIOH) | 
|  | +	 * We expect ngpio being set in the device tree and this is a fallback | 
|  | +	 * option. | 
|  | +	 */ | 
|  | +	{ | 
|  | +		.nr_gpios = 244, | 
|  | +		.props = ast2700_bank_props, | 
|  | +		.llops = &aspeed_g7_llops, | 
|  | +		.debounce_timers_array = g7_debounce_timers, | 
|  | +		.debounce_timers_num = ARRAY_SIZE(g7_debounce_timers), | 
|  | +		.require_dcache = 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); | 
|  |  | 
|  | -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; | 
|  | @@ -1191,7 +1353,7 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev) | 
|  | if (!gpio_id) | 
|  | return -EINVAL; | 
|  |  | 
|  | -	gpio->clk = of_clk_get(pdev->dev.of_node, 0); | 
|  | +	gpio->clk = devm_clk_get_enabled(&pdev->dev, NULL); | 
|  | if (IS_ERR(gpio->clk)) { | 
|  | dev_warn(&pdev->dev, | 
|  | "Failed to get clock from devicetree, debouncing disabled\n"); | 
|  | @@ -1200,6 +1362,10 @@ 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_bit_get || | 
|  | +	    !gpio->config->llops->reg_bank_get) | 
|  | +		return -EINVAL; | 
|  | + | 
|  | gpio->chip.parent = &pdev->dev; | 
|  | err = of_property_read_u32(pdev->dev.of_node, "ngpios", &ngpio); | 
|  | gpio->chip.ngpio = (u16) ngpio; | 
|  | @@ -1216,27 +1382,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->require_dcache) { | 
|  | +		/* 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_bank_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) | 
|  | @@ -1268,13 +1430,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.34.1 | 
|  |  |