Merge branch 'misc.namei' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[linux-2.6-microblaze.git] / drivers / gpio / gpio-aspeed-sgpio.c
index a99ece1..10f303d 100644 (file)
 #include <linux/spinlock.h>
 #include <linux/string.h>
 
-/*
- * 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
 
-#define ASPEED_SGPIO_PINS_MASK         GENMASK(9, 6)
 #define ASPEED_SGPIO_CLK_DIV_MASK      GENMASK(31, 16)
 #define ASPEED_SGPIO_ENABLE            BIT(0)
+#define ASPEED_SGPIO_PINS_SHIFT                6
+
+struct aspeed_sgpio_pdata {
+       const u32 pin_mask;
+};
 
 struct aspeed_sgpio {
        struct gpio_chip chip;
+       struct irq_chip intc;
        struct clk *pclk;
        spinlock_t lock;
        void __iomem *base;
        int irq;
-       int n_sgpio;
 };
 
 struct aspeed_sgpio_bank {
-       uint16_t    val_regs;
-       uint16_t    rdata_reg;
-       uint16_t    irq_regs;
+       u16    val_regs;
+       u16    rdata_reg;
+       u16    irq_regs;
+       u16    tolerance_regs;
        const char  names[4][3];
 };
 
@@ -63,19 +56,29 @@ static const struct aspeed_sgpio_bank aspeed_sgpio_banks[] = {
                .val_regs = 0x0000,
                .rdata_reg = 0x0070,
                .irq_regs = 0x0004,
+               .tolerance_regs = 0x0018,
                .names = { "A", "B", "C", "D" },
        },
        {
                .val_regs = 0x001C,
                .rdata_reg = 0x0074,
                .irq_regs = 0x0020,
+               .tolerance_regs = 0x0034,
                .names = { "E", "F", "G", "H" },
        },
        {
                .val_regs = 0x0038,
                .rdata_reg = 0x0078,
                .irq_regs = 0x003C,
-               .names = { "I", "J" },
+               .tolerance_regs = 0x0050,
+               .names = { "I", "J", "K", "L" },
+       },
+       {
+               .val_regs = 0x0090,
+               .rdata_reg = 0x007C,
+               .irq_regs = 0x0094,
+               .tolerance_regs = 0x00A8,
+               .names = { "M", "N", "O", "P" },
        },
 };
 
@@ -87,6 +90,7 @@ enum aspeed_sgpio_reg {
        reg_irq_type1,
        reg_irq_type2,
        reg_irq_status,
+       reg_tolerance,
 };
 
 #define GPIO_VAL_VALUE      0x00
@@ -115,15 +119,17 @@ static void __iomem *bank_reg(struct aspeed_sgpio *gpio,
                return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE2;
        case reg_irq_status:
                return gpio->base + bank->irq_regs + GPIO_IRQ_STATUS;
+       case reg_tolerance:
+               return gpio->base + bank->tolerance_regs;
        default:
                /* acturally if code runs to here, it's an error case */
                BUG();
        }
 }
 
-#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))
+#define GPIO_BANK(x)    ((x) >> 6)
+#define GPIO_OFFSET(x)  ((x) & GENMASK(5, 0))
+#define GPIO_BIT(x)     BIT(GPIO_OFFSET(x) >> 1)
 
 static const struct aspeed_sgpio_bank *to_bank(unsigned int offset)
 {
@@ -138,39 +144,25 @@ static const struct aspeed_sgpio_bank *to_bank(unsigned int offset)
 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);
-
+       bitmap_set(valid_mask, 0, ngpios);
        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;
+       unsigned int i;
 
-       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);
+       /* input GPIOs are even bits */
+       for (i = 0; i < ngpios; i++) {
+               if (i % 2)
+                       clear_bit(i, valid_mask);
+       }
 }
 
 static bool aspeed_sgpio_is_input(unsigned int offset)
 {
-       return offset < SGPIO_OUTPUT_OFFSET;
+       return !(offset % 2);
 }
 
 static int aspeed_sgpio_get(struct gpio_chip *gc, unsigned int offset)
@@ -409,14 +401,6 @@ static void aspeed_sgpio_irq_handler(struct irq_desc *desc)
        chained_irq_exit(ic, desc);
 }
 
-static struct irq_chip aspeed_sgpio_irqchip = {
-       .name       = "aspeed-sgpio",
-       .irq_ack    = aspeed_sgpio_irq_ack,
-       .irq_mask   = aspeed_sgpio_irq_mask,
-       .irq_unmask = aspeed_sgpio_irq_unmask,
-       .irq_set_type   = aspeed_sgpio_set_type,
-};
-
 static int aspeed_sgpio_setup_irqs(struct aspeed_sgpio *gpio,
                                   struct platform_device *pdev)
 {
@@ -439,8 +423,14 @@ static int aspeed_sgpio_setup_irqs(struct aspeed_sgpio *gpio,
                iowrite32(0xffffffff, bank_reg(gpio, bank, reg_irq_status));
        }
 
+       gpio->intc.name = dev_name(&pdev->dev);
+       gpio->intc.irq_ack = aspeed_sgpio_irq_ack;
+       gpio->intc.irq_mask = aspeed_sgpio_irq_mask;
+       gpio->intc.irq_unmask = aspeed_sgpio_irq_unmask;
+       gpio->intc.irq_set_type = aspeed_sgpio_set_type;
+
        irq = &gpio->chip.irq;
-       irq->chip = &aspeed_sgpio_irqchip;
+       irq->chip = &gpio->intc;
        irq->init_valid_mask = aspeed_sgpio_irq_init_valid_mask;
        irq->handler = handle_bad_irq;
        irq->default_type = IRQ_TYPE_NONE;
@@ -463,9 +453,56 @@ static int aspeed_sgpio_setup_irqs(struct aspeed_sgpio *gpio,
        return 0;
 }
 
+static const struct aspeed_sgpio_pdata ast2400_sgpio_pdata = {
+       .pin_mask = GENMASK(9, 6),
+};
+
+static int aspeed_sgpio_reset_tolerance(struct gpio_chip *chip,
+                                       unsigned int offset, bool enable)
+{
+       struct aspeed_sgpio *gpio = gpiochip_get_data(chip);
+       unsigned long flags;
+       void __iomem *reg;
+       u32 val;
+
+       reg = bank_reg(gpio, to_bank(offset), reg_tolerance);
+
+       spin_lock_irqsave(&gpio->lock, flags);
+
+       val = readl(reg);
+
+       if (enable)
+               val |= GPIO_BIT(offset);
+       else
+               val &= ~GPIO_BIT(offset);
+
+       writel(val, reg);
+
+       spin_unlock_irqrestore(&gpio->lock, flags);
+
+       return 0;
+}
+
+static int aspeed_sgpio_set_config(struct gpio_chip *chip, unsigned int offset,
+                                  unsigned long config)
+{
+       unsigned long param = pinconf_to_config_param(config);
+       u32 arg = pinconf_to_config_argument(config);
+
+       if (param == PIN_CONFIG_PERSIST_STATE)
+               return aspeed_sgpio_reset_tolerance(chip, offset, arg);
+
+       return -ENOTSUPP;
+}
+
+static const struct aspeed_sgpio_pdata ast2600_sgpiom_pdata = {
+       .pin_mask = GENMASK(10, 6),
+};
+
 static const struct of_device_id aspeed_sgpio_of_table[] = {
-       { .compatible = "aspeed,ast2400-sgpio" },
-       { .compatible = "aspeed,ast2500-sgpio" },
+       { .compatible = "aspeed,ast2400-sgpio", .data = &ast2400_sgpio_pdata, },
+       { .compatible = "aspeed,ast2500-sgpio", .data = &ast2400_sgpio_pdata, },
+       { .compatible = "aspeed,ast2600-sgpiom", .data = &ast2600_sgpiom_pdata, },
        {}
 };
 
@@ -473,10 +510,11 @@ MODULE_DEVICE_TABLE(of, aspeed_sgpio_of_table);
 
 static int __init aspeed_sgpio_probe(struct platform_device *pdev)
 {
+       u32 nr_gpios, sgpio_freq, sgpio_clk_div, gpio_cnt_regval, pin_mask;
+       const struct aspeed_sgpio_pdata *pdata;
        struct aspeed_sgpio *gpio;
-       u32 nr_gpios, sgpio_freq, sgpio_clk_div;
-       int rc;
        unsigned long apb_freq;
+       int rc;
 
        gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
        if (!gpio)
@@ -486,18 +524,23 @@ static int __init aspeed_sgpio_probe(struct platform_device *pdev)
        if (IS_ERR(gpio->base))
                return PTR_ERR(gpio->base);
 
-       rc = of_property_read_u32(pdev->dev.of_node, "ngpios", &nr_gpios);
+       pdata = device_get_match_data(&pdev->dev);
+       if (!pdata)
+               return -EINVAL;
+
+       pin_mask = pdata->pin_mask;
+
+       rc = device_property_read_u32(&pdev->dev, "ngpios", &nr_gpios);
        if (rc < 0) {
                dev_err(&pdev->dev, "Could not read ngpios property\n");
                return -EINVAL;
-       } else if (nr_gpios > MAX_NR_HW_SGPIO) {
-               dev_err(&pdev->dev, "Number of GPIOs exceeds the maximum of %d: %d\n",
-                       MAX_NR_HW_SGPIO, nr_gpios);
+       } else if (nr_gpios % 8) {
+               dev_err(&pdev->dev, "Number of GPIOs not multiple of 8: %d\n",
+                       nr_gpios);
                return -EINVAL;
        }
-       gpio->n_sgpio = nr_gpios;
 
-       rc = of_property_read_u32(pdev->dev.of_node, "bus-frequency", &sgpio_freq);
+       rc = device_property_read_u32(&pdev->dev, "bus-frequency", &sgpio_freq);
        if (rc < 0) {
                dev_err(&pdev->dev, "Could not read bus-frequency property\n");
                return -EINVAL;
@@ -528,15 +571,14 @@ static int __init aspeed_sgpio_probe(struct platform_device *pdev)
        if (sgpio_clk_div > (1 << 16) - 1)
                return -EINVAL;
 
-       iowrite32(FIELD_PREP(ASPEED_SGPIO_CLK_DIV_MASK, sgpio_clk_div) |
-                 FIELD_PREP(ASPEED_SGPIO_PINS_MASK, (nr_gpios / 8)) |
-                 ASPEED_SGPIO_ENABLE,
-                 gpio->base + ASPEED_SGPIO_CTRL);
+       gpio_cnt_regval = ((nr_gpios / 8) << ASPEED_SGPIO_PINS_SHIFT) & pin_mask;
+       iowrite32(FIELD_PREP(ASPEED_SGPIO_CLK_DIV_MASK, sgpio_clk_div) | gpio_cnt_regval |
+                 ASPEED_SGPIO_ENABLE, gpio->base + ASPEED_SGPIO_CTRL);
 
        spin_lock_init(&gpio->lock);
 
        gpio->chip.parent = &pdev->dev;
-       gpio->chip.ngpio = MAX_NR_HW_SGPIO * 2;
+       gpio->chip.ngpio = nr_gpios * 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;
@@ -545,7 +587,7 @@ static int __init aspeed_sgpio_probe(struct platform_device *pdev)
        gpio->chip.free = NULL;
        gpio->chip.get = aspeed_sgpio_get;
        gpio->chip.set = aspeed_sgpio_set;
-       gpio->chip.set_config = NULL;
+       gpio->chip.set_config = aspeed_sgpio_set_config;
        gpio->chip.label = dev_name(&pdev->dev);
        gpio->chip.base = -1;