mirror of https://github.com/OpenIPC/firmware.git
				
				
				
			
		
			
				
	
	
		
			150 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Diff
		
	
	
			
		
		
	
	
			150 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Diff
		
	
	
| diff -drupN a/drivers/mfd/rn5t567-irq.c b/drivers/mfd/rn5t567-irq.c
 | |
| --- a/drivers/mfd/rn5t567-irq.c	1970-01-01 03:00:00.000000000 +0300
 | |
| +++ b/drivers/mfd/rn5t567-irq.c	2022-06-09 05:02:30.000000000 +0300
 | |
| @@ -0,0 +1,145 @@
 | |
| +/*
 | |
| + * Interrupt driver for RICOH567 power management chip.
 | |
| + *
 | |
| + * Copyright (C) 2016 Ingenic Semiconductor Co., Ltd.
 | |
| + *
 | |
| + * Author: cli <chen.li@ingenic.com>
 | |
| + *
 | |
| + * Based on code
 | |
| + *	Copyright (c) 2011-2012, NVIDIA CORPORATION.  All rights reserved.
 | |
| + *	Author: Laxman dewangan <ldewangan@nvidia.com>
 | |
| + *
 | |
| + * This program is distributed in the hope that it will be useful, but WITHOUT
 | |
| + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | |
| + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 | |
| + * more details.
 | |
| + *
 | |
| + * You should have received a copy of the GNU General Public License
 | |
| + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | |
| + *
 | |
| + */
 | |
| +#include <linux/interrupt.h>
 | |
| +#include <linux/irq.h>
 | |
| +#include <linux/i2c.h>
 | |
| +#include <linux/mfd/rn5t567.h>
 | |
| +#include <linux/regmap.h>
 | |
| +#include <linux/irqdomain.h>
 | |
| +#include <linux/of_irq.h>
 | |
| +
 | |
| +struct rn5t567_irq_data {
 | |
| +	struct device *dev;
 | |
| +	struct regmap *regmap;
 | |
| +	struct mutex  irq_lock;
 | |
| +	struct irq_domain *irq_domain;
 | |
| +};
 | |
| +
 | |
| +static struct rn5t567_irq_data *rn5t567 = NULL;
 | |
| +
 | |
| +static irqreturn_t rn5t567_irq_thread_handler(int virq, void *data)
 | |
| +{
 | |
| +	struct rn5t567_irq_data *pdata = data;
 | |
| +	int enable, pending = 0, ret;
 | |
| +
 | |
| +	might_sleep();
 | |
| +
 | |
| +	mutex_lock(&pdata->irq_lock);
 | |
| +
 | |
| +	ret = regmap_read(pdata->regmap, RN5T567_INTMON, &pending);
 | |
| +	ret |=regmap_read(pdata->regmap, RN5T567_INTEN, &enable);
 | |
| +	if (!ret) {
 | |
| +		dev_warn(pdata->dev, "irq read i2c reg faild: %d\n", ret);
 | |
| +		mutex_unlock(&pdata->irq_lock);
 | |
| +		return IRQ_HANDLED;
 | |
| +	}
 | |
| +	pending = (pending & enable);
 | |
| +	mutex_unlock(&pdata->irq_lock);
 | |
| +
 | |
| +	while (pending) {
 | |
| +		int cur_irq , i;
 | |
| +		i = fls(pending) - 1;
 | |
| +		cur_irq = irq_find_mapping(pdata->irq_domain, i);
 | |
| +		handle_nested_irq(cur_irq);
 | |
| +		pending &= ~BIT(i);
 | |
| +	};
 | |
| +
 | |
| +	return IRQ_HANDLED;
 | |
| +}
 | |
| +
 | |
| +static int rn5t567_domain_xlate_onecell(struct irq_domain *d, struct device_node *ctrlr,
 | |
| +		const u32 *intspec, unsigned int intsize,
 | |
| +		unsigned long *out_hwirq, unsigned int *out_type)
 | |
| +{
 | |
| +	struct regmap *regmap = (struct regmap *)d->host_data;
 | |
| +	int ret;
 | |
| +
 | |
| +	if (!regmap)
 | |
| +		return -EINVAL;
 | |
| +
 | |
| +	ret = irq_domain_xlate_onecell(d, ctrlr, intspec, intsize,
 | |
| +			out_hwirq, out_type);
 | |
| +	if (ret)
 | |
| +		return ret;
 | |
| +
 | |
| +	switch (*out_hwirq) {
 | |
| +	case RN5T567_IRQ_WDG:
 | |
| +		break;
 | |
| +	case RN5T567_IRQ_SYSTEM:
 | |
| +	case RN5T567_IRQ_DCDC:
 | |
| +	case RN5T567_IRQ_GPIO:
 | |
| +		regmap_update_bits(regmap, RN5T567_INTEN, BIT(*out_hwirq), BIT(*out_hwirq));
 | |
| +		break;
 | |
| +	default:
 | |
| +		return -EINVAL;
 | |
| +	}
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +static const struct irq_domain_ops rn5t567_irq_domain_ops = {
 | |
| +	.xlate = rn5t567_domain_xlate_onecell,
 | |
| +};
 | |
| +
 | |
| +int rn5t567_irq_init(struct i2c_client *i2c, struct regmap *regmap)
 | |
| +{
 | |
| +	int ret;
 | |
| +
 | |
| +	i2c->irq = irq_of_parse_and_map(i2c->dev.of_node, 0);
 | |
| +	if (!i2c->irq)
 | |
| +		return 0;
 | |
| +
 | |
| +	rn5t567 = devm_kzalloc(&i2c->dev, sizeof(struct rn5t567_irq_data), GFP_KERNEL);
 | |
| +	if (!rn5t567)
 | |
| +		return -ENOMEM;
 | |
| +
 | |
| +	mutex_init(&rn5t567->irq_lock);
 | |
| +	rn5t567->regmap = regmap;
 | |
| +
 | |
| +	regmap_write(rn5t567->regmap, RN5T567_INTEN, 0);
 | |
| +	regmap_write(rn5t567->regmap, RN5T567_INTPOL, 0);
 | |
| +
 | |
| +	ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, rn5t567_irq_thread_handler,
 | |
| +			IRQF_TRIGGER_LOW | IRQF_ONESHOT, i2c->name, rn5t567);
 | |
| +	if (ret) {
 | |
| +		dev_err(&i2c->dev, "irq request failed: %d\n", ret);
 | |
| +		return ret;
 | |
| +	}
 | |
| +
 | |
| +	rn5t567->irq_domain = irq_domain_add_linear(i2c->dev.of_node, RN5T567_IRQ_NUM,
 | |
| +			&rn5t567_irq_domain_ops, regmap);
 | |
| +	if (IS_ERR(rn5t567->irq_domain)) {
 | |
| +		ret = PTR_ERR(rn5t567->irq_domain);
 | |
| +		dev_err(&i2c->dev, "irq domain add failed: %d\n", ret);
 | |
| +		return ret;
 | |
| +	}
 | |
| +
 | |
| +	rn5t567->dev = &i2c->dev;
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +void rn5t567_irq_deinit(void)
 | |
| +{
 | |
| +	if (rn5t567->irq_domain)
 | |
| +		irq_domain_remove(rn5t567->irq_domain);
 | |
| +
 | |
| +	regmap_write(rn5t567->regmap, RN5T567_INTEN, 0);
 | |
| +}
 |