diff options
Diffstat (limited to 'drivers/gpio/gpio-aspeed-sgpio.c')
-rw-r--r-- | drivers/gpio/gpio-aspeed-sgpio.c | 178 |
1 files changed, 110 insertions, 68 deletions
diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c index a99ece15db95..10f303d15225 100644 --- a/drivers/gpio/gpio-aspeed-sgpio.c +++ b/drivers/gpio/gpio-aspeed-sgpio.c @@ -17,37 +17,30 @@ #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; |