diff -drupN a/drivers/pinctrl/pinctrl-ingenic.c b/drivers/pinctrl/pinctrl-ingenic.c --- a/drivers/pinctrl/pinctrl-ingenic.c 1970-01-01 03:00:00.000000000 +0300 +++ b/drivers/pinctrl/pinctrl-ingenic.c 2022-06-09 05:02:33.000000000 +0300 @@ -0,0 +1,1790 @@ +/* + * pinctrl/ingenic/pinctrl-ingenic.c + * + * Copyright 2015 Ingenic Semiconductor Co.,Ltd + * + * Author: cli + * + * redistribute from drivers/pinctrl/pinctrl-samsung.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pinctrl-ingenic.h" + +static struct ingenic_pinctrl *gpctl = NULL; +static const struct ingenic_priv common_priv_data[]; +static const struct of_device_id ingenic_pinctrl_dt_match[]; + +static void ingenic_gpio_set_func_normal(struct ingenic_gpio_chip *chip, + enum gpio_function func, u32 pins) +{ + unsigned long flags, func_tmp = func; + spin_lock_irqsave(&chip->lock, flags); + + if(func_tmp & 0x10){ + func = GPIO_INT_FE; + } + if (func & 0x8) + ingenic_gpio_writel(chip, PxINTS, pins); + else + ingenic_gpio_writel(chip, PxINTC, pins); + if (func & 0x4) + ingenic_gpio_writel(chip, PxMSKS, pins); + else + ingenic_gpio_writel(chip, PxMSKC, pins); + + if (func & 0x2) + ingenic_gpio_writel(chip, PxPAT1S, pins); + else + ingenic_gpio_writel(chip, PxPAT1C, pins); + + if(func_tmp & 0x10){ + int old, new, timeout = 10; + do { + old = ingenic_gpio_readl(chip, PxPIN) & pins; + if (old) + func = GPIO_INT_FE; + else + func = GPIO_INT_RE; + if (func & 0x1) + ingenic_gpio_writel(chip, PxPAT0S, pins); + else + ingenic_gpio_writel(chip, PxPAT0C, pins); + new = ingenic_gpio_readl(chip, PxPIN) & pins; + timeout --; + }while(old != new && timeout); + if(!timeout) + pr_err("pins %x function %d failed\n", pins, func); + } else { + if (func & 0x1) + ingenic_gpio_writel(chip, PxPAT0S, pins); + else + ingenic_gpio_writel(chip, PxPAT0C, pins); + } + ingenic_gpio_writel(chip, PxFLGC, pins); + spin_unlock_irqrestore(&chip->lock, flags); +} + +static void ingenic_gpio_fill_func_shadow(struct ingenic_gpio_chip *chip, + enum gpio_function func, u32 pins) +{ + if (func & 0x8) + ingenic_gpio_shadow_fill(chip, PxINTS, pins); + else + ingenic_gpio_shadow_fill(chip, PxINTC, pins); + if (func & 0x4) + ingenic_gpio_shadow_fill(chip, PxMSKS, pins); + else + ingenic_gpio_shadow_fill(chip, PxMSKC, pins); + if (func & 0x2) + ingenic_gpio_shadow_fill(chip, PxPAT1S, pins); + else + ingenic_gpio_shadow_fill(chip, PxPAT1C, pins); + if (func & 0x1) + ingenic_gpio_shadow_fill(chip, PxPAT0S, pins); + else + ingenic_gpio_shadow_fill(chip, PxPAT0C, pins); +} + +static void ingenic_gpio_set_func_shadow(struct ingenic_gpio_chip *chip, + enum gpio_function func, u32 pins) +{ + struct ingenic_pinctrl *pctl = chip->pctl; + unsigned long flags; + unsigned long func_tmp = func; + + spin_lock_irqsave(&pctl->shadow_lock, flags); + if(func_tmp & 0x10){ + int old, new, timeout = 10; + do { + old = ingenic_gpio_readl(chip, PxPIN) & pins; + if (old) + func = GPIO_INT_FE; + else + func = GPIO_INT_RE; + ingenic_gpio_fill_func_shadow(chip, func, pins); + ingenic_gpio_shadow_writel(chip); + new = ingenic_gpio_readl(chip, PxPIN) & pins; + timeout --; + }while(old != new && timeout); + if(!timeout) + pr_err("pins %x function %d failed\n",pins, func); + } else { + ingenic_gpio_fill_func_shadow(chip, func, pins); + ingenic_gpio_shadow_writel(chip); + } + spin_unlock_irqrestore(&pctl->shadow_lock, flags); +} + +/************************************************************** + * Set GPIO Function + * chip: gpio chip + * have_shadow: soc support shadow register ot not + * func: function select + * pins: pins bitmap + **************************************************************/ +static void ingenic_gpio_set_func(struct ingenic_gpio_chip *chip, + bool have_shadow, enum gpio_function func, + u32 pins) +{ + if (have_shadow) + ingenic_gpio_set_func_shadow(chip, func, pins); + else + ingenic_gpio_set_func_normal(chip, func, pins); +} + +static void ingenic_gpio_set_pull(struct ingenic_gpio_chip *chip, + u32 pins, unsigned int state) +{ + const struct ingenic_priv *priv = chip->pctl->priv; + + if(priv->pull_tristate) { + if(state == INGENIC_GPIO_PULLUP) { + ingenic_gpio_writel(chip, PxPDENC, pins); + ingenic_gpio_writel(chip, PxPUENS, pins); + } else if(state == INGENIC_GPIO_PULLDOWN) { + ingenic_gpio_writel(chip, PxPUENC, pins); + ingenic_gpio_writel(chip, PxPDENS, pins); + } else if(state == INGENIC_GPIO_HIZ) { + ingenic_gpio_writel(chip, PxPUENC, pins); + ingenic_gpio_writel(chip, PxPDENC, pins); + } + } else { + printk("error: this gpio func not support\n"); + return; + } +} + +static int ingenic_gpio_get_pull_state(struct ingenic_gpio_chip *chip, u32 pins) +{ + unsigned int pull_down = 0; + unsigned int pull_up = 0; + int state = 0; + + pull_down = (ingenic_gpio_readl(chip, PxPDEN) & BIT(pins)) >> pins; + pull_up = (ingenic_gpio_readl(chip, PxPUEN) & BIT(pins)) >> pins; + + if(pull_down && pull_up) { + dev_warn(chip->gc.dev, "Set both pull_up and pull_down on %s %d\n", chip->name, pins); + } + + if(pull_down) { + state = INGENIC_GPIO_PULLDOWN; + } else if(pull_up) { + state = INGENIC_GPIO_PULLUP; + } else { + state = INGENIC_GPIO_HIZ; + } + + return state; + +} + +/************************************************************* + * Set GPIO Input&Interrupt HW filter + * chip: gpio chip + * pin: pin num (one pin) + * pinflt: filter to set + *************************************************************/ +static void ingenic_gpio_set_filter(struct ingenic_gpio_chip *chip, + u32 pin, u16 pinflt) +{ + const struct ingenic_priv *priv = chip->pctl->priv; + if (chip->filter_bitmap & BIT(pin)) + priv->set_filter(chip, pin, pinflt); +} + +static void ingenic_gpio_set_schmitt_enable(struct ingenic_gpio_chip *chip, + u32 pin, int enable) +{ + if(enable) { + ingenic_gpio_writel(chip, PxPSMTS, BIT(pin)); + } else { + ingenic_gpio_writel(chip, PxPSMTC, BIT(pin)); + } +} + +static void ingenic_gpio_set_slew_rate(struct ingenic_gpio_chip *chip, + u32 pin, int enable) +{ + if(enable) { + ingenic_gpio_writel(chip, PxPSLWS, BIT(pin)); + } else { + ingenic_gpio_writel(chip, PxPSLWC, BIT(pin)); + } +} + +static void ingenic_gpio_set_drive_strength(struct ingenic_gpio_chip *chip, + u32 pin, u16 strength) +{ + if(strength > 7) + strength = 7; + + if(strength & BIT(0)) + ingenic_gpio_writel(chip, PxPDS0S, BIT(pin)); + else + ingenic_gpio_writel(chip, PxPDS0C, BIT(pin)); + + if(strength & BIT(1)) + ingenic_gpio_writel(chip, PxPDS1S, BIT(pin)); + else + ingenic_gpio_writel(chip, PxPDS1C, BIT(pin)); + + if(strength & BIT(2)) + ingenic_gpio_writel(chip, PxPDS2S, BIT(pin)); + else + ingenic_gpio_writel(chip, PxPDS2C, BIT(pin)); +} + +static unsigned short ingenic_gpio_get_drive_strength(struct ingenic_gpio_chip *chip, + u32 pin) +{ + unsigned short strength = 0; + + strength |= (ingenic_gpio_readl(chip, PxPDS0) & BIT(pin)) ? (1 << 0) : 0; + strength |= (ingenic_gpio_readl(chip, PxPDS1) & BIT(pin)) ? (1 << 1) : 0; + strength |= (ingenic_gpio_readl(chip, PxPDS2) & BIT(pin)) ? (1 << 2) : 0; + + return strength; +} + +static void ingenic_gpio_irq_mask(struct irq_data *irqd) +{ + struct ingenic_gpio_chip *jzgc = irq_data_get_irq_chip_data(irqd); + ingenic_gpio_writel(jzgc, PxMSKS, BIT(irqd->hwirq)); +} + +static void ingenic_gpio_irq_unmask(struct irq_data *irqd) +{ + struct ingenic_gpio_chip *jzgc = irq_data_get_irq_chip_data(irqd); + ingenic_gpio_writel(jzgc, PxMSKC, BIT(irqd->hwirq)); +} + +static void ingenic_gpio_irq_ack(struct irq_data *irqd) +{ + struct ingenic_gpio_chip *jzgc = irq_data_get_irq_chip_data(irqd); + unsigned pins, pins_before, pending; + + ingenic_gpio_writel(jzgc, PxFLGC, BIT(irqd->hwirq)); + /* + * The controller not support BOTH EDGE trigger + * So we set the right trigger edge after irq ack + * Note: To make sure our edge interrupt will be tigger and handle at next time + * 0, clear pending + * 1, read the pin level + * 2, set edge according the pin level + * 3, read the pin level + * 4, read the pending + * 5, if pin level is change and irq not pending the irq may miss, goto 2 retry + */ + if (irqd_get_trigger_type(irqd) == IRQ_TYPE_EDGE_BOTH) { + pins = ingenic_gpio_readl(jzgc, PxPIN); + do { + ingenic_gpio_writel(jzgc, pins & BIT(irqd->hwirq) ? + PxPAT0C : PxPAT0S, BIT(irqd->hwirq)); + pins_before = pins; + pins = ingenic_gpio_readl(jzgc, PxPIN); + pending = ingenic_gpio_readl(jzgc, PxFLG); + } while(( (pins & BIT(irqd->hwirq)) != + (pins_before & BIT(irqd->hwirq)) ) && + !(pending & BIT(irqd->hwirq))); + } +} + +static int ingenic_gpio_irq_set_type(struct irq_data *irqd, unsigned int flow_type) +{ + struct ingenic_gpio_chip *jzgc = irq_data_get_irq_chip_data(irqd); + const struct ingenic_priv *priv = jzgc->pctl->priv; + enum gpio_function func; + + if (!(flow_type & IRQ_TYPE_SENSE_MASK)) + return 0; + + if (flow_type & IRQ_TYPE_EDGE_BOTH) + irq_set_handler_locked(irqd, handle_edge_irq); + else + irq_set_handler_locked(irqd, handle_level_irq); + + switch (flow_type & IRQD_TRIGGER_MASK) { + case IRQ_TYPE_LEVEL_HIGH: func = GPIO_INT_HI; break; + case IRQ_TYPE_LEVEL_LOW: func = GPIO_INT_LO; break; + case IRQ_TYPE_EDGE_RISING: func = GPIO_INT_RE; break; + case IRQ_TYPE_EDGE_FALLING: func = GPIO_INT_FE; break; + case IRQ_TYPE_EDGE_BOTH: func = GPIO_INT_RE_FE; break; + default: + pr_err("unsupported external interrupt type\n"); + return -EINVAL; + } + + ingenic_gpio_set_func(jzgc, priv->have_shadow, func, BIT(irqd->hwirq)); + return 0; +} + +static int ingenic_gpio_irq_set_wake(struct irq_data *irqd, unsigned int on) +{ + struct ingenic_gpio_chip *jzgc = irq_data_get_irq_chip_data(irqd); + + if (on) + jzgc->wakeup_bitmap |= BIT(irqd->hwirq); + else + jzgc->wakeup_bitmap &= ~BIT(irqd->hwirq); + + return irq_set_irq_wake(jzgc->irq, on); +} + +static void ingenic_gpio_irq_suspend(struct irq_data *irqd) +{ + struct ingenic_gpio_chip *jzgc = irq_data_get_irq_chip_data(irqd); + + if ((!(jzgc->wakeup_bitmap & BIT(irqd->hwirq))) && + (!(ingenic_gpio_readl(jzgc, PxMSK) & BIT(irqd->hwirq)))) { + jzgc->pm_irq_bitmap |= BIT(irqd->hwirq); + ingenic_gpio_writel(jzgc, PxMSKS, BIT(irqd->hwirq)); + } +} + +static void ingenic_gpio_irq_resume(struct irq_data *irqd) +{ + struct ingenic_gpio_chip *jzgc = irq_data_get_irq_chip_data(irqd); + + if (jzgc->pm_irq_bitmap & BIT(irqd->hwirq)) { + ingenic_gpio_writel(jzgc, PxMSKC, BIT(irqd->hwirq)); + jzgc->pm_irq_bitmap &= ~BIT(irqd->hwirq); + } +} + +static int ingenic_irq_request_resources(struct irq_data *irqd) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(irqd); + + if (!try_module_get(chip->owner)) + return -ENODEV; + + if (gpiochip_lock_as_irq(chip, irqd->hwirq)) { + pr_err("GPIO chip %s: unable to lock HW IRQ %lu for IRQ\n", + chip->label, + irqd->hwirq); + module_put(chip->owner); + return -EINVAL; + } + return 0; +} + +static void ingenic_irq_release_resources(struct irq_data *irqd) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(irqd); + + gpiochip_unlock_as_irq(chip, irqd->hwirq); + module_put(chip->owner); +} + +static struct irq_chip ingenic_gpio_irq_chip = { + .name = "GPIO", + .irq_unmask = ingenic_gpio_irq_unmask, + .irq_mask = ingenic_gpio_irq_mask, + .irq_ack = ingenic_gpio_irq_ack, + .irq_set_type = ingenic_gpio_irq_set_type, + .irq_set_wake = ingenic_gpio_irq_set_wake, + .irq_suspend = ingenic_gpio_irq_suspend, + .irq_resume = ingenic_gpio_irq_resume, + .irq_request_resources = ingenic_irq_request_resources, + .irq_release_resources = ingenic_irq_release_resources, +}; + +static void ingenic_gpio_irq_handler(struct irq_desc *desc) +{ + struct ingenic_gpio_chip *jzgc = irq_desc_get_handler_data(desc); + unsigned long pend, mask; + unsigned long i; + + if (IS_ERR_OR_NULL(jzgc->mcu_gpio_reg)) { + mask = ingenic_gpio_readl(jzgc, PxMSK); + pend = ingenic_gpio_readl(jzgc, PxFLG); + pend = pend & ~mask; + } else { + pend = *(jzgc->mcu_gpio_reg); + } + + for_each_set_bit(i, &pend, jzgc->gc.ngpio) + generic_handle_irq(irq_find_mapping(jzgc->irq_domain, i)); + + if (!IS_ERR_OR_NULL(jzgc->mcu_gpio_reg)) + *(jzgc->mcu_gpio_reg) = 0; + return; +} + +static void ingenic_gpio_set(struct gpio_chip *chip, + unsigned pin, int value) +{ + struct ingenic_gpio_chip *jzgc = gc_to_ingenic_gc(chip); + + BUG_ON(pin > jzgc->gc.ngpio); + if (value) + ingenic_gpio_writel(jzgc, PxPAT0S, BIT(pin)); + else + ingenic_gpio_writel(jzgc, PxPAT0C, BIT(pin)); +} + +static int ingenic_gpio_get(struct gpio_chip *chip, unsigned pin) +{ + struct ingenic_gpio_chip *jzgc = gc_to_ingenic_gc(chip); + + BUG_ON(pin > chip->ngpio); + if (jzgc->resume_pending & BIT(pin)) { + jzgc->resume_pending &= ~BIT(pin); + return !!(jzgc->sleep_level & BIT(pin)); + } + return !!(ingenic_gpio_readl(jzgc, PxPIN) & BIT(pin)); +} + +static int ingenic_gpio_direction_input(struct gpio_chip *chip, + unsigned pin) +{ + BUG_ON(pin > chip->ngpio); + return pinctrl_gpio_direction_input(chip->base + pin); +} + +static int ingenic_gpio_direction_output(struct gpio_chip *chip, + unsigned pin, int value) +{ + struct ingenic_gpio_chip *jzgc = gc_to_ingenic_gc(chip); + struct ingenic_pinctrl *pctl = jzgc->pctl; + const struct ingenic_priv *priv = pctl->priv; + enum gpio_function func; + unsigned long flags; + BUG_ON(pin > jzgc->gc.ngpio); + if(value) + func = GPIO_OUTPUT1; + else + func = GPIO_OUTPUT0; + if(priv->have_shadow){ + spin_lock_irqsave(&pctl->shadow_lock, flags); + ingenic_gpio_fill_func_shadow(jzgc, func, BIT(pin)); + ingenic_gpio_shadow_writel(jzgc); + spin_unlock_irqrestore(&pctl->shadow_lock, flags); + }else{ + ingenic_gpio_set_func_normal(jzgc, func, BIT(pin)); + } + return pinctrl_gpio_direction_output(chip->base + pin); +} + +static int ingenic_gpio_to_irq(struct gpio_chip *chip, + unsigned pin) +{ + struct ingenic_gpio_chip *jzgc = gc_to_ingenic_gc(chip); + unsigned int virq; + + + BUG_ON(pin > chip->ngpio); + if (NULL == jzgc->irq_domain) return -ENXIO; + virq = irq_create_mapping(jzgc->irq_domain, pin); + + return (virq) ?: -ENXIO; +} +static int ingenic_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + struct ingenic_gpio_chip *jzgc = gc_to_ingenic_gc(chip); + unsigned gpio = chip->base + offset; + + if(jzgc->used_pins_bitmap & (1 << offset)) { + printk("%s: GP:%s used_pins_bitmap: 0X%08X\n", __func__, jzgc->name, jzgc->used_pins_bitmap); + printk("current gpio request pin: chip->name %s, gpio: 0X%08X\n", chip->of_node->name, 1 << offset); + dump_stack(); + printk("%s:gpio functions has redefinition\n", __FILE__); + } + + jzgc->used_pins_bitmap |= 1 << offset; + + return pinctrl_request_gpio(gpio); +} +static void ingenic_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + struct ingenic_gpio_chip *jzgc = gc_to_ingenic_gc(chip); + unsigned gpio = chip->base + offset; + pinctrl_free_gpio(gpio); + jzgc->used_pins_bitmap &= ~(1 << offset); +} + +static const struct gpio_chip ingenic_gpiolib_chip = { + .owner = THIS_MODULE, + .set = ingenic_gpio_set, + .get = ingenic_gpio_get, + .direction_input = ingenic_gpio_direction_input, + .direction_output = ingenic_gpio_direction_output, + .to_irq = ingenic_gpio_to_irq, + .request = ingenic_gpio_request, + .free = ingenic_gpio_free, +}; + +static int ingenic_of_gpio_xlate(struct gpio_chip *gc, + const struct of_phandle_args *gpiospec, u32 *flags) +{ + struct ingenic_gpio_chip *jzgc = gc_to_ingenic_gc(gc); + u32 pin; + u32 pull; +#if defined(INGENIC_GPIO_FILTER) + u32 filter; +#endif + u32 drive_strength = 0; + u32 schmit_enable = 0; + u32 slewrate_enable = 0; + + if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells)) + return -EINVAL; + + if (gpiospec->args[0] >= gc->ngpio) + return -EINVAL; + + if (flags) + *flags = gpiospec->args[1]; + + pin = gpiospec->args[0]; + + pull = INGENIC_GPIO_PULL(gpiospec->args[2]); + ingenic_gpio_set_pull(jzgc, BIT(pin), pull); + + drive_strength = INGENIC_GPIO_DRIVE_STRENGTH(gpiospec->args[2]); + schmit_enable = INGENIC_GPIO_SCHMITT_ENABLE(gpiospec->args[2]); + slewrate_enable = INGENIC_GPIO_SLEW_RATE_ENABLE(gpiospec->args[2]); + + ingenic_gpio_set_drive_strength(jzgc, pin, drive_strength); + ingenic_gpio_set_schmitt_enable(jzgc, pin, schmit_enable); + ingenic_gpio_set_slew_rate(jzgc, pin, slewrate_enable); + +#if defined(INGENIC_GPIO_FILTER) + filter = INGENIC_GPIO_FILTER(gpiospec->args[2]); + if (jzgc->filter_bitmap & BIT(pin)) + ingenic_gpio_set_filter(jzgc, pin, filter); +#endif + return pin; +} + +static int ingenic_gpio_chip_add(struct ingenic_pinctrl *pctl, + struct device_node *np, int base, int idx) +{ + struct ingenic_gpio_chip *jzgc = &pctl->gpio_chips[idx]; + struct gpio_chip *gc = &jzgc->gc; + u32 ngpio; + + jzgc->gc = ingenic_gpiolib_chip; + snprintf(jzgc->name, sizeof(jzgc->name), "GP%c", 'A' + idx); + + if (of_property_read_u32(np, "ingenic,num-gpios", &ngpio)) + ngpio = MAX_GPIOS_ON_CHIP; + if (of_property_read_u32(np, "ingenic,filter-gpios", &jzgc->filter_bitmap)) + jzgc->filter_bitmap = 0; + if (of_property_read_u32(np, "ingenic,pull-gpios", &jzgc->pull_bitmap)) + jzgc->pull_bitmap = 0; + if (of_property_read_u32(np, "#gpio-cells", &gc->of_gpio_n_cells)) + gc->of_gpio_n_cells = 3; + jzgc->used_pins_bitmap = 0; + pr_debug("%s (%d) config:\nfilter %08x\npull %08x\ncells=%d\nbase%d\n", + jzgc->name, + ngpio, + jzgc->filter_bitmap, + jzgc->pull_bitmap, + gc->of_gpio_n_cells, + base); + jzgc->of_node = np; + jzgc->idx = idx; + jzgc->pctl = pctl; + gc->ngpio = (u16)ngpio; + gc->of_node = np; + gc->base = base; + gc->dev = pctl->dev; + gc->label = jzgc->name; + gc->of_xlate = ingenic_of_gpio_xlate; + + return gpiochip_add(gc); +} + +static void ingenic_gpio_sleep_init(struct device_node *np, + const char *list_name, u32 *pm_pin) +{ + int size, i, array[32], pin_bitmap = 0; + + if (!of_find_property(np, list_name, &size)) + return; + + if (of_property_read_u32_array(np, list_name, + array, size/sizeof(u32))) + return; + + for (i = 0; i < size/sizeof(u32); i++) { + BUG_ON(array[i] > 31); + pin_bitmap |= BIT(array[i]); + } + + *pm_pin = pin_bitmap; +} + +static void ingenic_gpio_pm_init(struct ingenic_pinctrl *pctl, + struct device_node *np, int idx) +{ + struct ingenic_gpio_chip *jzgc = &pctl->gpio_chips[idx]; + + ingenic_gpio_sleep_init(np, "ingenic,gpio-sleep-low", + &jzgc->pm_bitmap[PM_SLEEP_LOW]); + ingenic_gpio_sleep_init(np, "ingenic,gpio-sleep-high", + &jzgc->pm_bitmap[PM_SLEEP_HIGH]); + ingenic_gpio_sleep_init(np, "ingenic,gpio-sleep-pull", + &jzgc->pm_bitmap[PM_SLEEP_PULL]); + ingenic_gpio_sleep_init(np, "ingenic,gpio-sleep-npul", + &jzgc->pm_bitmap[PM_SLEEP_NOPULL]); + ingenic_gpio_sleep_init(np, "ingenic,gpio-sleep-pullup", + &jzgc->pm_bitmap[PM_SLEEP_PULL_UP]); + ingenic_gpio_sleep_init(np, "ingenic,gpio-sleep-pulldown", + &jzgc->pm_bitmap[PM_SLEEP_PULL_DOWN]); + ingenic_gpio_sleep_init(np, "ingenic,gpio-sleep-hiz", + &jzgc->pm_bitmap[PM_SLEEP_HIZ]); + + jzgc->wakeup_bitmap = 0; + + /*pr_info("%s pm sleep:\nlow %08x\nhigh %08x\npull %08x\nnpull %08x\n", + jzgc->name, jzgc->pm_bitmap[PM_SLEEP_LOW], + jzgc->pm_bitmap[PM_SLEEP_HIGH], + jzgc->pm_bitmap[PM_SLEEP_PULL], + jzgc->pm_bitmap[PM_SLEEP_NOPULL]); + */ +} + +static int ingenic_irq_domain_xlate(struct irq_domain *d, + struct device_node *ctrlr, + const u32 *intspec, unsigned int intsize, + unsigned long *out_hwirq, unsigned int *out_type) +{ + struct ingenic_gpio_chip *jzgc = d->host_data; + u32 pin; +#if defined(INGENIC_GPIO_PULL) + u32 pull; +#endif +#if defined(INGENIC_GPIO_FILTER) + u32 filter; +#endif + + if (WARN_ON(intsize < 3)) + return -EINVAL; + pin = *out_hwirq = intspec[0]; + *out_type = (intsize > 1) ? intspec[1] : IRQ_TYPE_NONE; + +#if defined(INGENIC_GPIO_PULL) + pull = INGENIC_GPIO_PULL(intspec[2]); + ingenic_gpio_set_pull(jzgc, BIT(pin), pull); +#endif + +#if defined(INGENIC_GPIO_FILTER) + filter = INGENIC_GPIO_FILTER(intspec[2]); + if (jzgc->filter_bitmap & BIT(pin)) + ingenic_gpio_set_filter(jzgc, pin, filter); +#endif + return 0; +} + +static int ingenic_irq_domain_map(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw) +{ + struct ingenic_gpio_chip *jzgc = d->host_data; + irq_set_chip_data(virq, jzgc); + irq_set_chip_and_handler(virq, &ingenic_gpio_irq_chip, + handle_level_irq); + return 0; +} + +static const struct irq_domain_ops ingenic_irq_domain_ops = { + .map = ingenic_irq_domain_map, + .xlate = ingenic_irq_domain_xlate, +}; + +static int ingenic_gpio_irq_init(struct ingenic_pinctrl *pctl, + struct device_node *np, int idx) +{ + struct ingenic_gpio_chip *jzgc = &pctl->gpio_chips[idx]; + + jzgc->irq = irq_of_parse_and_map(np, 0); + if (!jzgc->irq) + return -EINVAL; + + jzgc->irq_domain = irq_domain_add_linear(np, jzgc->gc.ngpio, + &ingenic_irq_domain_ops, (void *)jzgc); + if (!jzgc->irq_domain) + return -ENOMEM; + irq_set_handler_data(jzgc->irq, jzgc); + irq_set_chained_handler(jzgc->irq, ingenic_gpio_irq_handler); + return 0; +} + +static int ingenic_gpiolib_register(struct ingenic_pinctrl *pctl) +{ + struct ingenic_gpio_chip *gpio_chips; + struct device_node *np; + int idx = 0, ret; + + if (of_property_read_u32(pctl->of_node,"ingenic,num-chips", &pctl->num_chips)) + return -EINVAL; + + pctl->bitmap_priv = devm_kzalloc(pctl->dev, sizeof(u32) * pctl->num_chips, + GFP_KERNEL); + if (!pctl->bitmap_priv) + return -ENOMEM; + + gpio_chips = devm_kzalloc(pctl->dev, + pctl->num_chips * sizeof(struct ingenic_gpio_chip), + GFP_KERNEL); + pctl->gpio_chips = gpio_chips; + pctl->total_pins = 0; + spin_lock_init(&pctl->shadow_lock); + + for_each_child_of_node(pctl->of_node, np) { + if (!of_find_property(np, "gpio-controller", NULL)) + continue; + + if (WARN_ON(idx >= pctl->num_chips)) + break; + + ret = ingenic_gpio_chip_add(pctl, np, pctl->total_pins, idx); + if (ret) { + dev_err(pctl->dev, "%s gpio add failed\n", gpio_chips[idx].name); + return ret; + } + pctl->total_pins += gpio_chips[idx].gc.ngpio; + spin_lock_init(&gpio_chips[idx].lock); + + ingenic_gpio_pm_init(pctl, np, idx); + + if (!of_find_property(np, "interrupt-controller", NULL)) + goto next_chip; + + ret = ingenic_gpio_irq_init(pctl, np, idx); + if (ret) + dev_err(pctl->dev, "%s irq init failed\n", + gpio_chips[idx].gc.label); + +next_chip: + of_node_get(np); + idx++; + } + + dev_info(pctl->dev, "%d gpio chip add success, pins %d\n", + idx, pctl->total_pins); + return 0; +} + +static int ingenic_get_group_count(struct pinctrl_dev *pctldev) +{ + struct ingenic_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + return pctl->num_groups; +} + +static const char* ingenic_get_group_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + struct ingenic_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + BUG_ON(selector >= pctl->num_groups); + return pctl->groups[selector].name; +} + +static int ingenic_get_group_pins(struct pinctrl_dev *pctldev, + unsigned selector, + const unsigned **pins, + unsigned *num_pins) +{ + struct ingenic_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + BUG_ON(selector >= pctl->num_groups); + *pins = pctl->groups[selector].pins; + *num_pins = pctl->groups[selector].num_pins; + return 0; +} + +static int ingenic_gc_match(struct gpio_chip *chip, void *data) +{ + return chip->of_node == data; +} + +static int ingenic_packed_config(int cfgval, int *config) +{ + int value; + + value = PINCFG_UNPACK_VALUE(cfgval); + switch (cfgval & PINCTL_CFG_TYPE_MSK ) { + case PINCTL_CFG_FILTER: + *config = pinconf_to_config_packed(PIN_CONFIG_INPUT_DEBOUNCE, value & 0xffff); + case PINCTL_CFG_DRIVE_STRENGTH: + *config = pinconf_to_config_packed(PIN_CONFIG_DRIVE_STRENGTH, value & 0xffff); + break; + case PINCTL_CFG_BIAS_PULL_PIN_DEFAULT: + *config = pinconf_to_config_packed(PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, 1); + break; + case PINCTL_CFG_BIAS_DISABLE: + *config = pinconf_to_config_packed(PIN_CONFIG_BIAS_DISABLE, 1); + break; + case PINCTL_CFG_BIAS_HIGH_IMPEDANCE: + *config = pinconf_to_config_packed(PIN_CONFIG_BIAS_HIGH_IMPEDANCE, 1); + break; + case PINCTL_CFG_INPUT_SCHMITT_ENABLE: + *config = pinconf_to_config_packed(PIN_CONFIG_INPUT_SCHMITT_ENABLE, 1); + break; + case PINCTL_CFG_SLEW_RATE: + *config = pinconf_to_config_packed(PIN_CONFIG_SLEW_RATE, 1); + break; + default: + *config = 0; + } + return 0; +} + +#if defined(PINCTL_CFG_TYPES) +static int of_prase_pincfg_to_map(struct device_node *np, + struct pinctrl_map *map, int num_maps) +{ + struct of_phandle_args out_args; + struct ingenic_gpio_chip *jzgc; + struct gpio_chip *gc; + u32 idx = 0, config = 0, index = 0; + unsigned long pin, bitmap; + char *pin_names; + + while (!of_parse_phandle_with_args(np, "ingenic,pincfg", + "#ingenic,pincfg-cells", index++, + &out_args)) { + gc = gpiochip_find(out_args.np, ingenic_gc_match); + if (!gc) return -EINVAL; + jzgc = gc_to_ingenic_gc(gc); + + bitmap = pin_bitmap(out_args.args[PIN_ARGS_FROM_INDEX], + out_args.args[PIN_ARGS_TO_INDEX]); + + ingenic_packed_config(out_args.args[PIN_ARGS_CFG_INDEX], &config); + for_each_set_bit(pin, &bitmap, jzgc->gc.ngpio) { + pin_names = kzalloc(sizeof(char) * PIN_NAMES_LEN, GFP_KERNEL); + if (!pin_names) return -ENOMEM; + sprintf(pin_names, "%s-%d", jzgc->name, (unsigned)pin); + for (idx = 0; idx < num_maps && + map[idx].data.configs.group_or_pin && + strcmp(pin_names, map[idx].data.configs.group_or_pin); + idx++); + BUG_ON(num_maps == idx); + if (map[idx].data.configs.num_configs == PINCTL_CFG_TYPES) { + __WARN(); + continue; + } + kfree(map[idx].data.configs.group_or_pin); + map[idx].data.configs.group_or_pin = pin_names; + map[idx].data.configs.configs[map[idx].data.configs.num_configs] = config; + map[idx].data.configs.num_configs++; + map[idx].type = PIN_MAP_TYPE_CONFIGS_PIN; + } + } + return 0; +} +#endif + +static int ingenic_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np_config, + struct pinctrl_map **maps, unsigned *num_maps) +{ + struct ingenic_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + struct gpio_chip *gc; + struct ingenic_gpio_chip *jzgc; + struct device_node *node; + struct ingenic_pinctrl_group *grp; + struct ingenic_pinctrl_func *func; + struct pinctrl_map *new_map; + unsigned long *cfgs; + int index = 0, ret = 0; + unsigned map_cnt = 0, cfg_map_cnt = 0, idx; + struct of_phandle_args out_args; + u32 *pincfg_bitmap = pctl->bitmap_priv; + + grp = find_group_by_of_node(pctl, np_config); + if (!grp) + return -EINVAL; + + if ((node = of_get_parent(np_config)) == pctl->of_node) + func = find_func_by_of_node(pctl, np_config); + else + func = find_func_by_of_node(pctl, node); + of_node_put(node); + if (!func) + return -EINVAL; + + +#if defined(PINCTL_CFG_TYPES) + for (idx = 0; idx < pctl->num_chips; idx++) + pincfg_bitmap[idx] = 0; + while (!of_parse_phandle_with_args(np_config, "ingenic,pincfg", + "#ingenic,pincfg-cells", index++, &out_args)) { + gc = gpiochip_find(out_args.np, ingenic_gc_match); + if (!gc) return -ENOMEM; + jzgc = gc_to_ingenic_gc(gc); + pincfg_bitmap[jzgc->idx] |= pin_bitmap(out_args.args[PIN_ARGS_FROM_INDEX], + out_args.args[PIN_ARGS_TO_INDEX]); + }; + for (idx = 0; idx < pctl->num_chips; idx++) + cfg_map_cnt += bit_count(pincfg_bitmap[idx]); + map_cnt = cfg_map_cnt; +#endif + + if (of_find_property(np_config, "ingenic,pinmux", NULL) && + of_find_property(np_config, "ingenic,pinmux-funcsel", NULL)) + map_cnt++; + if (!map_cnt) return -EINVAL; + + *maps = new_map = kzalloc(sizeof(*new_map) * map_cnt, GFP_KERNEL); + if (!new_map) + return -ENOMEM; + +#if defined(PINCTL_CFG_TYPES) + if (!cfg_map_cnt) + goto skip_config; + + cfgs = kzalloc(sizeof(*cfgs) * PINCTL_CFG_TYPES * cfg_map_cnt, GFP_KERNEL); + if (!cfgs) + return -ENOMEM; + + for (idx = 0; idx < cfg_map_cnt; idx++, cfgs += PINCTL_CFG_TYPES) { + new_map[idx].data.configs.configs = cfgs; + } + + ret = of_prase_pincfg_to_map(np_config, new_map, cfg_map_cnt); + if (ret) + return ret; +#endif +skip_config: + *num_maps = cfg_map_cnt; + if (!of_find_property(np_config, "ingenic,pinmux", NULL)) + return 0; + + jzgc = gc_to_ingenic_gc(grp->gc); + /* if(jzgc->used_pins_bitmap & grp->pinmux_bitmap) { */ + /* printk("%s: GP:%s used_pins_bitmap: 0X%08X\n", __func__, jzgc->name, jzgc->used_pins_bitmap); */ + /* printk("current set function pin: chip->name %s, gpio: 0X%08X\n", grp->name, grp->pinmux_bitmap); */ + /* dump_stack(); */ + /* printk("%s:gpio functions has redefinition\n", __FILE__); */ + /* } */ + jzgc->used_pins_bitmap |= grp->pinmux_bitmap; + new_map[*num_maps].type = PIN_MAP_TYPE_MUX_GROUP; + new_map[*num_maps].data.mux.function = func->name; + new_map[*num_maps].data.mux.group = grp->name; + (*num_maps)++; + return 0; +} + +static void ingenic_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *maps, unsigned num_maps) +{ + unsigned long *configs = NULL; + int idx; + for (idx = 0; idx < num_maps; idx++) { + if (maps[idx].type == PIN_MAP_TYPE_CONFIGS_GROUP) { + if (NULL == configs) + configs = maps[idx].data.configs.configs; + kfree(maps[idx].data.configs.group_or_pin); + } + } + kfree(configs); + kfree(maps); +} + +static const struct pinctrl_ops ingenic_pctl_ops = { + .get_groups_count = ingenic_get_group_count, + .get_group_name = ingenic_get_group_name, + .get_group_pins = ingenic_get_group_pins, + .dt_node_to_map = ingenic_dt_node_to_map, /* TODO: use generic map*/ + .dt_free_map = ingenic_dt_free_map, +}; + +static int ingenic_pinmux_get_functions_count(struct pinctrl_dev *pctldev) +{ + struct ingenic_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + return pctl->num_funs; +} + +static const char *ingenic_pinmux_get_function_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + struct ingenic_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + return pctl->functions[selector].name; +} + +static int ingenic_pinmux_get_groups(struct pinctrl_dev *pctldev, + unsigned selector, + const char * const **groups, + unsigned * const num_groups) +{ + struct ingenic_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + BUG_ON(selector > pctl->num_funs); + *groups = pctl->functions[selector].groups; + *num_groups = pctl->functions[selector].num_groups; + return 0; +} + +static int ingenic_pinmux_enable(struct pinctrl_dev *pctldev, + unsigned func_selector, + unsigned group_selector) +{ + struct ingenic_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + struct ingenic_pinctrl_group *grp = NULL; + const struct ingenic_priv *priv = pctl->priv; + struct ingenic_gpio_chip *jzgc; + + if (func_selector > pctl->num_funs || + group_selector > pctl->num_groups) + return -EINVAL; + + grp = &pctl->groups[group_selector]; + jzgc = gc_to_ingenic_gc(grp->gc); + + ingenic_gpio_set_func(jzgc, priv->have_shadow, + grp->pinmux_func, grp->pinmux_bitmap); + return 0; +} + +static int ingenic_pinmux_gpio_set_dir(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, unsigned gpio, + bool input) +{ + struct ingenic_gpio_chip *jzgc = gc_to_ingenic_gc(range->gc); + const struct ingenic_priv *priv = jzgc->pctl->priv; + unsigned pin = gpio - range->gc->base; + unsigned pxpat0, pxint; + enum gpio_function func = GPIO_INPUT; + if (!input) { + pxpat0 = ingenic_gpio_readl(jzgc, PxPAT0); + if (pxpat0 & BIT(pin)) + func = GPIO_OUTPUT1; + else + func = GPIO_OUTPUT0; + } else { + pxint = ingenic_gpio_readl(jzgc, PxINT); + if (pxint & BIT(pin)) + return 0; + } + ingenic_gpio_set_func(jzgc, priv->have_shadow, func, BIT(pin)); + return 0; +} + +static const struct pinmux_ops ingenic_pinmux_ops = { + .get_functions_count = ingenic_pinmux_get_functions_count, + .get_function_name = ingenic_pinmux_get_function_name, + .get_function_groups = ingenic_pinmux_get_groups, + .set_mux = ingenic_pinmux_enable, + .gpio_set_direction = ingenic_pinmux_gpio_set_dir, +}; + +static int ingenic_pinconf_get(struct pinctrl_dev *pctldev, + unsigned gpio, + unsigned long *config) +{ + struct gpio_chip *gc = gpio_to_chip(gpio); + struct ingenic_gpio_chip *jzgc = gc_to_ingenic_gc(gc); + enum pin_config_param param; + unsigned pin; + unsigned arg; + bool pull; + + if (!jzgc) return -EINVAL; + + pin = gpio - jzgc->gc.base; + + param = pinconf_to_config_param(*config); + + switch (param) { + case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: + case PIN_CONFIG_BIAS_DISABLE: + pull = ingenic_gpio_readl(jzgc, PxPUEN)&BIT(pin); + if ((jzgc->pull_bitmap & BIT(pin)) && pull) + *config = pinconf_to_config_packed(PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, 1); + else + *config = pinconf_to_config_packed(PIN_CONFIG_BIAS_DISABLE, 1); + break; + case PIN_CONFIG_DRIVE_STRENGTH: + arg = ingenic_gpio_get_drive_strength(jzgc, pin); + *config = pinconf_to_config_packed(param, arg); + break; + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: + arg = ingenic_gpio_get_pull_state(jzgc, pin); + *config = pinconf_to_config_packed(param, arg); + break; + case PIN_CONFIG_SLEW_RATE: + arg = !!(ingenic_gpio_readl(jzgc, PxPSLW) & BIT(pin)); + *config = pinconf_to_config_packed(param, arg); + break; + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + arg = !!(ingenic_gpio_readl(jzgc, PxPSMT) & BIT(pin)); + *config = pinconf_to_config_packed(param, arg); + break; + default: + return -ENOTSUPP; + break; + } + return 0; +} + +static int ingenic_pinconf_set(struct pinctrl_dev *pctldev, + unsigned gpio, + unsigned long *configs, + unsigned num_configs) +{ + struct gpio_chip *gc = gpio_to_chip(gpio); + struct ingenic_gpio_chip *jzgc = gc_to_ingenic_gc(gc); + enum pin_config_param param; + u16 value; + unsigned pin; + int i; + + + if (!jzgc) { + return -EINVAL; + } + + pin = gpio - jzgc->gc.base; + + dev_dbg(pctldev->dev, "%s %s %u %lx\n", __func__, jzgc->gc.label, pin, *configs); + + for(i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + switch (param) { + case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: + if (!(jzgc->pull_bitmap & BIT(pin))) + return -EINVAL; + ingenic_gpio_set_pull(jzgc, BIT(pin), INGENIC_GPIO_PULLUP); + break; + case PIN_CONFIG_BIAS_DISABLE: + if ((!(jzgc->pull_bitmap & BIT(pin)))) + return -EINVAL; + ingenic_gpio_set_pull(jzgc, BIT(pin), INGENIC_GPIO_HIZ); + break; + case PIN_CONFIG_INPUT_DEBOUNCE: + value = pinconf_to_config_argument(configs[i]); + ingenic_gpio_set_filter(jzgc, pin, value); + break; + case PIN_CONFIG_DRIVE_STRENGTH: + value = pinconf_to_config_argument(configs[i]); + ingenic_gpio_set_drive_strength(jzgc, pin, value); + break; + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + ingenic_gpio_set_schmitt_enable(jzgc, pin, 1); + break; + case PIN_CONFIG_SLEW_RATE: + ingenic_gpio_set_slew_rate(jzgc, pin, 1); + break; + case PIN_CONFIG_BIAS_PULL_UP: + ingenic_gpio_set_pull(jzgc, BIT(pin), INGENIC_GPIO_PULLUP); + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + ingenic_gpio_set_pull(jzgc, BIT(pin), INGENIC_GPIO_PULLDOWN); + break; + case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: + ingenic_gpio_set_pull(jzgc, BIT(pin), INGENIC_GPIO_HIZ); + break; + default: + return -ENOTSUPP; + } + } + return 0; +} + +/* set the pin config settings for a specified pin group */ +static int ingenic_pinconf_group_set(struct pinctrl_dev *pctldev, + unsigned group, unsigned long *configs, + unsigned num_configs) +{ + struct ingenic_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + struct ingenic_pinctrl_group *grp = NULL; + const unsigned int *pins; + unsigned int cnt; + + grp = &pctl->groups[group]; + pins = grp->pins; + + for (cnt = 0; cnt < grp->num_pins; cnt++) + ingenic_pinconf_set(pctldev, pins[cnt], configs, num_configs); + + return 0; +} + +/* get the pin config settings for a specified pin group */ +static int ingenic_pinconf_group_get(struct pinctrl_dev *pctldev, + unsigned int group, unsigned long *config) +{ + struct ingenic_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + const unsigned int *pins; + + pins = pctl->groups[group].pins; + ingenic_pinconf_get(pctldev, pins[0], config); + return 0; +} + + +static const struct pinconf_ops ingenic_pinconf_ops = { + .pin_config_get = ingenic_pinconf_get, + .pin_config_set = ingenic_pinconf_set, + .pin_config_group_get = ingenic_pinconf_group_get, + .pin_config_group_set = ingenic_pinconf_group_set, +}; + +static int ingenic_init_group(struct device *dev, struct device_node *np, + struct ingenic_pinctrl_group *grp) +{ + struct device_node *gpio_np = NULL; + struct of_phandle_args out_args; + u32 func; + int pin, idx = 0, index = 0; + + grp->name = np->name; + grp->of_node = np; + + while(!of_parse_phandle_with_args(np, "ingenic,pinmux", + "#ingenic,pinmux-cells", index++, &out_args)) { + if (gpio_np != NULL && out_args.np != gpio_np) + return -EINVAL; + if (of_property_read_u32(np, "ingenic,pinmux-funcsel", &func)) + return -EINVAL; + gpio_np = out_args.np; + grp->pinmux_bitmap |= pin_bitmap(out_args.args[PIN_ARGS_FROM_INDEX], + out_args.args[PIN_ARGS_TO_INDEX]); + } + if (!gpio_np) + return 0; + grp->gc = gpiochip_find(gpio_np, ingenic_gc_match); + if (!grp->gc) return -EINVAL; + grp->num_pins = bit_count(grp->pinmux_bitmap); + grp->pins = devm_kzalloc(dev, sizeof(unsigned) * grp->num_pins, + GFP_KERNEL); + if (!grp->pins) + return -ENOMEM; + + switch (func) { + case PINCTL_FUNCTION0: grp->pinmux_func = GPIO_FUNC_0; break; + case PINCTL_FUNCTION1: grp->pinmux_func = GPIO_FUNC_1; break; + case PINCTL_FUNCTION2: grp->pinmux_func = GPIO_FUNC_2; break; + case PINCTL_FUNCTION3: grp->pinmux_func = GPIO_FUNC_3; break; + case PINCTL_FUNCLOLVL: grp->pinmux_func = GPIO_OUTPUT0; break; + case PINCTL_FUNCHILVL: grp->pinmux_func = GPIO_OUTPUT1; break; + case PINCTL_FUNCINPUT: grp->pinmux_func = GPIO_INPUT; break; + default: + return -EINVAL; + } + for_each_set_bit(pin, (unsigned long *)&grp->pinmux_bitmap, grp->gc->ngpio) + grp->pins[idx++] = grp->gc->base + pin; + return 0; +} + +static struct ingenic_pinctrl_group* ingenic_pinctrl_create_groups( + struct device *dev, unsigned *cnt) +{ + struct device_node *np, *subnp; + struct ingenic_pinctrl_group *grp; + unsigned int i = 0; + + *cnt = 0; + for_each_child_of_node(dev->of_node, np) { + unsigned count = 0; + if (!!of_find_property(np, "gpio-controller", NULL)) + continue; + if (!!(count = of_get_child_count(np))) + *cnt += count; + else + (*cnt)++; + } + + grp = devm_kzalloc(dev, sizeof(*grp) * (*cnt), GFP_KERNEL); + if (!grp) + return ERR_PTR(-ENOMEM); + + for_each_child_of_node(dev->of_node, np) { + if (!!of_find_property(np, "gpio-controller", NULL)) + continue; + + if (!of_get_child_count(np)) { + ingenic_init_group(dev, np, &grp[i++]); + continue; + } + + for_each_child_of_node(np, subnp) { + ingenic_init_group(dev, subnp, &grp[i++]); + } + } + return grp; +} + +static struct ingenic_pinctrl_func* ingenic_pinctrl_create_functions( + struct device *dev, unsigned int *cnt) +{ + struct device_node *np, *subnp; + struct ingenic_pinctrl_func *func; + int i = 0; + + *cnt = 0; + for_each_child_of_node(dev->of_node, np) { + if (!!of_find_property(np, "gpio-controller", NULL)) + continue; + (*cnt)++; + } + + func = devm_kzalloc(dev, sizeof(*func) * (*cnt), GFP_KERNEL); + if (!func) + return ERR_PTR(-ENOMEM); + + for_each_child_of_node(dev->of_node, np) { + u8 num_child, j = 0; + if (!!of_find_property(np, "gpio-controller", NULL)) + continue; + func[i].name = np->name; + func[i].of_node = np; + num_child = of_get_child_count(np); + func[i].num_groups = num_child ?:1; + func[i].groups = devm_kzalloc(dev, sizeof(char *) * func[i].num_groups, + GFP_KERNEL); + if (!func[i].groups) + return ERR_PTR(-ENOMEM); + if (num_child) { + for_each_child_of_node(np, subnp) + func[i].groups[j++] = subnp->name; + } else + func[i].groups[0] = func[i].name; + i++; + } + return func; +} + +static int ingenic_pinctrl_parse_dt(struct ingenic_pinctrl *pctl) +{ + struct ingenic_pinctrl_group *groups; + struct ingenic_pinctrl_func *functions; + unsigned int grp_cnt = 0, func_cnt = 0; + + groups = ingenic_pinctrl_create_groups(pctl->dev, &grp_cnt); + if (IS_ERR(groups)) { + dev_err(pctl->dev, "failed to parse pin groups\n"); + return PTR_ERR(groups); + } + + functions = ingenic_pinctrl_create_functions(pctl->dev, &func_cnt); + if (IS_ERR(functions)) { + dev_err(pctl->dev, "failed to parse pin functions\n"); + return PTR_ERR(groups); + } + + pctl->groups = groups; + pctl->num_groups = grp_cnt; + pctl->functions = functions; + pctl->num_funs = func_cnt; + return 0; +} + +static int ingenic_pinctrl_register(struct ingenic_pinctrl *pctl) +{ + struct pinctrl_desc *pctl_desc = &pctl->pctl_desc; + struct pinctrl_pin_desc *pindesc, *pdesc; + char *pin_names; + unsigned chip, pin; + int ret; + + pctl_desc->name = "ingenic pinctrl"; + pctl_desc->owner = THIS_MODULE; + pctl_desc->pctlops = &ingenic_pctl_ops; + pctl_desc->pmxops = &ingenic_pinmux_ops; + pctl_desc->confops = &ingenic_pinconf_ops; + pctl_desc->npins = pctl->total_pins; + pindesc = devm_kzalloc(pctl->dev, + sizeof(*pindesc) * pctl_desc->npins, GFP_KERNEL); + if (!pindesc) + return -ENOMEM; + pctl_desc->pins = pindesc; + + for (pin = 0, pdesc = pindesc; pin < pctl_desc->npins; pin++, pdesc++) + pdesc->number = pin; + + pin_names = devm_kzalloc(pctl->dev, + sizeof(char) * PIN_NAMES_LEN * pctl_desc->npins, + GFP_KERNEL); + if (!pin_names) + return -ENOMEM; + + for (chip = 0; chip < pctl->num_chips; chip++) { + struct ingenic_gpio_chip *jzgc = &pctl->gpio_chips[chip]; + for (pin = 0; pin < jzgc->gc.ngpio; pin++) { + sprintf(pin_names, "%s-%d", jzgc->name, pin); + pdesc = pindesc++; + pdesc->name = pin_names; + pin_names += PIN_NAMES_LEN; + } + } + + ret = ingenic_pinctrl_parse_dt(pctl); + if (ret) + return ret; + + pctl->pctl_dev = pinctrl_register(pctl_desc, pctl->dev, pctl); + if (!pctl->pctl_dev) + return -EINVAL; + + for (chip = 0; chip < pctl->num_chips; chip++) { + struct ingenic_gpio_chip *jzgc = &pctl->gpio_chips[chip]; + + jzgc->grange.name = jzgc->name; + jzgc->grange.id = jzgc->idx; + jzgc->grange.pin_base = jzgc->gc.base; + jzgc->grange.base = jzgc->gc.base; + jzgc->grange.npins = jzgc->gc.ngpio; + jzgc->grange.gc = &jzgc->gc; + pinctrl_add_gpio_range(pctl->pctl_dev, &jzgc->grange); + } + + return 0; +} + +static ssize_t dump_gpio(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ingenic_pinctrl *pctl = dev_get_drvdata(dev); + struct ingenic_gpio_chip *gpio_chips = pctl->gpio_chips; + const struct ingenic_priv *priv = pctl->priv; + int idx; + ssize_t len = 0; + + for (idx = 0; idx < pctl->num_chips; idx++) + len += snprintf(buf + len, PAGE_SIZE - len, "REG :|+++GP%c++|", + 'A' + idx); + len += snprintf(buf+len, PAGE_SIZE - len, "\n"); + for (idx = 0; idx < pctl->num_chips; idx++) + len += snprintf(buf + len, PAGE_SIZE - len, "INT :|%08x|", + ingenic_gpio_readl(&gpio_chips[idx], PxINT)); + len += snprintf(buf+len, PAGE_SIZE - len, "\n"); + for (idx = 0; idx < pctl->num_chips; idx++) + len += snprintf(buf + len, PAGE_SIZE - len, "MSK :|%08x|", + ingenic_gpio_readl(&gpio_chips[idx], PxMSK)); + len += snprintf(buf+len, PAGE_SIZE - len, "\n"); + for (idx = 0; idx < pctl->num_chips; idx++) + len += snprintf(buf + len, PAGE_SIZE - len, "PAT1:|%08x|", + ingenic_gpio_readl(&gpio_chips[idx], PxPAT1)); + len += snprintf(buf+len, PAGE_SIZE - len, "\n"); + for (idx = 0; idx < pctl->num_chips; idx++) + len += snprintf(buf + len, PAGE_SIZE - len, "PAT0:|%08x|", + ingenic_gpio_readl(&gpio_chips[idx], PxPAT0)); + len += snprintf(buf+len, PAGE_SIZE - len, "\n"); + for (idx = 0; idx < pctl->num_chips; idx++) + len += snprintf(buf + len, PAGE_SIZE - len, "PULL_UP:|%08x|", + ingenic_gpio_readl(&gpio_chips[idx], PxPUEN)); + len += snprintf(buf+len, PAGE_SIZE - len, "\n"); + for (idx = 0; idx < pctl->num_chips; idx++) + len += snprintf(buf + len, PAGE_SIZE - len, "PULL_DOWN:|%08x|", + ingenic_gpio_readl(&gpio_chips[idx], PxPDEN)); + len += snprintf(buf+len, PAGE_SIZE - len, "\n"); + for (idx = 0; idx < pctl->num_chips; idx++) + len += snprintf(buf + len, PAGE_SIZE - len, "FLAG:|%08x|", + ingenic_gpio_readl(&gpio_chips[idx], PxFLG)); + len += snprintf(buf+len, PAGE_SIZE - len, "\n"); + + if (priv->dump_filter) + len += priv->dump_filter(pctl, buf+len, PAGE_SIZE - len); + return len; +} +static DEVICE_ATTR(dump_gpio, 0444, dump_gpio, NULL); + +static struct attribute *ingenic_pinctrl_attrs[] = { + &dev_attr_dump_gpio.attr, + NULL, +}; + +static const struct attribute_group ingenic_pinctrl_attr_group = { + .attrs = (struct attribute **)ingenic_pinctrl_attrs, +}; + + +static int ingenic_pinctrl_probe(struct platform_device *pdev) +{ + struct ingenic_pinctrl *pctl; + const struct of_device_id *match; + struct resource *res; + int ret; + + pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL); + if (!pctl) + return -ENOMEM; + match = of_match_node(ingenic_pinctrl_dt_match, pdev->dev.of_node); + if (!match) + return -ENODEV; + + pctl->priv = !match->data ? common_priv_data : (struct ingenic_priv *)match->data; + pctl->dev = &pdev->dev; + pctl->of_node = pdev->dev.of_node; + + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOMEM; + + pctl->io_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pctl->io_base)) + return PTR_ERR(pctl->io_base); + + platform_set_drvdata(pdev, pctl); + + ret = ingenic_gpiolib_register(pctl); + if (ret) + return ret; + + ret = ingenic_pinctrl_register(pctl); + if (ret) + return ret; + + ret = sysfs_create_group(&pdev->dev.kobj, &ingenic_pinctrl_attr_group); + if (ret) + dev_info(&pdev->dev, "ingenic pinctrl attr create failed\n"); + + gpctl = pctl; + + dev_info(&pdev->dev, "ingenic pinctrl probe success\n"); + return 0; +} + +unsigned long ingenic_pinctrl_lock(int port) +{ + unsigned long flags; + struct ingenic_gpio_chip *chip; + struct ingenic_pinctrl *pctl = NULL; + const struct ingenic_priv *priv = NULL; + + if (port < 0 || port >= gpctl->num_chips) + return 0; + + chip = &gpctl->gpio_chips[port]; + pctl = chip->pctl; + priv = pctl->priv; + + if(priv->have_shadow) + spin_lock_irqsave(&pctl->shadow_lock, flags); + else + spin_lock_irqsave(&chip->lock, flags); + + return flags; +} + +unsigned long ingenic_pinctrl_unlock(int port, unsigned long flags) +{ + struct ingenic_gpio_chip *chip; + struct ingenic_pinctrl *pctl = NULL; + const struct ingenic_priv *priv = NULL; + + if (port < 0 || port >= gpctl->num_chips) + return 0; + + chip = &gpctl->gpio_chips[port]; + pctl = chip->pctl; + priv = pctl->priv; + + if(priv->have_shadow) + spin_unlock_irqrestore(&pctl->shadow_lock, flags); + else + spin_unlock_irqrestore(&chip->lock, flags); + + return flags; +} + +#ifdef CONFIG_PM_SLEEP +#ifdef PINCTL_DEBUG +static char dump_buf[PAGE_SIZE]; +static void dump_pinctl(void) +{ + struct ingenic_pinctrl *pctl = gpctl; + char *buf = dump_buf; + dump_gpio(pctl->dev, NULL, buf); + printk("%s\n", buf); +} +#else +static void dump_pinctl(void) {} +#endif + +static int ingenic_gpio_suspend_set(struct ingenic_gpio_chip *jzgc) +{ + const struct ingenic_priv *priv = jzgc->pctl->priv; + u32 pm_high = 0, pm_low = 0, pm_pullup = 0, pm_pulldown = 0, pm_hiz = 0; + + jzgc->pm_bitmap[PM_RESUME_INT] = ingenic_gpio_readl(jzgc, PxINT); + jzgc->pm_bitmap[PM_RESUME_MSK] = ingenic_gpio_readl(jzgc, PxMSK); + jzgc->pm_bitmap[PM_RESUME_PAT0] = ingenic_gpio_readl(jzgc, PxPAT0); + jzgc->pm_bitmap[PM_RESUME_PAT1] = ingenic_gpio_readl(jzgc, PxPAT1); + jzgc->pm_bitmap[PM_RESUME_PULL_UP] = ingenic_gpio_readl(jzgc, PxPUEN); + jzgc->pm_bitmap[PM_RESUME_PULL_DOWN] = ingenic_gpio_readl(jzgc, PxPDEN); + + pm_pullup = jzgc->pm_bitmap[PM_SLEEP_PULL_UP] & (~(jzgc->wakeup_bitmap)); + pm_pulldown = jzgc->pm_bitmap[PM_SLEEP_PULL_DOWN] & (~(jzgc->wakeup_bitmap)); + + pm_high = jzgc->pm_bitmap[PM_SLEEP_HIGH] & (~(jzgc->wakeup_bitmap)); + pm_low = jzgc->pm_bitmap[PM_SLEEP_LOW] & (~(jzgc->wakeup_bitmap)); + pm_hiz = jzgc->pm_bitmap[PM_SLEEP_HIZ] & (~(jzgc->wakeup_bitmap)); + + if (pm_pullup) { + ingenic_gpio_set_func(jzgc, priv->have_shadow, GPIO_INPUT, pm_pullup); + ingenic_gpio_set_pull(jzgc, pm_pullup, INGENIC_GPIO_PULLUP); + } + if (pm_pulldown) { + ingenic_gpio_set_func(jzgc, priv->have_shadow, GPIO_INPUT, pm_pulldown); + ingenic_gpio_set_pull(jzgc, pm_pulldown, INGENIC_GPIO_PULLDOWN); + } + if(pm_hiz) { + ingenic_gpio_set_func(jzgc, priv->have_shadow, GPIO_INPUT, pm_hiz); + ingenic_gpio_set_pull(jzgc, pm_hiz, INGENIC_GPIO_HIZ); + } + + if (pm_high) + ingenic_gpio_set_func(jzgc, priv->have_shadow, GPIO_OUTPUT1, pm_high); + if (pm_low) + ingenic_gpio_set_func(jzgc, priv->have_shadow, GPIO_OUTPUT0, pm_low); + + return 0; +} + +static int ingenic_pinctrl_suspend(void) +{ + struct ingenic_pinctrl *pctl = gpctl; + int idx; + + if (!gpctl) + return 0; + dump_pinctl(); + for (idx = 0; idx < pctl->num_chips; idx++) { + struct ingenic_gpio_chip *chip = &pctl->gpio_chips[idx]; + ingenic_gpio_suspend_set(chip); + chip->resume_pending = 0; + } + + dump_pinctl(); + return 0; +} + +static void ingenic_check_wakeup_gpio(struct ingenic_gpio_chip *jzgc) +{ + u32 pend, mask; + + pend = ingenic_gpio_readl(jzgc, PxFLG); + mask = ingenic_gpio_readl(jzgc, PxMSK); + pend = pend & ~mask; + pend = pend & jzgc->wakeup_bitmap; + if(pend) { + jzgc->resume_pending = pend; + jzgc->sleep_level = ingenic_gpio_readl(jzgc, PxPAT0) & pend; + } +} + +static void ingenic_pinctrl_resume(void) +{ + struct ingenic_pinctrl *pctl = gpctl; + int idx; + + if (!gpctl) + return; + for (idx = 0; idx < pctl->num_chips; idx++) { + struct ingenic_gpio_chip *jzgc = &pctl->gpio_chips[idx]; + /*const struct ingenic_priv *priv = jzgc->pctl->priv;*/ + ingenic_check_wakeup_gpio(jzgc); + ingenic_gpio_writel(jzgc, PxINT, jzgc->pm_bitmap[PM_RESUME_INT]); + ingenic_gpio_writel(jzgc, PxMSK, jzgc->pm_bitmap[PM_RESUME_MSK]); + ingenic_gpio_writel(jzgc, PxPAT0, jzgc->pm_bitmap[PM_RESUME_PAT0]); + ingenic_gpio_writel(jzgc, PxPAT1, jzgc->pm_bitmap[PM_RESUME_PAT1]); + ingenic_gpio_writel(jzgc, PxPUEN, jzgc->pm_bitmap[PM_RESUME_PULL_UP]); + ingenic_gpio_writel(jzgc, PxPDEN, jzgc->pm_bitmap[PM_RESUME_PULL_DOWN]); + } +} +#else +#define ingenic_pinctrl_suspend NULL +#define ingenic_pinctrl_resume NULL +#endif + +static struct syscore_ops ingenic_pinctrl_syscore_ops = { + .suspend = ingenic_pinctrl_suspend, + .resume = ingenic_pinctrl_resume, +}; + +static const struct ingenic_priv common_priv_data[] = { + { + .have_shadow = true, + .pull_tristate = true, + }, +}; + +static const struct of_device_id ingenic_pinctrl_dt_match[] = { + { .compatible = "ingenic,t40-pinctrl", .data = (void *)common_priv_data}, + { .compatible = "ingenic,x2000-v12-pinctrl", .data = (void *)common_priv_data}, + {}, +}; +MODULE_DEVICE_TABLE(of, ingenic_pinctrl_dt_match); + +static struct platform_driver ingenic_pinctrl_drv = { + .probe = ingenic_pinctrl_probe, + .driver = { + .name = "ingenic pinctrl", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(ingenic_pinctrl_dt_match), + }, +}; + +static int __init ingenic_pinctrl_drv_init(void) +{ + register_syscore_ops(&ingenic_pinctrl_syscore_ops); + return platform_driver_register(&ingenic_pinctrl_drv); +} +postcore_initcall(ingenic_pinctrl_drv_init); + +static void __exit ingenic_pinctrl_drv_exit(void) +{ + unregister_syscore_ops(&ingenic_pinctrl_syscore_ops); + platform_driver_unregister(&ingenic_pinctrl_drv); + return; +} +module_exit(ingenic_pinctrl_drv_exit); + + +int mcu_gpio_register(unsigned int ggpio, unsigned int reg) +{ + struct ingenic_pinctrl *pctl = gpctl; + struct ingenic_gpio_chip *jzgc = NULL; + if (!gpctl) + return -ENODEV; + jzgc = &pctl->gpio_chips[ggpio]; + jzgc->mcu_gpio_reg = (unsigned int *)reg; + return 0; +} +EXPORT_SYMBOL_GPL(mcu_gpio_register); + +void mcu_gpio_unregister(unsigned int ggpio) +{ + struct ingenic_pinctrl *pctl = gpctl; + struct ingenic_gpio_chip *jzgc = NULL; + + if (!gpctl) + return; + jzgc = &pctl->gpio_chips[ggpio]; + jzgc->mcu_gpio_reg = NULL; +} +EXPORT_SYMBOL_GPL(mcu_gpio_unregister); + +MODULE_AUTHOR("cli "); +MODULE_DESCRIPTION("Ingenic pinctrl driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pinctrl");