brintos

brintos / linux-shallow public Read only

0
0
Text · 5.0 KiB · 53b1eb8 Raw
173 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * GPIO library for the ACCES IDIO-16 family4 * Copyright (C) 2022 William Breathitt Gray5 */6#include <linux/bits.h>7#include <linux/device.h>8#include <linux/err.h>9#include <linux/export.h>10#include <linux/gpio/regmap.h>11#include <linux/module.h>12#include <linux/regmap.h>13#include <linux/types.h>14 15#include "gpio-idio-16.h"16 17#define DEFAULT_SYMBOL_NAMESPACE GPIO_IDIO_1618 19#define IDIO_16_DAT_BASE 0x020#define IDIO_16_OUT_BASE IDIO_16_DAT_BASE21#define IDIO_16_IN_BASE (IDIO_16_DAT_BASE + 1)22#define IDIO_16_CLEAR_INTERRUPT 0x123#define IDIO_16_ENABLE_IRQ 0x224#define IDIO_16_DEACTIVATE_INPUT_FILTERS 0x325#define IDIO_16_DISABLE_IRQ IDIO_16_ENABLE_IRQ26#define IDIO_16_INTERRUPT_STATUS 0x627 28#define IDIO_16_NGPIO 3229#define IDIO_16_NGPIO_PER_REG 830#define IDIO_16_REG_STRIDE 431 32struct idio_16_data {33	struct regmap *map;34	unsigned int irq_mask;35};36 37static int idio_16_handle_mask_sync(const int index, const unsigned int mask_buf_def,38				    const unsigned int mask_buf, void *const irq_drv_data)39{40	struct idio_16_data *const data = irq_drv_data;41	const unsigned int prev_mask = data->irq_mask;42	int err;43	unsigned int val;44 45	/* exit early if no change since the previous mask */46	if (mask_buf == prev_mask)47		return 0;48 49	/* remember the current mask for the next mask sync */50	data->irq_mask = mask_buf;51 52	/* if all previously masked, enable interrupts when unmasking */53	if (prev_mask == mask_buf_def) {54		err = regmap_write(data->map, IDIO_16_CLEAR_INTERRUPT, 0x00);55		if (err)56			return err;57		return regmap_read(data->map, IDIO_16_ENABLE_IRQ, &val);58	}59 60	/* if all are currently masked, disable interrupts */61	if (mask_buf == mask_buf_def)62		return regmap_write(data->map, IDIO_16_DISABLE_IRQ, 0x00);63 64	return 0;65}66 67static int idio_16_reg_mask_xlate(struct gpio_regmap *const gpio, const unsigned int base,68				  const unsigned int offset, unsigned int *const reg,69				  unsigned int *const mask)70{71	unsigned int stride;72 73	/* Input lines start at GPIO 16 */74	if (offset < 16) {75		stride = offset / IDIO_16_NGPIO_PER_REG;76		*reg = IDIO_16_OUT_BASE + stride * IDIO_16_REG_STRIDE;77	} else {78		stride = (offset - 16) / IDIO_16_NGPIO_PER_REG;79		*reg = IDIO_16_IN_BASE + stride * IDIO_16_REG_STRIDE;80	}81 82	*mask = BIT(offset % IDIO_16_NGPIO_PER_REG);83 84	return 0;85}86 87static const char *idio_16_names[IDIO_16_NGPIO] = {88	"OUT0", "OUT1", "OUT2", "OUT3", "OUT4", "OUT5", "OUT6", "OUT7",89	"OUT8", "OUT9", "OUT10", "OUT11", "OUT12", "OUT13", "OUT14", "OUT15",90	"IIN0", "IIN1", "IIN2", "IIN3", "IIN4", "IIN5", "IIN6", "IIN7",91	"IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15",92};93 94/**95 * devm_idio_16_regmap_register - Register an IDIO-16 GPIO device96 * @dev:	device that is registering this IDIO-16 GPIO device97 * @config:	configuration for idio_16_regmap_config98 *99 * Registers an IDIO-16 GPIO device. Returns 0 on success and negative error number on failure.100 */101int devm_idio_16_regmap_register(struct device *const dev,102				 const struct idio_16_regmap_config *const config)103{104	struct gpio_regmap_config gpio_config = {};105	int err;106	struct idio_16_data *data;107	struct regmap_irq_chip *chip;108	struct regmap_irq_chip_data *chip_data;109 110	if (!config->parent)111		return -EINVAL;112 113	if (!config->map)114		return -EINVAL;115 116	if (!config->regmap_irqs)117		return -EINVAL;118 119	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);120	if (!data)121		return -ENOMEM;122	data->map = config->map;123 124	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);125	if (!chip)126		return -ENOMEM;127 128	chip->name = dev_name(dev);129	chip->status_base = IDIO_16_INTERRUPT_STATUS;130	chip->mask_base = IDIO_16_ENABLE_IRQ;131	chip->ack_base = IDIO_16_CLEAR_INTERRUPT;132	chip->no_status = config->no_status;133	chip->num_regs = 1;134	chip->irqs = config->regmap_irqs;135	chip->num_irqs = config->num_regmap_irqs;136	chip->handle_mask_sync = idio_16_handle_mask_sync;137	chip->irq_drv_data = data;138 139	/* Disable IRQ to prevent spurious interrupts before we're ready */140	err = regmap_write(data->map, IDIO_16_DISABLE_IRQ, 0x00);141	if (err)142		return err;143 144	err = devm_regmap_add_irq_chip(dev, data->map, config->irq, 0, 0, chip, &chip_data);145	if (err)146		return dev_err_probe(dev, err, "IRQ registration failed\n");147 148	if (config->filters) {149		/* Deactivate input filters */150		err = regmap_write(data->map, IDIO_16_DEACTIVATE_INPUT_FILTERS, 0x00);151		if (err)152			return err;153	}154 155	gpio_config.parent = config->parent;156	gpio_config.regmap = data->map;157	gpio_config.ngpio = IDIO_16_NGPIO;158	gpio_config.names = idio_16_names;159	gpio_config.reg_dat_base = GPIO_REGMAP_ADDR(IDIO_16_DAT_BASE);160	gpio_config.reg_set_base = GPIO_REGMAP_ADDR(IDIO_16_DAT_BASE);161	gpio_config.ngpio_per_reg = IDIO_16_NGPIO_PER_REG;162	gpio_config.reg_stride = IDIO_16_REG_STRIDE;163	gpio_config.irq_domain = regmap_irq_get_domain(chip_data);164	gpio_config.reg_mask_xlate = idio_16_reg_mask_xlate;165 166	return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config));167}168EXPORT_SYMBOL_GPL(devm_idio_16_regmap_register);169 170MODULE_AUTHOR("William Breathitt Gray");171MODULE_DESCRIPTION("ACCES IDIO-16 GPIO Library");172MODULE_LICENSE("GPL");173