drm/amd/display: Remove unnecessary NULL check
[linux-2.6-microblaze.git] / drivers / gpio / gpio-aspeed-sgpio.c
index 3aa4593..64e54f8 100644 (file)
 #include <linux/spinlock.h>
 #include <linux/string.h>
 
-#define MAX_NR_SGPIO                   80
+/*
+ * MAX_NR_HW_GPIO represents the number of actual hardware-supported GPIOs (ie,
+ * slots within the clocked serial GPIO data). Since each HW GPIO is both an
+ * input and an output, we provide MAX_NR_HW_GPIO * 2 lines on our gpiochip
+ * device.
+ *
+ * We use SGPIO_OUTPUT_OFFSET to define the split between the inputs and
+ * outputs; the inputs start at line 0, the outputs start at OUTPUT_OFFSET.
+ */
+#define MAX_NR_HW_SGPIO                        80
+#define SGPIO_OUTPUT_OFFSET            MAX_NR_HW_SGPIO
 
 #define ASPEED_SGPIO_CTRL              0x54
 
@@ -30,8 +40,8 @@ struct aspeed_sgpio {
        struct clk *pclk;
        spinlock_t lock;
        void __iomem *base;
-       uint32_t dir_in[3];
        int irq;
+       int n_sgpio;
 };
 
 struct aspeed_sgpio_bank {
@@ -111,31 +121,69 @@ static void __iomem *bank_reg(struct aspeed_sgpio *gpio,
        }
 }
 
-#define GPIO_BANK(x)    ((x) >> 5)
-#define GPIO_OFFSET(x)  ((x) & 0x1f)
+#define GPIO_BANK(x)    ((x % SGPIO_OUTPUT_OFFSET) >> 5)
+#define GPIO_OFFSET(x)  ((x % SGPIO_OUTPUT_OFFSET) & 0x1f)
 #define GPIO_BIT(x)     BIT(GPIO_OFFSET(x))
 
 static const struct aspeed_sgpio_bank *to_bank(unsigned int offset)
 {
-       unsigned int bank = GPIO_BANK(offset);
+       unsigned int bank;
+
+       bank = GPIO_BANK(offset);
 
        WARN_ON(bank >= ARRAY_SIZE(aspeed_sgpio_banks));
        return &aspeed_sgpio_banks[bank];
 }
 
+static int aspeed_sgpio_init_valid_mask(struct gpio_chip *gc,
+               unsigned long *valid_mask, unsigned int ngpios)
+{
+       struct aspeed_sgpio *sgpio = gpiochip_get_data(gc);
+       int n = sgpio->n_sgpio;
+       int c = SGPIO_OUTPUT_OFFSET - n;
+
+       WARN_ON(ngpios < MAX_NR_HW_SGPIO * 2);
+
+       /* input GPIOs in the lower range */
+       bitmap_set(valid_mask, 0, n);
+       bitmap_clear(valid_mask, n, c);
+
+       /* output GPIOS above SGPIO_OUTPUT_OFFSET */
+       bitmap_set(valid_mask, SGPIO_OUTPUT_OFFSET, n);
+       bitmap_clear(valid_mask, SGPIO_OUTPUT_OFFSET + n, c);
+
+       return 0;
+}
+
+static void aspeed_sgpio_irq_init_valid_mask(struct gpio_chip *gc,
+               unsigned long *valid_mask, unsigned int ngpios)
+{
+       struct aspeed_sgpio *sgpio = gpiochip_get_data(gc);
+       int n = sgpio->n_sgpio;
+
+       WARN_ON(ngpios < MAX_NR_HW_SGPIO * 2);
+
+       /* input GPIOs in the lower range */
+       bitmap_set(valid_mask, 0, n);
+       bitmap_clear(valid_mask, n, ngpios - n);
+}
+
+static bool aspeed_sgpio_is_input(unsigned int offset)
+{
+       return offset < SGPIO_OUTPUT_OFFSET;
+}
+
 static int aspeed_sgpio_get(struct gpio_chip *gc, unsigned int offset)
 {
        struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
        const struct aspeed_sgpio_bank *bank = to_bank(offset);
        unsigned long flags;
        enum aspeed_sgpio_reg reg;
-       bool is_input;
        int rc = 0;
 
        spin_lock_irqsave(&gpio->lock, flags);
 
-       is_input = gpio->dir_in[GPIO_BANK(offset)] & GPIO_BIT(offset);
-       reg = is_input ? reg_val : reg_rdata;
+       reg = aspeed_sgpio_is_input(offset) ? reg_val : reg_rdata;
        rc = !!(ioread32(bank_reg(gpio, bank, reg)) & GPIO_BIT(offset));
 
        spin_unlock_irqrestore(&gpio->lock, flags);
@@ -143,22 +191,31 @@ static int aspeed_sgpio_get(struct gpio_chip *gc, unsigned int offset)
        return rc;
 }
 
-static void sgpio_set_value(struct gpio_chip *gc, unsigned int offset, int val)
+static int sgpio_set_value(struct gpio_chip *gc, unsigned int offset, int val)
 {
        struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
        const struct aspeed_sgpio_bank *bank = to_bank(offset);
-       void __iomem *addr;
+       void __iomem *addr_r, *addr_w;
        u32 reg = 0;
 
-       addr = bank_reg(gpio, bank, reg_val);
-       reg = ioread32(addr);
+       if (aspeed_sgpio_is_input(offset))
+               return -EINVAL;
+
+       /* Since this is an output, read the cached value from rdata, then
+        * update val. */
+       addr_r = bank_reg(gpio, bank, reg_rdata);
+       addr_w = bank_reg(gpio, bank, reg_val);
+
+       reg = ioread32(addr_r);
 
        if (val)
                reg |= GPIO_BIT(offset);
        else
                reg &= ~GPIO_BIT(offset);
 
-       iowrite32(reg, addr);
+       iowrite32(reg, addr_w);
+
+       return 0;
 }
 
 static void aspeed_sgpio_set(struct gpio_chip *gc, unsigned int offset, int val)
@@ -175,43 +232,28 @@ static void aspeed_sgpio_set(struct gpio_chip *gc, unsigned int offset, int val)
 
 static int aspeed_sgpio_dir_in(struct gpio_chip *gc, unsigned int offset)
 {
-       struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
-       unsigned long flags;
-
-       spin_lock_irqsave(&gpio->lock, flags);
-       gpio->dir_in[GPIO_BANK(offset)] |= GPIO_BIT(offset);
-       spin_unlock_irqrestore(&gpio->lock, flags);
-
-       return 0;
+       return aspeed_sgpio_is_input(offset) ? 0 : -EINVAL;
 }
 
 static int aspeed_sgpio_dir_out(struct gpio_chip *gc, unsigned int offset, int val)
 {
        struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
        unsigned long flags;
+       int rc;
 
-       spin_lock_irqsave(&gpio->lock, flags);
-
-       gpio->dir_in[GPIO_BANK(offset)] &= ~GPIO_BIT(offset);
-       sgpio_set_value(gc, offset, val);
+       /* No special action is required for setting the direction; we'll
+        * error-out in sgpio_set_value if this isn't an output GPIO */
 
+       spin_lock_irqsave(&gpio->lock, flags);
+       rc = sgpio_set_value(gc, offset, val);
        spin_unlock_irqrestore(&gpio->lock, flags);
 
-       return 0;
+       return rc;
 }
 
 static int aspeed_sgpio_get_direction(struct gpio_chip *gc, unsigned int offset)
 {
-       int dir_status;
-       struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
-       unsigned long flags;
-
-       spin_lock_irqsave(&gpio->lock, flags);
-       dir_status = gpio->dir_in[GPIO_BANK(offset)] & GPIO_BIT(offset);
-       spin_unlock_irqrestore(&gpio->lock, flags);
-
-       return dir_status;
-
+       return !!aspeed_sgpio_is_input(offset);
 }
 
 static void irqd_to_aspeed_sgpio_data(struct irq_data *d,
@@ -402,6 +444,7 @@ static int aspeed_sgpio_setup_irqs(struct aspeed_sgpio *gpio,
 
        irq = &gpio->chip.irq;
        irq->chip = &aspeed_sgpio_irqchip;
+       irq->init_valid_mask = aspeed_sgpio_irq_init_valid_mask;
        irq->handler = handle_bad_irq;
        irq->default_type = IRQ_TYPE_NONE;
        irq->parent_handler = aspeed_sgpio_irq_handler;
@@ -409,17 +452,15 @@ static int aspeed_sgpio_setup_irqs(struct aspeed_sgpio *gpio,
        irq->parents = &gpio->irq;
        irq->num_parents = 1;
 
-       /* set IRQ settings and Enable Interrupt */
+       /* Apply default IRQ settings */
        for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) {
                bank = &aspeed_sgpio_banks[i];
                /* set falling or level-low irq */
                iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_type0));
                /* trigger type is edge */
                iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_type1));
-               /* dual edge trigger mode. */
-               iowrite32(0xffffffff, bank_reg(gpio, bank, reg_irq_type2));
-               /* enable irq */
-               iowrite32(0xffffffff, bank_reg(gpio, bank, reg_irq_enable));
+               /* single edge trigger */
+               iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_type2));
        }
 
        return 0;
@@ -452,11 +493,12 @@ static int __init aspeed_sgpio_probe(struct platform_device *pdev)
        if (rc < 0) {
                dev_err(&pdev->dev, "Could not read ngpios property\n");
                return -EINVAL;
-       } else if (nr_gpios > MAX_NR_SGPIO) {
+       } else if (nr_gpios > MAX_NR_HW_SGPIO) {
                dev_err(&pdev->dev, "Number of GPIOs exceeds the maximum of %d: %d\n",
-                       MAX_NR_SGPIO, nr_gpios);
+                       MAX_NR_HW_SGPIO, nr_gpios);
                return -EINVAL;
        }
+       gpio->n_sgpio = nr_gpios;
 
        rc = of_property_read_u32(pdev->dev.of_node, "bus-frequency", &sgpio_freq);
        if (rc < 0) {
@@ -497,7 +539,8 @@ static int __init aspeed_sgpio_probe(struct platform_device *pdev)
        spin_lock_init(&gpio->lock);
 
        gpio->chip.parent = &pdev->dev;
-       gpio->chip.ngpio = nr_gpios;
+       gpio->chip.ngpio = MAX_NR_HW_SGPIO * 2;
+       gpio->chip.init_valid_mask = aspeed_sgpio_init_valid_mask;
        gpio->chip.direction_input = aspeed_sgpio_dir_in;
        gpio->chip.direction_output = aspeed_sgpio_dir_out;
        gpio->chip.get_direction = aspeed_sgpio_get_direction;
@@ -509,9 +552,6 @@ static int __init aspeed_sgpio_probe(struct platform_device *pdev)
        gpio->chip.label = dev_name(&pdev->dev);
        gpio->chip.base = -1;
 
-       /* set all SGPIO pins as input (1). */
-       memset(gpio->dir_in, 0xff, sizeof(gpio->dir_in));
-
        aspeed_sgpio_setup_irqs(gpio, pdev);
 
        rc = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio);