diff options
| author | Andrew Jeffery <andrew@aj.id.au> | 2022-02-16 10:26:56 +1030 | 
|---|---|---|
| committer | Tom Rini <trini@konsulko.com> | 2022-03-07 11:36:12 -0500 | 
| commit | 7ad889b0f37a122b7cb8c2a43302914423d77292 (patch) | |
| tree | f8b83d7168e6eb74767fb6327769fcc67b3dbbe5 /drivers/gpio/gpio-aspeed.c | |
| parent | 4a9099ef1c77c4a166509a811d15694309bd4c17 (diff) | |
gpio: Add Aspeed GPIO driver
The Aspeed GPIO driver supports the GPIO controllers found in the
AST2400, AST2500 and AST2600 BMC SoCs. The implementation is a cut-down
copy of the upstream Linux kernel driver, adapted for u-boot.
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
Diffstat (limited to 'drivers/gpio/gpio-aspeed.c')
| -rw-r--r-- | drivers/gpio/gpio-aspeed.c | 299 | 
1 files changed, 299 insertions, 0 deletions
| diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c new file mode 100644 index 00000000000..a8a2afcb5c8 --- /dev/null +++ b/drivers/gpio/gpio-aspeed.c @@ -0,0 +1,299 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2015 IBM Corp. + * Joel Stanley <joel@jms.id.au> + * Ryan Chen <ryan_chen@aspeedtech.com> + * + * Implementation extracted from the Linux kernel and adapted for u-boot. + */ +#include <common.h> +#include <asm/io.h> +#include <asm/gpio.h> + +#include <config.h> +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <asm/io.h> +#include <linux/bug.h> +#include <linux/sizes.h> + +struct aspeed_gpio_priv { +	void *regs; +}; + +struct aspeed_gpio_bank { +	u16		val_regs;	/* +0: Rd: read input value, Wr: set write latch +					 * +4: Rd/Wr: Direction (0=in, 1=out) +					 */ +	u16		rdata_reg;	/*     Rd: read write latch, Wr: <none>  */ +	u16		irq_regs; +	u16		debounce_regs; +	u16		tolerance_regs; +	u16		cmdsrc_regs; +	const char	names[4][3]; +}; + +static const struct aspeed_gpio_bank aspeed_gpio_banks[] = { +	{ +		.val_regs = 0x0000, +		.rdata_reg = 0x00c0, +		.irq_regs = 0x0008, +		.debounce_regs = 0x0040, +		.tolerance_regs = 0x001c, +		.cmdsrc_regs = 0x0060, +		.names = { "A", "B", "C", "D" }, +	}, +	{ +		.val_regs = 0x0020, +		.rdata_reg = 0x00c4, +		.irq_regs = 0x0028, +		.debounce_regs = 0x0048, +		.tolerance_regs = 0x003c, +		.cmdsrc_regs = 0x0068, +		.names = { "E", "F", "G", "H" }, +	}, +	{ +		.val_regs = 0x0070, +		.rdata_reg = 0x00c8, +		.irq_regs = 0x0098, +		.debounce_regs = 0x00b0, +		.tolerance_regs = 0x00ac, +		.cmdsrc_regs = 0x0090, +		.names = { "I", "J", "K", "L" }, +	}, +	{ +		.val_regs = 0x0078, +		.rdata_reg = 0x00cc, +		.irq_regs = 0x00e8, +		.debounce_regs = 0x0100, +		.tolerance_regs = 0x00fc, +		.cmdsrc_regs = 0x00e0, +		.names = { "M", "N", "O", "P" }, +	}, +	{ +		.val_regs = 0x0080, +		.rdata_reg = 0x00d0, +		.irq_regs = 0x0118, +		.debounce_regs = 0x0130, +		.tolerance_regs = 0x012c, +		.cmdsrc_regs = 0x0110, +		.names = { "Q", "R", "S", "T" }, +	}, +	{ +		.val_regs = 0x0088, +		.rdata_reg = 0x00d4, +		.irq_regs = 0x0148, +		.debounce_regs = 0x0160, +		.tolerance_regs = 0x015c, +		.cmdsrc_regs = 0x0140, +		.names = { "U", "V", "W", "X" }, +	}, +	{ +		.val_regs = 0x01E0, +		.rdata_reg = 0x00d8, +		.irq_regs = 0x0178, +		.debounce_regs = 0x0190, +		.tolerance_regs = 0x018c, +		.cmdsrc_regs = 0x0170, +		.names = { "Y", "Z", "AA", "AB" }, +	}, +	{ +		.val_regs = 0x01e8, +		.rdata_reg = 0x00dc, +		.irq_regs = 0x01a8, +		.debounce_regs = 0x01c0, +		.tolerance_regs = 0x01bc, +		.cmdsrc_regs = 0x01a0, +		.names = { "AC", "", "", "" }, +	}, +}; + +enum aspeed_gpio_reg { +	reg_val, +	reg_rdata, +	reg_dir, +	reg_irq_enable, +	reg_irq_type0, +	reg_irq_type1, +	reg_irq_type2, +	reg_irq_status, +	reg_debounce_sel1, +	reg_debounce_sel2, +	reg_tolerance, +	reg_cmdsrc0, +	reg_cmdsrc1, +}; + +#define GPIO_VAL_VALUE	0x00 +#define GPIO_VAL_DIR	0x04 + +#define GPIO_IRQ_ENABLE	0x00 +#define GPIO_IRQ_TYPE0	0x04 +#define GPIO_IRQ_TYPE1	0x08 +#define GPIO_IRQ_TYPE2	0x0c +#define GPIO_IRQ_STATUS	0x10 + +#define GPIO_DEBOUNCE_SEL1 0x00 +#define GPIO_DEBOUNCE_SEL2 0x04 + +#define GPIO_CMDSRC_0	0x00 +#define GPIO_CMDSRC_1	0x04 +#define  GPIO_CMDSRC_ARM		0 +#define  GPIO_CMDSRC_LPC		1 +#define  GPIO_CMDSRC_COLDFIRE		2 +#define  GPIO_CMDSRC_RESERVED		3 + +/* This will be resolved at compile time */ +static inline void __iomem *bank_reg(struct aspeed_gpio_priv *gpio, +				     const struct aspeed_gpio_bank *bank, +				     const enum aspeed_gpio_reg reg) +{ +	switch (reg) { +	case reg_val: +		return gpio->regs + bank->val_regs + GPIO_VAL_VALUE; +	case reg_rdata: +		return gpio->regs + bank->rdata_reg; +	case reg_dir: +		return gpio->regs + bank->val_regs + GPIO_VAL_DIR; +	case reg_irq_enable: +		return gpio->regs + bank->irq_regs + GPIO_IRQ_ENABLE; +	case reg_irq_type0: +		return gpio->regs + bank->irq_regs + GPIO_IRQ_TYPE0; +	case reg_irq_type1: +		return gpio->regs + bank->irq_regs + GPIO_IRQ_TYPE1; +	case reg_irq_type2: +		return gpio->regs + bank->irq_regs + GPIO_IRQ_TYPE2; +	case reg_irq_status: +		return gpio->regs + bank->irq_regs + GPIO_IRQ_STATUS; +	case reg_debounce_sel1: +		return gpio->regs + bank->debounce_regs + GPIO_DEBOUNCE_SEL1; +	case reg_debounce_sel2: +		return gpio->regs + bank->debounce_regs + GPIO_DEBOUNCE_SEL2; +	case reg_tolerance: +		return gpio->regs + bank->tolerance_regs; +	case reg_cmdsrc0: +		return gpio->regs + bank->cmdsrc_regs + GPIO_CMDSRC_0; +	case reg_cmdsrc1: +		return gpio->regs + bank->cmdsrc_regs + GPIO_CMDSRC_1; +	} +	BUG(); +} + +#define GPIO_BANK(x)	((x) >> 5) +#define GPIO_OFFSET(x)	((x) & 0x1f) +#define GPIO_BIT(x)	BIT(GPIO_OFFSET(x)) + +static const struct aspeed_gpio_bank *to_bank(unsigned int offset) +{ +	unsigned int bank = GPIO_BANK(offset); + +	WARN_ON(bank >= ARRAY_SIZE(aspeed_gpio_banks)); +	return &aspeed_gpio_banks[bank]; +} + +static int +aspeed_gpio_direction_input(struct udevice *dev, unsigned int offset) +{ +	struct aspeed_gpio_priv *priv = dev_get_priv(dev); +	const struct aspeed_gpio_bank *bank = to_bank(offset); +	u32 dir = readl(bank_reg(priv, bank, reg_dir)); + +	dir &= ~GPIO_BIT(offset); +	writel(dir, bank_reg(priv, bank, reg_dir)); + +	return 0; +} + +static int aspeed_gpio_direction_output(struct udevice *dev, unsigned int offset, +					int value) +{ +	struct aspeed_gpio_priv *priv = dev_get_priv(dev); +	const struct aspeed_gpio_bank *bank = to_bank(offset); +	u32 dir = readl(bank_reg(priv, bank, reg_dir)); +	u32 output = readl(bank_reg(priv, bank, reg_val)); + +	dir |= GPIO_BIT(offset); +	writel(dir, bank_reg(priv, bank, reg_dir)); + +	if (value) +		output |= GPIO_BIT(offset); +	else +		output &= ~GPIO_BIT(offset); + +	writel(output, bank_reg(priv, bank, reg_val)); + +	return 0; +} + +static int aspeed_gpio_get_value(struct udevice *dev, unsigned int offset) +{ +	struct aspeed_gpio_priv *priv = dev_get_priv(dev); +	const struct aspeed_gpio_bank *bank = to_bank(offset); + +	return !!(readl(bank_reg(priv, bank, reg_val)) & GPIO_BIT(offset)); +} + +static int +aspeed_gpio_set_value(struct udevice *dev, unsigned int offset, int value) +{ +	struct aspeed_gpio_priv *priv = dev_get_priv(dev); +	const struct aspeed_gpio_bank *bank = to_bank(offset); +	u32 data = readl(bank_reg(priv, bank, reg_val)); + +	if (value) +		data |= GPIO_BIT(offset); +	else +		data &= ~GPIO_BIT(offset); + +	writel(data, bank_reg(priv, bank, reg_val)); + +	return 0; +} + +static int aspeed_gpio_get_function(struct udevice *dev, unsigned int offset) +{ +	struct aspeed_gpio_priv *priv = dev_get_priv(dev); +	const struct aspeed_gpio_bank *bank = to_bank(offset); + +	if (readl(bank_reg(priv, bank, reg_dir)) & GPIO_BIT(offset)) +		return GPIOF_OUTPUT; + +	return GPIOF_INPUT; +} + +static const struct dm_gpio_ops aspeed_gpio_ops = { +	.direction_input	= aspeed_gpio_direction_input, +	.direction_output	= aspeed_gpio_direction_output, +	.get_value		= aspeed_gpio_get_value, +	.set_value		= aspeed_gpio_set_value, +	.get_function		= aspeed_gpio_get_function, +}; + +static int aspeed_gpio_probe(struct udevice *dev) +{ +	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); +	struct aspeed_gpio_priv *priv = dev_get_priv(dev); + +	uc_priv->bank_name = dev->name; +	ofnode_read_u32(dev_ofnode(dev), "ngpios", &uc_priv->gpio_count); +	priv->regs = devfdt_get_addr_ptr(dev); + +	return 0; +} + +static const struct udevice_id aspeed_gpio_ids[] = { +	{ .compatible = "aspeed,ast2400-gpio",  }, +	{ .compatible = "aspeed,ast2500-gpio",	}, +	{ .compatible = "aspeed,ast2600-gpio",	}, +	{ } +}; + +U_BOOT_DRIVER(gpio_aspeed) = { +	.name   = "gpio-aspeed", +	.id     = UCLASS_GPIO, +	.of_match = aspeed_gpio_ids, +	.ops    = &aspeed_gpio_ops, +	.probe  = aspeed_gpio_probe, +	.priv_auto = sizeof(struct aspeed_gpio_priv), +}; | 
