mirror of https://github.com/OpenIPC/firmware.git
				
				
				
			
		
			
				
	
	
		
			1795 lines
		
	
	
		
			52 KiB
		
	
	
	
		
			Diff
		
	
	
			
		
		
	
	
			1795 lines
		
	
	
		
			52 KiB
		
	
	
	
		
			Diff
		
	
	
| 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 <chen.li@ingenic.com>
 | |
| + *
 | |
| + * 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 <linux/module.h>
 | |
| +#include <linux/syscore_ops.h>
 | |
| +#include <linux/interrupt.h>
 | |
| +#include <linux/io.h>
 | |
| +#include <linux/gpio.h>
 | |
| +#include <linux/of_irq.h>
 | |
| +#include <linux/of.h>
 | |
| +#include <linux/pinctrl/pinctrl.h>
 | |
| +#include <linux/pinctrl/pinmux.h>
 | |
| +#include <linux/pinctrl/pinconf.h>
 | |
| +#include <linux/pinctrl/consumer.h>
 | |
| +#include <linux/pinctrl/pinconf-generic.h>
 | |
| +#include <linux/slab.h>
 | |
| +
 | |
| +#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 <chen.li@ingenic.com>");
 | |
| +MODULE_DESCRIPTION("Ingenic pinctrl driver");
 | |
| +MODULE_LICENSE("GPL");
 | |
| +MODULE_ALIAS("platform:pinctrl");
 |