[PATCH linux dev-4.13 2/6] gpio/aspeed: Use a cache of output data registers
Christopher Bostic
cbostic at linux.vnet.ibm.com
Wed May 9 02:17:17 AEST 2018
Reviewed-by: Christopher Bostic <cbostic at linux.vnet.ibm.com>
On 5/7/18 8:06 PM, Benjamin Herrenschmidt wrote:
> The current driver does a read/modify/write of the output
> registers when changing a bit in __aspeed_gpio_set().
>
> This is sub-optimal for a couple of reasons:
>
> - If any of the neighbouring GPIOs (sharing the shared
> register) isn't (yet) configured as an output, it will
> read the current input value, and then apply it to the
> output latch, which may not be what the user expects. There
> should be no bug in practice as aspeed_gpio_dir_out() will
> establish a new value but it's not great either.
>
> - The GPIO block in the aspeed chip is clocked rather
> slowly (typically 25Mhz). That extra MMIO read halves the maximum
> speed at which we can toggle the GPIO.
>
> This provides a significant performance improvement to the GPIO
> based FSI master.
>
> Signed-off-by: Benjamin Herrenschmidt <benh at kernel.crashing.org>
> ---
> drivers/gpio/gpio-aspeed.c | 21 +++++++++++++++++++--
> 1 file changed, 19 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
> index ac54b9b25f74..a62cbf3ed4a8 100644
> --- a/drivers/gpio/gpio-aspeed.c
> +++ b/drivers/gpio/gpio-aspeed.c
> @@ -54,6 +54,8 @@ struct aspeed_gpio {
> u8 *offset_timer;
> unsigned int timer_users[4];
> struct clk *clk;
> +
> + u32 *dcache;
> };
>
> struct aspeed_gpio_bank {
> @@ -231,12 +233,13 @@ static void __aspeed_gpio_set(struct gpio_chip *gc, unsigned int offset,
> u32 reg;
>
> addr = bank_val_reg(gpio, bank, GPIO_DATA);
> - reg = ioread32(addr);
> + 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);
> }
> @@ -848,7 +851,7 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
> const struct of_device_id *gpio_id;
> struct aspeed_gpio *gpio;
> struct resource *res;
> - int rc;
> + int rc, i, banks;
>
> gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
> if (!gpio)
> @@ -889,6 +892,20 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
> gpio->chip.base = -1;
> gpio->chip.irq_need_valid_mask = true;
>
> + /* Allocate a cache of the output registers */
> + banks = gpio->config->nr_gpios >> 5;
> + gpio->dcache = devm_kzalloc(&pdev->dev,
> + sizeof(u32) * banks, GFP_KERNEL);
> + if (!gpio->dcache)
> + return -ENOMEM;
> +
> + /* Populate it with initial values read from the HW */
> + for (i = 0; i < banks; i++) {
> + const struct aspeed_gpio_bank *bank = &aspeed_gpio_banks[i];
> + gpio->dcache[i] = ioread32(gpio->base + bank->val_regs +
> + GPIO_DATA);
> + }
> +
> rc = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio);
> if (rc < 0)
> return rc;
More information about the openbmc
mailing list