From patchwork Fri Feb 22 10:21:39 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Walleij X-Patchwork-Id: 159017 Delivered-To: patch@linaro.org Received: by 2002:a02:48:0:0:0:0:0 with SMTP id 69csp1557458jaa; Fri, 22 Feb 2019 02:21:56 -0800 (PST) X-Google-Smtp-Source: AHgI3IZHZ8twYDsrtH2DApb+0dEzGa4Dr8qoCYUc/AZdijnjnbKbSZJ1CpFIoyU/AKb6Xzh/lLJ7 X-Received: by 2002:a17:902:850a:: with SMTP id bj10mr3416922plb.91.1550830916197; Fri, 22 Feb 2019 02:21:56 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1550830916; cv=none; d=google.com; s=arc-20160816; b=EUe8qlkgM35Jzwm//GjAb53SrwQ6uW9XFIu/1ui9wH/PMhsD42nG4DSXkmxIUtILbI ynbWPEA9c/yCDoYrfF4WqCcs+9QPJHFYYnHHupbFhuh3gAfRENhh9sQ010heZ5V5ACH/ MzlY4xNfiX/4OxoHw/p/2hYKQWVDzjwc3vgmK8nSMAdNwJPC+nv93zHiHFk6bb93dRG7 T5kV6pSQM7qzeui7yt7PtawA9NM8t8JlPNY/lxeEww/s2q0TEXtLeF5WfP4iqbXWTjEW J19/oxKQa27yu7nRuLKE7FbfR8XXeeFR/5MMLFuX8VKBN8+50Mu1hsvqFNhixz0uzBeV ktIw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :message-id:date:subject:cc:to:from:dkim-signature; bh=9KSsYTIyNk3nk3HSDYo7wQcxiJliLi0uWqzBQPJQ3KA=; b=uo69548l70Y+YbJjPDwwG4vfsKmm1jp1ctzohmJap2+zD04gH8v3m3BiPvqy5kyKsS lYN2scljR3s7LQL/DC/0DbNOecYL3XTxqfkxNdAGHdWo5nTPxNIRgRIog8zcyc+OSu2j MXM4GxYHsggG7ef+waTGX/DORg2POMd4Y4AEucO/Nfs5onO4IMhTP2giEo71VtjMfkZ7 IJBeAw6moKso9hhTEeeWOB+bALskB/+bSN1CwdLafZ1rTBSCDgpEcCRU/i+EBWiUlBbY Vq3sYv8IMq7p33PvmCHBXuZaM711swa1z2ogfUAZsV6g23stxzJx/23PrJHXfOkpk3vg j/MA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=zrPwvxBf; spf=pass (google.com: best guess record for domain of linux-gpio-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-gpio-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id bg3si1025624plb.363.2019.02.22.02.21.55; Fri, 22 Feb 2019 02:21:56 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-gpio-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=zrPwvxBf; spf=pass (google.com: best guess record for domain of linux-gpio-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-gpio-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726532AbfBVKVz (ORCPT + 5 others); Fri, 22 Feb 2019 05:21:55 -0500 Received: from mail-lj1-f196.google.com ([209.85.208.196]:38629 "EHLO mail-lj1-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725944AbfBVKVy (ORCPT ); Fri, 22 Feb 2019 05:21:54 -0500 Received: by mail-lj1-f196.google.com with SMTP id j19so1285591ljg.5 for ; Fri, 22 Feb 2019 02:21:52 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=9KSsYTIyNk3nk3HSDYo7wQcxiJliLi0uWqzBQPJQ3KA=; b=zrPwvxBfaj9w5LwUJ3hZAFqTTTntPznOMs1E1Rvxxl7lmgNinkqQtLpjs2X5917BYd 5fFUW8XRuRn6LDX0qGPApuREmq8gewzfkjAknJ6JMbI+m8iYgtIg4Ytfn5RbRWDjKGA3 AC/Hrbm2oUdDArSD3Rs3UMZOlfmw7+L7bPBjJQmh6yL6qsO4ojwqei7VrdxHVbjEFqPh ljTGPvRuTG0Xo32YWAHBfZvuQ9N9kJ3nWy1oJ0aIhAH5Rsh/zPn6fnUyu6ofPoaBfCHD g3JbqZKCzV6pY8LsyjnCG6JNPFJZWhXuVWDdwHZaZFuwFthdKC76vzPOt/dVvEnJ1Z7n 6hEg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=9KSsYTIyNk3nk3HSDYo7wQcxiJliLi0uWqzBQPJQ3KA=; b=fri+c4y89jQOl1MBeETmar1nHYtWby6C5pWca9nudTrcKxWkWPUMXbeil3H7YsKF5P IojyWcoYA+reo5jS2rX91vWmIKiJj6imP8xtTtRqnXXr72HMMfDUYQMdaTUldU61Q0go evBXD+mU9PvEXetj4ooOg0hY8Tdb+g51nfp/eSaGiRmWS44uGAOltBcX2yHlzcWPyz05 h4RPABV3WzkbXMV90uhPD6F5j+goerfmpwfXl6oOPjjenIy810AuOxmQXTJ0B0O+cEH/ g2m/CpKrWv3bhIGcA69WiVle2AulMnobUhIrMi9uy/icUIETSjd/fhigNhP/UEGFgQC8 cerw== X-Gm-Message-State: AHQUAubjQYQxPlUKimq4K6AOXLJkwnWVmrKUlWUVOPQYb2vrrySnb7Cc SXVf6Mv3Hgoc1GxQYnpmYis9GD148gw= X-Received: by 2002:a2e:a28b:: with SMTP id k11mr1892066lja.176.1550830910735; Fri, 22 Feb 2019 02:21:50 -0800 (PST) Received: from genomnajs.ideon.se ([85.235.10.227]) by smtp.gmail.com with ESMTPSA id d25sm414886lji.9.2019.02.22.02.21.46 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Fri, 22 Feb 2019 02:21:47 -0800 (PST) From: Linus Walleij To: linux-gpio@vger.kernel.org Cc: Bartosz Golaszewski , Linus Walleij , David Woods , Shravan Kumar Ramani Subject: [PATCH] gpio: mmio: Support two direction registers Date: Fri, 22 Feb 2019 11:21:39 +0100 Message-Id: <20190222102139.9242-1-linus.walleij@linaro.org> X-Mailer: git-send-email 2.20.1 MIME-Version: 1.0 Sender: linux-gpio-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org It turns out that one specific hardware has two direction registers: one to set a GPIO line as input and another one to set a GPIO line as output. So in theory a line can be configured as input and output at the same time. Make the MMIO GPIO helper deal with this: store both registers in the state container, use both in the generic code if present. Synchronize the input register to the output register when we register a GPIO chip, with the output settings taking precedence. Keep the helper variable to detect inverted direction semantics (only direction in register) but augment the code to be more straight-forward for the generic case when setting the registers. Fix some flunky with unreadable direction registers at the same time as we're touching this code. Cc: David Woods Cc: Shravan Kumar Ramani Signed-off-by: Linus Walleij --- David/Shravan: plese test if this can be used to cut down the code in your driver to just use GPIO_GENERIC and bgpio_init(). --- drivers/gpio/gpio-mmio.c | 85 ++++++++++++++++++++++++------------- include/linux/gpio/driver.h | 15 +++++-- 2 files changed, 67 insertions(+), 33 deletions(-) -- 2.20.1 Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c index 50bdc29591c0..f172b4382d8d 100644 --- a/drivers/gpio/gpio-mmio.c +++ b/drivers/gpio/gpio-mmio.c @@ -372,11 +372,12 @@ static int bgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) spin_lock_irqsave(&gc->bgpio_lock, flags); - if (gc->bgpio_dir_inverted) - gc->bgpio_dir |= bgpio_line2mask(gc, gpio); - else - gc->bgpio_dir &= ~bgpio_line2mask(gc, gpio); - gc->write_reg(gc->reg_dir, gc->bgpio_dir); + gc->bgpio_dir &= ~bgpio_line2mask(gc, gpio); + + if (gc->reg_dir_in) + gc->write_reg(gc->reg_dir_in, ~gc->bgpio_dir); + if (gc->reg_dir_out) + gc->write_reg(gc->reg_dir_out, gc->bgpio_dir); spin_unlock_irqrestore(&gc->bgpio_lock, flags); @@ -385,11 +386,16 @@ static int bgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) static int bgpio_get_dir(struct gpio_chip *gc, unsigned int gpio) { - /* Return 0 if output, 1 of input */ - if (gc->bgpio_dir_inverted) - return !!(gc->read_reg(gc->reg_dir) & bgpio_line2mask(gc, gpio)); - else - return !(gc->read_reg(gc->reg_dir) & bgpio_line2mask(gc, gpio)); + /* Return 0 if output, 1 if input */ + if (gc->bgpio_dir_unreadable) + return !(gc->bgpio_dir & bgpio_line2mask(gc, gpio)); + if (gc->reg_dir_out) + return !(gc->read_reg(gc->reg_dir_out) & bgpio_line2mask(gc, gpio)); + if (gc->reg_dir_in) + return !!(gc->read_reg(gc->reg_dir_in) & bgpio_line2mask(gc, gpio)); + + /* This should not happen */ + return 1; } static int bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) @@ -400,11 +406,12 @@ static int bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) spin_lock_irqsave(&gc->bgpio_lock, flags); - if (gc->bgpio_dir_inverted) - gc->bgpio_dir &= ~bgpio_line2mask(gc, gpio); - else - gc->bgpio_dir |= bgpio_line2mask(gc, gpio); - gc->write_reg(gc->reg_dir, gc->bgpio_dir); + gc->bgpio_dir |= bgpio_line2mask(gc, gpio); + + if (gc->reg_dir_in) + gc->write_reg(gc->reg_dir_in, ~gc->bgpio_dir); + if (gc->reg_dir_out) + gc->write_reg(gc->reg_dir_out, gc->bgpio_dir); spin_unlock_irqrestore(&gc->bgpio_lock, flags); @@ -537,19 +544,19 @@ static int bgpio_setup_direction(struct gpio_chip *gc, void __iomem *dirin, unsigned long flags) { - if (dirout && dirin) { - return -EINVAL; - } else if (dirout) { - gc->reg_dir = dirout; - gc->direction_output = bgpio_dir_out; - gc->direction_input = bgpio_dir_in; - gc->get_direction = bgpio_get_dir; - } else if (dirin) { - gc->reg_dir = dirin; + if (dirout || dirin) { + gc->reg_dir_out = dirout; + gc->reg_dir_in = dirin; gc->direction_output = bgpio_dir_out; gc->direction_input = bgpio_dir_in; gc->get_direction = bgpio_get_dir; - gc->bgpio_dir_inverted = true; + /* + * If only dirin is available, this means we need + * inverted semantics when handling get/set registers + * so detect this here. + */ + if (dirin && !dirout) + gc->bgpio_dir_inverted = true; } else { if (flags & BGPIOF_NO_OUTPUT) gc->direction_output = bgpio_dir_out_err; @@ -588,11 +595,11 @@ static int bgpio_request(struct gpio_chip *chip, unsigned gpio_pin) * @dirout: MMIO address for the register to set the line as OUTPUT. It is assumed * that setting a line to 1 in this register will turn that line into an * output line. Conversely, setting the line to 0 will turn that line into - * an input. Either this or @dirin can be defined, but never both. + * an input. * @dirin: MMIO address for the register to set this line as INPUT. It is assumed * that setting a line to 1 in this register will turn that line into an * input line. Conversely, setting the line to 0 will turn that line into - * an output. Either this or @dirout can be defined, but never both. + * an output. * @flags: Different flags that will affect the behaviour of the device, such as * endianness etc. */ @@ -634,8 +641,28 @@ int bgpio_init(struct gpio_chip *gc, struct device *dev, if (gc->set == bgpio_set_set && !(flags & BGPIOF_UNREADABLE_REG_SET)) gc->bgpio_data = gc->read_reg(gc->reg_set); - if (gc->reg_dir && !(flags & BGPIOF_UNREADABLE_REG_DIR)) - gc->bgpio_dir = gc->read_reg(gc->reg_dir); + + if (flags & BGPIOF_UNREADABLE_REG_DIR) + gc->bgpio_dir_unreadable = true; + + /* + * Inspect hardware to find initial direction setting. + */ + if ((gc->reg_dir_out || gc->reg_dir_in) && + !(flags & BGPIOF_UNREADABLE_REG_DIR)) { + if (gc->reg_dir_out) + gc->bgpio_dir = gc->read_reg(gc->reg_dir_out); + else if (gc->reg_dir_in) + gc->bgpio_dir = ~gc->read_reg(gc->reg_dir_in); + /* + * If we have two direction registers, synchronise + * input setting to output setting, the library + * can not handle a line being input and output at + * the same time. + */ + if (gc->reg_dir_out && gc->reg_dir_in) + gc->write_reg(gc->reg_dir_in, ~gc->bgpio_dir); + } return ret; } diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 07cddbf45186..81dbcfd71245 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -227,9 +227,13 @@ struct gpio_irq_chip { * @reg_dat: data (in) register for generic GPIO * @reg_set: output set register (out=high) for generic GPIO * @reg_clr: output clear register (out=low) for generic GPIO - * @reg_dir: direction setting register for generic GPIO + * @reg_dir_out: direction out setting register for generic GPIO + * @reg_dir_in: direction in setting register for generic GPIO * @bgpio_dir_inverted: indicates that the direction register is inverted - * (gpiolib private state variable) + * (gpiolib private state variable) this means @reg_dir_in is + * available but not @reg_dir_out. + * @bgpio_dir_unreadable: indicates that the direction register(s) cannot + * be read and we need to rely on out internal state tracking. * @bgpio_bits: number of register bits used for a generic GPIO i.e. * * 8 * @bgpio_lock: used to lock chip->bgpio_data. Also, this is needed to keep @@ -237,7 +241,8 @@ struct gpio_irq_chip { * @bgpio_data: shadowed data register for generic GPIO to clear/set bits * safely. * @bgpio_dir: shadowed direction register for generic GPIO to clear/set - * direction safely. + * direction safely. A "1" in this word means the line is set as + * output. * * A gpio_chip can help platforms abstract various sources of GPIOs so * they can all be accessed through a common programing interface. @@ -298,8 +303,10 @@ struct gpio_chip { void __iomem *reg_dat; void __iomem *reg_set; void __iomem *reg_clr; - void __iomem *reg_dir; + void __iomem *reg_dir_out; + void __iomem *reg_dir_in; bool bgpio_dir_inverted; + bool bgpio_dir_unreadable; int bgpio_bits; spinlock_t bgpio_lock; unsigned long bgpio_data;