All of lore.kernel.org
 help / color / mirror / Atom feed
From: JaeJoon Jung <rgbi3307@gmail.com>
To: Paul Walmsley <paul.walmsley@sifive.com>,
	andrew@sifive.com,  Palmer Dabbelt <palmer@dabbelt.com>,
	Anup Patel <anup@brainfault.org>
Cc: linux-riscv <linux-riscv@lists.infradead.org>
Subject: [PATCH] RISC-V: Add and Update gpio-sifive.c for SiFive FU540 GPIO
Date: Tue, 28 Jan 2020 15:41:54 +0900	[thread overview]
Message-ID: <CAHOvCC5h1cKJzYx4sm-U0HDY9LGkhqWYGh+VaaOaJidP=Jkerw@mail.gmail.com> (raw)

[-- Attachment #1: Type: text/plain, Size: 24065 bytes --]

I added and updated gpio-sifive.c for SiFive FU540 GPIO.
(References: SiFive FU540-C000 manual v1p0, Chapter 17 "GPIO")
I attached drivers/gpio/gpio-sifive.c (new)

RISCV-FU540:/root# uname -a
Linux RISCV-FU540 5.5.0-dirty #6 SMP Tue Jan 28 14:38:11 KST 2020
riscv64 GNU/Linux

$ git diff v5.5 --compact-summary
 arch/riscv/boot/dts/sifive/fu540-c000.dtsi          |  13 ++++
 arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts |  36 +++++++++
 drivers/gpio/Kconfig                                |   8 ++
 drivers/gpio/Makefile                               |   1 +
 drivers/gpio/gpio-sifive.c (new)                    | 447
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 574 insertions(+)

I used drivers/gpio/gpio-sifive.c in kernel v4.15(freedom-u-sdk)
released by sifive.
I changed iowrite32()/ioread32() with writel()/readl().
I modified function names with gpio_sifive_* and added debug functions.
I adjusted sifive_assign_bit(), sifive_get_value() and sifive_set_value().
I fixed bugs in the sifive_irq_enable()/disable() functions.

I have verified gpio input/output as below:

RISCV-FU540:/# cd /sys/class/gpio
RISCV-FU540:/sys/class/gpio# ll
total 0
drwxr-xr-x    2 root     root             0 Jan  1 00:00 ./
drwxr-xr-x   33 root     root             0 Jan  1 00:00 ../
--w-------    1 root     root          4096 Jan  1 00:00 export
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 gpiochip496
-> ../../devices/platform/soc/10060000.gpio/gpio/gpiochip496/
--w-------    1 root     root          4096 Jan  1 00:00 unexport
RISCV-FU540:/sys/class/gpio# echo 496 > export
RISCV-FU540:/sys/class/gpio# ll
total 0
drwxr-xr-x    2 root     root             0 Jan  1 00:00 ./
drwxr-xr-x   33 root     root             0 Jan  1 00:00 ../
--w-------    1 root     root          4096 Jan  1 00:00 export
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 gpio496 ->
../../devices/platform/soc/10060000.gpio/gpiochip0/gpio/gpio496/
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 gpiochip496
-> ../../devices/platform/soc/10060000.gpio/gpio/gpiochip496/
--w-------    1 root     root          4096 Jan  1 00:00 unexport
RISCV-FU540:/sys/class/gpio# cd gpio496/
RISCV-FU540:/sys/class/gpio/gpio496# ll
total 0
drwxr-xr-x    2 root     root             0 Jan  1 00:00 ./
drwxr-xr-x    3 root     root             0 Jan  1 00:00 ../
-rw-r--r--    1 root     root          4096 Jan  1 00:01 active_low
lrwxrwxrwx    1 root     root             0 Jan  1 00:01 device ->
../../../gpiochip0/
-rw-r--r--    1 root     root          4096 Jan  1 00:01 direction
-rw-r--r--    1 root     root          4096 Jan  1 00:01 edge
lrwxrwxrwx    1 root     root             0 Jan  1 00:01 subsystem ->
../../../../../../../class/gpio/
-rw-r--r--    1 root     root          4096 Jan  1 00:01 uevent
-rw-r--r--    1 root     root          4096 Jan  1 00:01 value
RISCV-FU540:/sys/class/gpio/gpio496# cat direction
in
RISCV-FU540:/sys/class/gpio/gpio496# echo out > direction
RISCV-FU540:/sys/class/gpio/gpio496# cat value
0
RISCV-FU540:/sys/class/gpio/gpio496# echo 1 > value
RISCV-FU540:/sys/class/gpio/gpio496# cat value
1
RISCV-FU540:/sys/class/gpio/gpio496#

I have verified gpio interrupts as below:
If you define GPIO_SIFIVE_DEBUG in the drivers/gpio/gpio-sifive.c,
you can see below messages.

RISCV-FU540:/root# dmesg | grep GPIO
[    0.348258] GPIO: registers values ---------------------------
[    0.348302] GPIO: reg=[00], value=[00000000]
[    0.348338] GPIO: reg=[04], value=[00000000]
[    0.348374] GPIO: reg=[08], value=[00001000]
[    0.348411] GPIO: reg=[0C], value=[00001000]
[    0.348447] GPIO: reg=[10], value=[00000000]
[    0.348483] GPIO: reg=[14], value=[00000000]
[    0.348519] GPIO: reg=[18], value=[00000000]
[    0.348555] GPIO: reg=[1C], value=[00000000]
[    0.348591] GPIO: reg=[20], value=[00000000]
[    0.348627] GPIO: reg=[24], value=[00000000]
[    0.348663] GPIO: reg=[28], value=[00000000]
[    0.348698] GPIO: reg=[2C], value=[00000000]
[    0.348734] GPIO: reg=[30], value=[00000000]
[    0.348771] GPIO: reg=[34], value=[0000FFFF]
[    0.348806] GPIO: reg=[38], value=[00000000]
[    0.348842] GPIO: reg=[3C], value=[00000000]
[    0.348879] GPIO: reg=[40], value=[00000000]
[    0.348914] GPIO: irq_enable=[00000000]
[    0.348949] GPIO: irq_type=0
[    0.348983] GPIO: irq_parent=0
[    0.349016] GPIO:
[    0.349470] gpio-sifive 10060000.gpio: GPIO SiFive driver registered 16 GPIOs
[    0.349517] GPIO: registers values ---------------------------
[    0.349556] GPIO: reg=[00], value=[00000000]
[    0.349592] GPIO: reg=[04], value=[000083FF]
[    0.349628] GPIO: reg=[08], value=[00001000]
[    0.349664] GPIO: reg=[0C], value=[00001000]
[    0.349700] GPIO: reg=[10], value=[00000000]
[    0.349736] GPIO: reg=[14], value=[00000000]
[    0.349772] GPIO: reg=[18], value=[00000000]
[    0.349808] GPIO: reg=[1C], value=[00000000]
[    0.349844] GPIO: reg=[20], value=[00000000]
[    0.349880] GPIO: reg=[24], value=[00000000]
[    0.349916] GPIO: reg=[28], value=[00000080]  /* GPIO7 interrupt enabled */
[    0.349952] GPIO: reg=[2C], value=[00000000]
[    0.349988] GPIO: reg=[30], value=[00000000]
[    0.350024] GPIO: reg=[34], value=[0000FFFF]
[    0.350060] GPIO: reg=[38], value=[00000000]
[    0.350096] GPIO: reg=[3C], value=[00000000]
[    0.350132] GPIO: reg=[40], value=[00000000]
[    0.350168] GPIO: irq_enable=[00000080]
[    0.350202] GPIO: irq_type=4
[    0.350236] GPIO: irq_parent=26
[    0.350269] GPIO:

Interrupt occured on GPIO7:

RISCV-FU540:/root# [  104.441392] GPIO: irq handler: offset=7
[  104.444474] GPIO: irq handler: offset=7
[  104.457639] GPIO: irq handler: offset=7
[  104.460717] GPIO: irq handler: offset=7
[  104.707453] GPIO: irq handler: offset=7
[  104.710533] GPIO: irq handler: offset=7

Source Differences:

diff --git a/arch/riscv/boot/dts/sifive/fu540-c000.dtsi
b/arch/riscv/boot/dts/sifive/fu540-c000.dtsi
index a2e3d54e830c..a8f165a378bd 100644
--- a/arch/riscv/boot/dts/sifive/fu540-c000.dtsi
+++ b/arch/riscv/boot/dts/sifive/fu540-c000.dtsi
@@ -268,6 +268,19 @@
                        interrupts = <1 2 3>;
                        reg = <0x0 0x2010000 0x0 0x1000>;
                };
+                gpio0: gpio@10060000 {
+                        compatible = "sifive,fu540-c000-gpio", "sifive,gpio0";
+                        reg = <0x0 0x10060000 0x0 0x1000>;
+                        reg-names = "control";
+                        interrupt-parent = <&plic0>;
+                        interrupts = <7 8 9 10 11 12 13 14 15 16 17
18 19 20 21 22>;
+                        ngpios = <16>;
+                        gpio-controller;
+                        #gpio-cells = <2>;
+                        interrupt-controller;
+                        #interrupt-cells = <2>;
+                        status = "disabled";
+                };

        };
 };


diff --git a/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts
b/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts
index 88cfcb96bf23..81516394c2a8 100644
--- a/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts
+++ b/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts

 &uart0 {
@@ -94,3 +126,7 @@
 &pwm1 {
        status = "okay";
 };
+
+&gpio0 {
+        status = "okay";
+};


diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 4b6d2ef15c39..0f01ef8339b1 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -693,6 +693,14 @@ config GPIO_AMD_FCH
          Note: This driver doesn't registers itself automatically, as it
          needs to be provided with platform specific configuration.
          (See eg. CONFIG_PCENGINES_APU2.)
+
+config GPIO_SIFIVE
+        tristate "SiFive GPIO support"
+        depends on OF_GPIO
+        select GPIOLIB_IRQCHIP
+        help
+          Say yes here to support the GPIO device on SiFive SoCs.
+
 endmenu

 menu "Port-mapped I/O GPIO drivers"


diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 34eb8b2b12dd..dfbf9282369b 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -124,6 +124,7 @@ obj-$(CONFIG_ARCH_SA1100)           += gpio-sa1100.o
 obj-$(CONFIG_GPIO_SAMA5D2_PIOBU)       += gpio-sama5d2-piobu.o
 obj-$(CONFIG_GPIO_SCH311X)             += gpio-sch311x.o
 obj-$(CONFIG_GPIO_SCH)                 += gpio-sch.o
+obj-$(CONFIG_GPIO_SIFIVE)              += gpio-sifive.o
 obj-$(CONFIG_GPIO_SIOX)                += gpio-siox.o
 obj-$(CONFIG_GPIO_SODAVILLE)           += gpio-sodaville.o
 obj-$(CONFIG_GPIO_SPEAR_SPICS)         += gpio-spear-spics.o



diff --git a/drivers/gpio/gpio-sifive.c b/drivers/gpio/gpio-sifive.c
new file mode 100644
index 000000000000..355b37cd1cca
--- /dev/null
+++ b/drivers/gpio/gpio-sifive.c
@@ -0,0 +1,447 @@
+/*
+ * SiFive GPIO driver
+ *
+ * Copyright (C) 2018 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * References:
+ *   SiFive FU540-C000 manual v1p0, Chapter 17 "GPIO"
+ *
+ * 2020 Editted by JaeJoon Jung <rgbi3307@gmail.com>
+ *
+ */
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/gpio/driver.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/raid/pq.h>
+
+#define GPIO_INPUT_VAL         0x00
+#define GPIO_INPUT_EN          0x04
+#define GPIO_OUTPUT_EN         0x08
+#define GPIO_OUTPUT_VAL        0x0C
+#define GPIO_PULLUP_EN         0x10
+#define GPIO_PIN_DS            0x14
+#define GPIO_RISE_IE           0x18
+#define GPIO_RISE_IP           0x1C
+#define GPIO_FALL_IE           0x20
+#define GPIO_FALL_IP           0x24
+#define GPIO_HIGH_IE           0x28
+#define GPIO_HIGH_IP           0x2C
+#define GPIO_LOW_IE            0x30
+#define GPIO_LOW_IP            0x34
+#define GPIO_OUTPUT_XOR        0x40
+
+#define GPIO_MAX_CNT           32
+#define GPIO_ENABLE_BITS        0x83FF
+
+//#define GPIO_SIFIVE_DEBUG
+#ifdef GPIO_SIFIVE_DEBUG
+        #define gpio_sifive_debug(...) printk("GPIO: " __VA_ARGS__)
+#else
+        #define gpio_sifive_debug(...)
+#endif
+
+struct sifive_gpio {
+       raw_spinlock_t          lock;
+       void __iomem            *base;
+       struct gpio_chip        gc;
+       unsigned int            irq_enable;
+       unsigned int            irq_type[GPIO_MAX_CNT];
+       unsigned int            irq_parent[GPIO_MAX_CNT];
+       struct sifive_gpio      *self_ptr[GPIO_MAX_CNT];
+};
+
+
+static void gpio_sifive_debug_reg(struct sifive_gpio *chip)
+{
+#ifdef GPIO_SIFIVE_DEBUG
+        u32 value;
+        int reg;
+
+        if (!chip->base) return;
+        gpio_sifive_debug("registers values ---------------------------\n");
+        for (reg=GPIO_INPUT_VAL; reg<=GPIO_OUTPUT_XOR; reg+=4) {
+                value = readl(chip->base + reg);
+                gpio_sifive_debug("reg=[%02X], value=[%08X]\n", reg, value);
+        }
+        gpio_sifive_debug("irq_enable=[%08X]\n", chip->irq_enable);
+        gpio_sifive_debug("irq_type=%d\n", chip->irq_type[0]);
+        gpio_sifive_debug("irq_parent=%d\n", chip->irq_parent[0]);
+        gpio_sifive_debug("\n");
+#endif
+}
+
+static void gpio_sifive_assign_bit(void __iomem *ptr, int offset, int value)
+{
+       // It's frustrating that we are not allowed to use the device atomics
+       // which are GUARANTEED to be supported by this device on RISC-V
+       u32 bit = BIT(offset);
+        u32 old = readl(ptr);
+
+        bit = (value) ? old | bit : old & ~bit;
+        writel(bit, ptr);
+}
+
+static int gpio_sifive_direction_input(struct gpio_chip *gc, unsigned offset)
+{
+       struct sifive_gpio *chip = gpiochip_get_data(gc);
+       unsigned long flags;
+
+       if (offset >= gc->ngpio)
+               return -EINVAL;
+
+       raw_spin_lock_irqsave(&chip->lock, flags);
+       gpio_sifive_assign_bit(chip->base + GPIO_OUTPUT_EN, offset, 0);
+       gpio_sifive_assign_bit(chip->base + GPIO_INPUT_EN,  offset, 1);
+       raw_spin_unlock_irqrestore(&chip->lock, flags);
+
+       return 0;
+}
+
+static int gpio_sifive_direction_output(struct gpio_chip *gc, unsigned offset
+                                                            , int value)
+{
+       struct sifive_gpio *chip = gpiochip_get_data(gc);
+       unsigned long flags;
+
+       if (offset >= gc->ngpio)
+               return -EINVAL;
+
+       raw_spin_lock_irqsave(&chip->lock, flags);
+       gpio_sifive_assign_bit(chip->base + GPIO_INPUT_EN,   offset, 0);
+       gpio_sifive_assign_bit(chip->base + GPIO_OUTPUT_EN,  offset, 1);
+       gpio_sifive_assign_bit(chip->base + GPIO_OUTPUT_VAL, offset, value);
+       raw_spin_unlock_irqrestore(&chip->lock, flags);
+
+       return 0;
+}
+
+static int gpio_sifive_get_direction(struct gpio_chip *gc, unsigned offset)
+{
+       struct sifive_gpio *chip = gpiochip_get_data(gc);
+        int value;
+
+       if (offset >= gc->ngpio)
+               return -EINVAL;
+
+        value = readl(chip->base + GPIO_OUTPUT_EN) & BIT(offset);
+        return !value;
+}
+
+static int gpio_sifive_get_value(struct gpio_chip *gc, unsigned offset)
+{
+       struct sifive_gpio *chip = gpiochip_get_data(gc);
+        int index, value;
+
+       if (offset >= gc->ngpio)
+               return -EINVAL;
+
+        index = gpio_sifive_get_direction(gc, offset) ?
+                                GPIO_INPUT_VAL : GPIO_OUTPUT_VAL;
+        value = readl(chip->base + index) & BIT(offset);
+        return value;
+}
+
+static void gpio_sifive_set_value(struct gpio_chip *gc, unsigned
offset, int value)
+{
+       struct sifive_gpio *chip = gpiochip_get_data(gc);
+       unsigned long flags;
+        int index;
+
+       if (offset >= gc->ngpio)
+               return;
+
+        index = gpio_sifive_get_direction(gc, offset) ?
+                                GPIO_INPUT_VAL : GPIO_OUTPUT_VAL;
+       raw_spin_lock_irqsave(&chip->lock, flags);
+       gpio_sifive_assign_bit(chip->base + index, offset, value);
+       raw_spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static void gpio_sifive_set_default(struct sifive_gpio *chip, u32 bits)
+{
+        if (bits == GPIO_ENABLE_BITS) {
+                //Input Enable/Disable
+                writel(bits, chip->base + GPIO_INPUT_EN);
+                return;
+        }
+        //Interrupts Enable/Disable
+        writel(bits, chip->base + GPIO_RISE_IE);
+        writel(bits, chip->base + GPIO_FALL_IE);
+        writel(bits, chip->base + GPIO_HIGH_IE);
+        writel(bits, chip->base + GPIO_LOW_IE);
+
+        writel(bits, chip->base + GPIO_RISE_IP);
+        writel(bits, chip->base + GPIO_FALL_IP);
+        writel(bits, chip->base + GPIO_HIGH_IP);
+        writel(bits, chip->base + GPIO_LOW_IP);
+
+        chip->irq_enable = bits;
+}
+
+static void gpio_sifive_set_ie(struct sifive_gpio *chip, int offset)
+{
+       unsigned long flags;
+       unsigned irq_type;
+
+       raw_spin_lock_irqsave(&chip->lock, flags);
+       irq_type = (chip->irq_enable & BIT(offset)) ?
chip->irq_type[offset] : 0;
+       gpio_sifive_assign_bit(chip->base + GPIO_RISE_IE, offset,
irq_type & IRQ_TYPE_EDGE_RISING);
+       gpio_sifive_assign_bit(chip->base + GPIO_FALL_IE, offset,
irq_type & IRQ_TYPE_EDGE_FALLING);
+       gpio_sifive_assign_bit(chip->base + GPIO_HIGH_IE, offset,
irq_type & IRQ_TYPE_LEVEL_HIGH);
+       gpio_sifive_assign_bit(chip->base + GPIO_LOW_IE,  offset,
irq_type & IRQ_TYPE_LEVEL_LOW);
+       raw_spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static int gpio_sifive_irq_set_type(struct irq_data *d, unsigned irq_type)
+{
+       struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+       struct sifive_gpio *chip = gpiochip_get_data(gc);
+       int offset = irqd_to_hwirq(d);
+
+       if (offset < 0 || offset >= gc->ngpio)
+               return -EINVAL;
+
+       chip->irq_type[offset] = irq_type;
+       gpio_sifive_set_ie(chip, offset);
+
+       return 0;
+}
+
+/* chained_irq_{enter,exit} already mask the parent */
+static void gpio_sifive_irq_mask(struct irq_data *d) { }
+static void gpio_sifive_irq_unmask(struct irq_data *d) { }
+
+static void gpio_sifive_irq_enable(struct irq_data *d)
+{
+       struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+       struct sifive_gpio *chip = gpiochip_get_data(gc);
+       int offset = irqd_to_hwirq(d) % GPIO_MAX_CNT; // must not fail
+       u32 bit = BIT(offset);
+
+       /* Switch to input */
+       gpio_sifive_direction_input(gc, offset);
+
+       /* Clear any sticky pending interrupts */
+       writel(bit, chip->base + GPIO_RISE_IP);
+       writel(bit, chip->base + GPIO_FALL_IP);
+       writel(bit, chip->base + GPIO_HIGH_IP);
+       writel(bit, chip->base + GPIO_LOW_IP);
+
+       /* Enable interrupts */
+        chip->irq_enable |= bit;
+       gpio_sifive_set_ie(chip, offset);
+}
+
+static void gpio_sifive_irq_disable(struct irq_data *d)
+{
+       struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+       struct sifive_gpio *chip = gpiochip_get_data(gc);
+       int offset = irqd_to_hwirq(d) % GPIO_MAX_CNT; // must not fail
+       u32 bit = BIT(offset);
+
+       /* Disable interrupts */
+        chip->irq_enable &= ~bit;
+       gpio_sifive_set_ie(chip, offset);
+}
+
+static struct irq_chip gpio_sifive_irqchip = {
+       .name           = "sifive-gpio",
+       .irq_set_type   = gpio_sifive_irq_set_type,
+       .irq_mask       = gpio_sifive_irq_mask,
+       .irq_unmask     = gpio_sifive_irq_unmask,
+       .irq_enable     = gpio_sifive_irq_enable,
+       .irq_disable    = gpio_sifive_irq_disable,
+};
+
+static void gpio_sifive_irq_handler(struct irq_desc *desc)
+{
+       struct irq_chip *irqchip = irq_desc_get_chip(desc);
+       struct sifive_gpio **self_ptr = irq_desc_get_handler_data(desc);
+       struct sifive_gpio *chip = *self_ptr;
+       int offset = self_ptr - &chip->self_ptr[0];
+        //int offset = desc->irq_data.irq - chip->irq_parent[0];
+       u32 bit = BIT(offset);
+
+       chained_irq_enter(irqchip, desc);
+
+       /* Re-arm the edge irq_types so don't miss the next one */
+       writel(bit, chip->base + GPIO_RISE_IP);
+       writel(bit, chip->base + GPIO_FALL_IP);
+
+       generic_handle_irq(irq_find_mapping(chip->gc.irq.domain, offset));
+
+       /* Re-arm the level irq_types after handling to prevent
spurious refire */
+       writel(bit, chip->base + GPIO_HIGH_IP);
+       writel(bit, chip->base + GPIO_LOW_IP);
+
+       chained_irq_exit(irqchip, desc);
+
+        gpio_sifive_debug("irq handler: offset=%d\n", offset);
+}
+
+#ifdef GPIO_SIFIVE_DEBUG
+static void gpio_sifive_set_irq_enable(struct sifive_gpio *chip,
unsigned offset)
+{
+        u32 bit = BIT(offset);
+
+        /* Switch to input */
+        gpio_sifive_direction_input(&chip->gc, offset);
+
+        /* Clear any sticky pending interrupts */
+        writel(bit, chip->base + GPIO_RISE_IP);
+        writel(bit, chip->base + GPIO_FALL_IP);
+        writel(bit, chip->base + GPIO_HIGH_IP);
+        writel(bit, chip->base + GPIO_LOW_IP);
+
+        /* Enable interrupts */
+        chip->irq_enable |= bit;
+        gpio_sifive_set_ie(chip, offset);
+}
+
+static void gpio_sifive_set_irq_disable(struct sifive_gpio *chip,
unsigned offset)
+{
+        u32 bit = BIT(offset);
+        chip->irq_enable &= ~bit;
+        gpio_sifive_set_ie(chip, offset);
+}
+#endif
+
+static int gpio_sifive_chip_setup(struct platform_device *pdev
+                                        , struct sifive_gpio *chip, int ngpio)
+{
+       struct device *dev = &pdev->dev;
+       int gpio, irq, ret;
+
+       raw_spin_lock_init(&chip->lock);
+       chip->gc.direction_input = gpio_sifive_direction_input;
+       chip->gc.direction_output = gpio_sifive_direction_output;
+       chip->gc.get_direction = gpio_sifive_get_direction;
+       chip->gc.get = gpio_sifive_get_value;
+       chip->gc.set = gpio_sifive_set_value;
+       chip->gc.base = -1;
+       chip->gc.ngpio = ngpio;
+       chip->gc.label = dev_name(dev);
+       chip->gc.parent = dev;
+       chip->gc.owner = THIS_MODULE;
+
+       ret = gpiochip_add_data(&chip->gc, chip);
+       if (ret)
+               return ret;
+
+       /* Disable all GPIO interrupts before enabling parent interrupts */
+        gpio_sifive_set_default(chip, 0);
+
+       ret = gpiochip_irqchip_add(&chip->gc, &gpio_sifive_irqchip, 0
+                                        , handle_simple_irq, IRQ_TYPE_NONE);
+       if (ret) {
+               dev_err(dev, "GPIO: could not add irqchip\n");
+               gpiochip_remove(&chip->gc);
+               return ret;
+       }
+
+       chip->gc.irq.num_parents = ngpio;
+       chip->gc.irq.parents = &chip->irq_parent[0];
+       chip->gc.irq.map = &chip->irq_parent[0];
+
+       for (gpio = 0; gpio < ngpio; ++gpio) {
+               irq = platform_get_irq(pdev, gpio);
+               if (irq < 0) {
+                       dev_err(dev, "GPIO: invalid IRQ\n");
+                       gpiochip_remove(&chip->gc);
+                       return -ENODEV;
+               }
+               chip->self_ptr[gpio] = chip;
+               chip->irq_parent[gpio] = irq;
+               chip->irq_type[gpio] = IRQ_TYPE_LEVEL_HIGH;
+       }
+       for (gpio = 0; gpio < ngpio; ++gpio) {
+               irq = chip->irq_parent[gpio];
+               irq_set_chained_handler_and_data(irq, gpio_sifive_irq_handler
+                                                , &chip->self_ptr[gpio]);
+               irq_set_parent(irq_find_mapping(chip->gc.irq.domain,
gpio), irq);
+       }
+
+        //Enable GPIO Input for default
+        gpio_sifive_set_default(chip, GPIO_ENABLE_BITS);
+        return 0;
+}
+
+static int gpio_sifive_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *node = pdev->dev.of_node;
+       struct sifive_gpio *chip;
+       struct resource *res;
+       int ngpio;
+
+       chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+       if (!chip) {
+               dev_err(dev, "out of memory\n");
+               return -ENOMEM;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       chip->base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(chip->base)) {
+               dev_err(dev, "failed to allocate device memory\n");
+               return PTR_ERR(chip->base);
+       }
+        gpio_sifive_debug_reg(chip);
+
+       if(of_property_read_u32(node, "ngpios", &ngpio))
+               ngpio = of_irq_count(node);
+
+       if (ngpio >= GPIO_MAX_CNT) {
+               dev_err(dev, "too many ngpios.\n");
+               return -EINVAL;
+       }
+
+        if (gpio_sifive_chip_setup(pdev, chip, ngpio) < 0) {
+               dev_err(dev, "failed to gpio sifive setup.\n");
+                return -EINVAL;
+        }
+
+       platform_set_drvdata(pdev, chip);
+       dev_info(dev, "GPIO SiFive driver registered %d GPIOs\n", ngpio);
+
+#ifdef GPIO_SIFIVE_DEBUG
+        gpio_sifive_set_irq_enable(chip, 7);    ///GPIO7 interrupt
enabled for test
+        gpio_sifive_set_irq_disable(chip, 9);   ///GPIO9 interrupt
disabled for test
+#endif
+        gpio_sifive_debug_reg(chip);
+       return 0;
+}
+
+static const struct of_device_id gpio_sifive_match[] = {
+       {
+               .compatible = "sifive,gpio0",
+       },
+       { },
+};
+MODULE_DEVICE_TABLE(of, gpio_sifive_match);
+
+static struct platform_driver gpio_sifive_driver = {
+       .probe          = gpio_sifive_probe,
+       .driver = {
+               .name   = "gpio-sifive",
+               .of_match_table = of_match_ptr(gpio_sifive_match),
+       },
+};
+module_platform_driver(gpio_sifive_driver);
+
+MODULE_DESCRIPTION("SiFive GPIO driver");
+MODULE_LICENSE("GPL v2");

Thanks. Have a nice day.

[-- Attachment #2: gpio-sifive.c --]
[-- Type: text/x-csrc, Size: 13727 bytes --]

/*
 * SiFive GPIO driver
 *
 * Copyright (C) 2018 SiFive, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 * 
 * References:                                                                     
 *   SiFive FU540-C000 manual v1p0, Chapter 17 "GPIO"
 * 
 * 2020 Editted by JaeJoon Jung <rgbi3307@gmail.com>
 *
 */
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/gpio/driver.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/raid/pq.h>

#define GPIO_INPUT_VAL	        0x00
#define GPIO_INPUT_EN	        0x04
#define GPIO_OUTPUT_EN	        0x08
#define GPIO_OUTPUT_VAL	        0x0C
#define GPIO_PULLUP_EN          0x10
#define GPIO_PIN_DS             0x14
#define GPIO_RISE_IE	        0x18
#define GPIO_RISE_IP	        0x1C
#define GPIO_FALL_IE	        0x20
#define GPIO_FALL_IP	        0x24
#define GPIO_HIGH_IE	        0x28
#define GPIO_HIGH_IP	        0x2C
#define GPIO_LOW_IE	        0x30
#define GPIO_LOW_IP	        0x34
#define GPIO_OUTPUT_XOR	        0x40

#define GPIO_MAX_CNT	        32
#define GPIO_ENABLE_BITS        0x83FF

//#define GPIO_SIFIVE_DEBUG
#ifdef GPIO_SIFIVE_DEBUG
        #define gpio_sifive_debug(...)	printk("GPIO: " __VA_ARGS__)
#else
        #define gpio_sifive_debug(...)
#endif

struct sifive_gpio {
	raw_spinlock_t		lock;
	void __iomem		*base;
	struct gpio_chip	gc;
	unsigned int		irq_enable;
	unsigned int		irq_type[GPIO_MAX_CNT];
	unsigned int		irq_parent[GPIO_MAX_CNT];
	struct sifive_gpio	*self_ptr[GPIO_MAX_CNT];
};


static void gpio_sifive_debug_reg(struct sifive_gpio *chip)
{
#ifdef GPIO_SIFIVE_DEBUG
        u32 value;
        int reg;

        if (!chip->base) return;
        gpio_sifive_debug("registers values ---------------------------\n");
        for (reg=GPIO_INPUT_VAL; reg<=GPIO_OUTPUT_XOR; reg+=4) {
                value = readl(chip->base + reg);
                gpio_sifive_debug("reg=[%02X], value=[%08X]\n", reg, value);
        }
        gpio_sifive_debug("irq_enable=[%08X]\n", chip->irq_enable);
        gpio_sifive_debug("irq_type=%d\n", chip->irq_type[0]);
        gpio_sifive_debug("irq_parent=%d\n", chip->irq_parent[0]);
        gpio_sifive_debug("\n");
#endif
}

static void gpio_sifive_assign_bit(void __iomem *ptr, int offset, int value)
{
	// It's frustrating that we are not allowed to use the device atomics
	// which are GUARANTEED to be supported by this device on RISC-V
	u32 bit = BIT(offset);
        u32 old = readl(ptr);

        bit = (value) ? old | bit : old & ~bit;
        writel(bit, ptr);
}

static int gpio_sifive_direction_input(struct gpio_chip *gc, unsigned offset)
{
	struct sifive_gpio *chip = gpiochip_get_data(gc);
	unsigned long flags;

	if (offset >= gc->ngpio)
		return -EINVAL;

	raw_spin_lock_irqsave(&chip->lock, flags);
	gpio_sifive_assign_bit(chip->base + GPIO_OUTPUT_EN, offset, 0);
	gpio_sifive_assign_bit(chip->base + GPIO_INPUT_EN,  offset, 1);
	raw_spin_unlock_irqrestore(&chip->lock, flags);

	return 0;
}

static int gpio_sifive_direction_output(struct gpio_chip *gc, unsigned offset
                                                            , int value)
{
	struct sifive_gpio *chip = gpiochip_get_data(gc);
	unsigned long flags;

	if (offset >= gc->ngpio)
		return -EINVAL;

	raw_spin_lock_irqsave(&chip->lock, flags);
	gpio_sifive_assign_bit(chip->base + GPIO_INPUT_EN,   offset, 0);
	gpio_sifive_assign_bit(chip->base + GPIO_OUTPUT_EN,  offset, 1);
	gpio_sifive_assign_bit(chip->base + GPIO_OUTPUT_VAL, offset, value);
	raw_spin_unlock_irqrestore(&chip->lock, flags);

	return 0;
}

static int gpio_sifive_get_direction(struct gpio_chip *gc, unsigned offset)
{
	struct sifive_gpio *chip = gpiochip_get_data(gc);
        int value;

	if (offset >= gc->ngpio)
		return -EINVAL;

        value = readl(chip->base + GPIO_OUTPUT_EN) & BIT(offset);
        return !value;
}

static int gpio_sifive_get_value(struct gpio_chip *gc, unsigned offset)
{
	struct sifive_gpio *chip = gpiochip_get_data(gc);
        int index, value;

	if (offset >= gc->ngpio)
		return -EINVAL;

        index = gpio_sifive_get_direction(gc, offset) ? 
                                GPIO_INPUT_VAL : GPIO_OUTPUT_VAL;
        value = readl(chip->base + index) & BIT(offset);
        return value;        
}

static void gpio_sifive_set_value(struct gpio_chip *gc, unsigned offset, int value)
{
	struct sifive_gpio *chip = gpiochip_get_data(gc);
	unsigned long flags;
        int index;

	if (offset >= gc->ngpio)
		return;

        index = gpio_sifive_get_direction(gc, offset) ? 
                                GPIO_INPUT_VAL : GPIO_OUTPUT_VAL;
	raw_spin_lock_irqsave(&chip->lock, flags);
	gpio_sifive_assign_bit(chip->base + index, offset, value);
	raw_spin_unlock_irqrestore(&chip->lock, flags);
}

static void gpio_sifive_set_default(struct sifive_gpio *chip, u32 bits)
{
        if (bits == GPIO_ENABLE_BITS) {
                //Input Enable/Disable
                writel(bits, chip->base + GPIO_INPUT_EN);
                return;
        }
        //Interrupts Enable/Disable
        writel(bits, chip->base + GPIO_RISE_IE);
        writel(bits, chip->base + GPIO_FALL_IE);
        writel(bits, chip->base + GPIO_HIGH_IE);
        writel(bits, chip->base + GPIO_LOW_IE);

        writel(bits, chip->base + GPIO_RISE_IP);
        writel(bits, chip->base + GPIO_FALL_IP);
        writel(bits, chip->base + GPIO_HIGH_IP);
        writel(bits, chip->base + GPIO_LOW_IP);

        chip->irq_enable = bits;
}

static void gpio_sifive_set_ie(struct sifive_gpio *chip, int offset)
{
	unsigned long flags;
	unsigned irq_type;

	raw_spin_lock_irqsave(&chip->lock, flags);
	irq_type = (chip->irq_enable & BIT(offset)) ? chip->irq_type[offset] : 0;
	gpio_sifive_assign_bit(chip->base + GPIO_RISE_IE, offset, irq_type & IRQ_TYPE_EDGE_RISING);
	gpio_sifive_assign_bit(chip->base + GPIO_FALL_IE, offset, irq_type & IRQ_TYPE_EDGE_FALLING);
	gpio_sifive_assign_bit(chip->base + GPIO_HIGH_IE, offset, irq_type & IRQ_TYPE_LEVEL_HIGH);
	gpio_sifive_assign_bit(chip->base + GPIO_LOW_IE,  offset, irq_type & IRQ_TYPE_LEVEL_LOW);
	raw_spin_unlock_irqrestore(&chip->lock, flags);
}

static int gpio_sifive_irq_set_type(struct irq_data *d, unsigned irq_type)
{
	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
	struct sifive_gpio *chip = gpiochip_get_data(gc);
	int offset = irqd_to_hwirq(d);

	if (offset < 0 || offset >= gc->ngpio)
		return -EINVAL;

	chip->irq_type[offset] = irq_type;
	gpio_sifive_set_ie(chip, offset);

	return 0;
}

/* chained_irq_{enter,exit} already mask the parent */
static void gpio_sifive_irq_mask(struct irq_data *d) { }
static void gpio_sifive_irq_unmask(struct irq_data *d) { }

static void gpio_sifive_irq_enable(struct irq_data *d)
{
	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
	struct sifive_gpio *chip = gpiochip_get_data(gc);
	int offset = irqd_to_hwirq(d) % GPIO_MAX_CNT; // must not fail
	u32 bit = BIT(offset);

	/* Switch to input */
	gpio_sifive_direction_input(gc, offset);

	/* Clear any sticky pending interrupts */
	writel(bit, chip->base + GPIO_RISE_IP);
	writel(bit, chip->base + GPIO_FALL_IP);
	writel(bit, chip->base + GPIO_HIGH_IP);
	writel(bit, chip->base + GPIO_LOW_IP);

	/* Enable interrupts */
        chip->irq_enable |= bit;
	gpio_sifive_set_ie(chip, offset);
}

static void gpio_sifive_irq_disable(struct irq_data *d)
{
	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
	struct sifive_gpio *chip = gpiochip_get_data(gc);
	int offset = irqd_to_hwirq(d) % GPIO_MAX_CNT; // must not fail
	u32 bit = BIT(offset);

	/* Disable interrupts */
        chip->irq_enable &= ~bit;
	gpio_sifive_set_ie(chip, offset);
}

static struct irq_chip gpio_sifive_irqchip = {
	.name		= "sifive-gpio",
	.irq_set_type	= gpio_sifive_irq_set_type,
	.irq_mask	= gpio_sifive_irq_mask,
	.irq_unmask	= gpio_sifive_irq_unmask,
	.irq_enable	= gpio_sifive_irq_enable,
	.irq_disable	= gpio_sifive_irq_disable,
};

static void gpio_sifive_irq_handler(struct irq_desc *desc)
{
	struct irq_chip *irqchip = irq_desc_get_chip(desc);
	struct sifive_gpio **self_ptr = irq_desc_get_handler_data(desc);
	struct sifive_gpio *chip = *self_ptr;
	int offset = self_ptr - &chip->self_ptr[0];
        //int offset = desc->irq_data.irq - chip->irq_parent[0];
	u32 bit = BIT(offset);

	chained_irq_enter(irqchip, desc);

	/* Re-arm the edge irq_types so don't miss the next one */
	writel(bit, chip->base + GPIO_RISE_IP);
	writel(bit, chip->base + GPIO_FALL_IP);

	generic_handle_irq(irq_find_mapping(chip->gc.irq.domain, offset));

	/* Re-arm the level irq_types after handling to prevent spurious refire */
	writel(bit, chip->base + GPIO_HIGH_IP);
	writel(bit, chip->base + GPIO_LOW_IP);

	chained_irq_exit(irqchip, desc);

        gpio_sifive_debug("irq handler: offset=%d\n", offset);
}

#ifdef GPIO_SIFIVE_DEBUG
static void gpio_sifive_set_irq_enable(struct sifive_gpio *chip, unsigned offset)                          
{
        u32 bit = BIT(offset);

        /* Switch to input */
        gpio_sifive_direction_input(&chip->gc, offset);

        /* Clear any sticky pending interrupts */
        writel(bit, chip->base + GPIO_RISE_IP);
        writel(bit, chip->base + GPIO_FALL_IP);
        writel(bit, chip->base + GPIO_HIGH_IP);
        writel(bit, chip->base + GPIO_LOW_IP);

        /* Enable interrupts */
        chip->irq_enable |= bit;
        gpio_sifive_set_ie(chip, offset);
}                                                                               
                                                                                
static void gpio_sifive_set_irq_disable(struct sifive_gpio *chip, unsigned offset)                          
{
        u32 bit = BIT(offset);
        chip->irq_enable &= ~bit;
        gpio_sifive_set_ie(chip, offset);
}
#endif

static int gpio_sifive_chip_setup(struct platform_device *pdev
                                        , struct sifive_gpio *chip, int ngpio)
{
	struct device *dev = &pdev->dev;
	int gpio, irq, ret;

	raw_spin_lock_init(&chip->lock);
	chip->gc.direction_input = gpio_sifive_direction_input;
	chip->gc.direction_output = gpio_sifive_direction_output;
	chip->gc.get_direction = gpio_sifive_get_direction;
	chip->gc.get = gpio_sifive_get_value;
	chip->gc.set = gpio_sifive_set_value;
	chip->gc.base = -1;
	chip->gc.ngpio = ngpio;
	chip->gc.label = dev_name(dev);
	chip->gc.parent = dev;
	chip->gc.owner = THIS_MODULE;

	ret = gpiochip_add_data(&chip->gc, chip);
	if (ret)
		return ret;

	/* Disable all GPIO interrupts before enabling parent interrupts */
        gpio_sifive_set_default(chip, 0);

	ret = gpiochip_irqchip_add(&chip->gc, &gpio_sifive_irqchip, 0
                                        , handle_simple_irq, IRQ_TYPE_NONE);
	if (ret) {
		dev_err(dev, "GPIO: could not add irqchip\n");
		gpiochip_remove(&chip->gc);
		return ret;
	}

	chip->gc.irq.num_parents = ngpio;
	chip->gc.irq.parents = &chip->irq_parent[0];
	chip->gc.irq.map = &chip->irq_parent[0];

	for (gpio = 0; gpio < ngpio; ++gpio) {
		irq = platform_get_irq(pdev, gpio);
		if (irq < 0) {
			dev_err(dev, "GPIO: invalid IRQ\n");
			gpiochip_remove(&chip->gc);
			return -ENODEV;
		}
		chip->self_ptr[gpio] = chip;
		chip->irq_parent[gpio] = irq;
		chip->irq_type[gpio] = IRQ_TYPE_LEVEL_HIGH;
	}
	for (gpio = 0; gpio < ngpio; ++gpio) {
		irq = chip->irq_parent[gpio];
		irq_set_chained_handler_and_data(irq, gpio_sifive_irq_handler
                                                , &chip->self_ptr[gpio]);
		irq_set_parent(irq_find_mapping(chip->gc.irq.domain, gpio), irq);
	}

        //Enable GPIO Input for default
        gpio_sifive_set_default(chip, GPIO_ENABLE_BITS);
        return 0;
}

static int gpio_sifive_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct device_node *node = pdev->dev.of_node;
	struct sifive_gpio *chip;
	struct resource *res;
	int ngpio;

	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
	if (!chip) {
		dev_err(dev, "out of memory\n");
		return -ENOMEM;
	}

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	chip->base = devm_ioremap_resource(dev, res);
	if (IS_ERR(chip->base)) {
		dev_err(dev, "failed to allocate device memory\n");
		return PTR_ERR(chip->base);
	}
        gpio_sifive_debug_reg(chip);

	if(of_property_read_u32(node, "ngpios", &ngpio)) 
	        ngpio = of_irq_count(node);
	
	if (ngpio >= GPIO_MAX_CNT) {
		dev_err(dev, "too many ngpios.\n");
		return -EINVAL;
	}

        if (gpio_sifive_chip_setup(pdev, chip, ngpio) < 0) {
		dev_err(dev, "failed to gpio sifive setup.\n");
                return -EINVAL;
        }

	platform_set_drvdata(pdev, chip);
	dev_info(dev, "GPIO SiFive driver registered %d GPIOs\n", ngpio);

#ifdef GPIO_SIFIVE_DEBUG
        gpio_sifive_set_irq_enable(chip, 7);    ///GPIO7 interrupt enabled for test                          
        gpio_sifive_set_irq_disable(chip, 9);   ///GPIO9 interrupt disabled for test                     
#endif
        gpio_sifive_debug_reg(chip);
	return 0;
}

static const struct of_device_id gpio_sifive_match[] = {
	{
		.compatible = "sifive,gpio0",
	},
	{ },
};
MODULE_DEVICE_TABLE(of, gpio_sifive_match);

static struct platform_driver gpio_sifive_driver = {
	.probe		= gpio_sifive_probe,
	.driver = {
		.name	= "gpio-sifive",
		.of_match_table = of_match_ptr(gpio_sifive_match),
	},
};
module_platform_driver(gpio_sifive_driver);

MODULE_DESCRIPTION("SiFive GPIO driver");
MODULE_LICENSE("GPL v2");

             reply	other threads:[~2020-01-28  6:42 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-01-28  6:41 JaeJoon Jung [this message]
2020-01-28  6:50 ` [PATCH] RISC-V: Add and Update gpio-sifive.c for SiFive FU540 GPIO David Abdurachmanov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CAHOvCC5h1cKJzYx4sm-U0HDY9LGkhqWYGh+VaaOaJidP=Jkerw@mail.gmail.com' \
    --to=rgbi3307@gmail.com \
    --cc=andrew@sifive.com \
    --cc=anup@brainfault.org \
    --cc=linux-riscv@lists.infradead.org \
    --cc=palmer@dabbelt.com \
    --cc=paul.walmsley@sifive.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.