2c96606a0f
New drivers: - add a driver for the Loongson GPIO controller - add a driver for the fxl6408 I2C GPIO expander - add a GPIO module containing code common for Intel Elkhart Lake and Merrifield platforms - add a driver for the Intel Elkhart Lake platform reusing the code from the intel tangier library GPIOLIB core: - GPIO ACPI improvements - simplify gpiochip_add_data_with_keys() fwnode handling - cleanup header inclusions (remove unneeded ones, order the rest alphabetically) - remove duplicate code (reuse krealloc() instead of open-coding it, drop a duplicated check in gpiod_find_and_request()) - reshuffle the code to remove unnecessary forward declarations - coding style cleanups and improvements - add a helper for accessing device fwnodes - small updates in docs Driver improvements: - convert all remaining GPIO irqchip drivers to using immutable irqchips - drop unnecessary of_match_ptr() macro expansions - shrink the code in gpio-merrifield significantly by reusing the code from gpio-tangier + minor tweaks to the driver code - remove MODULE_LICENSE() from drivers that can only be built-in - add device-tree support to gpio-loongson1 - use new regmap features in gpio-104-dio-48e and gpio-pcie-idio-24 - minor tweaks and fixes to gpio-xra1403, gpio-sim, gpio-tegra194, gpio-omap, gpio-aspeed, gpio-raspberrypi-exp - shrink code in gpio-ich and gpio-pxa - Kconfig tweak for gpio-pmic-eic-sprd -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEFp3rbAvDxGAT0sefEacuoBRx13IFAmRGjBIACgkQEacuoBRx 13IBMA/+PTEowr87BTJW+Z0Y3EoXPGZSKFzUpnzpbGo7CT5mEO3KBbyikZi3asZ4 5mVPbHOC7OU8t76KSGYWXwPh0bvskt+jR2wz19f6F65g1W2SnTym52wAPUJDrKvm YQofEGcz9ykTIo5KQjAyqADYrrfIOKCOZbN59k8GscXBHkYmGFO3ZhEa5HhzcF+S qJBxnJ13Tbg9bszyl062pLqsNYGDeqqSuELrhALQCzSCM3WlJQOaHUEG//mS1Syu OHX2pwjw8u3HxBo6pKMK5fa4/aFM+EUAvSdDX59WmVrPnpLCHezyh4K3WQFUSnwG OJsW+b/eUDjICQBRvsHIJLuiAr19UouWWY6IZE9dTOjoPO1KWHtbaYX8rHWRZWCM +/QVfLavmXbOHW/pS2+NNxCARwu8o8ozcopY3PT6TjC5aN8/IkVT4eSaT3mJYXmh 8uS/5aY1Th0eyK5GHv7IcNME5Jb+sAHEnqG0Ebns7a9kOGQdEMJwZrnc5IjKWSMd PAKNjWYZ49XALtl8vVSar2DYt6d6z+UvGDX67s686FVpCDk15cyUE6VjdtKdGdsd mH+OnCaWDt+l89DEqZ4298ZA6kNk2CkHHjIO/TBDkU3jP7/wp/NtU0RTuCXydwjW aNjnfHd2JMJ//wQ4l2fQgpzWfVEN34mKZ2pysDotY47bwjpPD7o= =X+sP -----END PGP SIGNATURE----- Merge tag 'gpio-updates-for-v6.4' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux Pull gpio updates from Bartosz Golaszewski: "We have some new drivers, significant refactoring of existing intel platforms, lots of improvements all around, mass conversion to using immutable irqchips by drivers that had not been converted individually yet and some changes in the core library code. Summary: New drivers: - add a driver for the Loongson GPIO controller - add a driver for the fxl6408 I2C GPIO expander - add a GPIO module containing code common for Intel Elkhart Lake and Merrifield platforms - add a driver for the Intel Elkhart Lake platform reusing the code from the intel tangier library GPIOLIB core: - GPIO ACPI improvements - simplify gpiochip_add_data_with_keys() fwnode handling - cleanup header inclusions (remove unneeded ones, order the rest alphabetically) - remove duplicate code (reuse krealloc() instead of open-coding it, drop a duplicated check in gpiod_find_and_request()) - reshuffle the code to remove unnecessary forward declarations - coding style cleanups and improvements - add a helper for accessing device fwnodes - small updates in docs Driver improvements: - convert all remaining GPIO irqchip drivers to using immutable irqchips - drop unnecessary of_match_ptr() macro expansions - shrink the code in gpio-merrifield significantly by reusing the code from gpio-tangier + minor tweaks to the driver code - remove MODULE_LICENSE() from drivers that can only be built-in - add device-tree support to gpio-loongson1 - use new regmap features in gpio-104-dio-48e and gpio-pcie-idio-24 - minor tweaks and fixes to gpio-xra1403, gpio-sim, gpio-tegra194, gpio-omap, gpio-aspeed, gpio-raspberrypi-exp - shrink code in gpio-ich and gpio-pxa - Kconfig tweak for gpio-pmic-eic-sprd" * tag 'gpio-updates-for-v6.4' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux: (99 commits) gpio: gpiolib: Simplify gpiochip_add_data_with_key() fwnode gpiolib: Add gpiochip_set_data() helper gpiolib: Move gpiochip_get_data() higher in the code gpiolib: Check array_info for NULL only once in gpiod_get_array() gpiolib: Replace open coded krealloc() gpiolib: acpi: Add a ignore wakeup quirk for Clevo NL5xNU gpiolib: acpi: Move ACPI device NULL check to acpi_get_driver_gpio_data() gpiolib: acpi: use the fwnode in acpi_gpiochip_find() gpio: mm-lantiq: Fix typo in the newly added header filename sh: mach-x3proto: Add missing #include <linux/gpio/driver.h> powerpc/40x: Add missing select OF_GPIO_MM_GPIOCHIP gpio: xlp: Convert to immutable irq_chip gpio: xilinx: Convert to immutable irq_chip gpio: xgs-iproc: Convert to immutable irq_chip gpio: visconti: Convert to immutable irq_chip gpio: tqmx86: Convert to immutable irq_chip gpio: thunderx: Convert to immutable irq_chip gpio: stmpe: Convert to immutable irq_chip gpio: siox: Convert to immutable irq_chip gpio: rda: Convert to immutable irq_chip ...
235 lines
7.4 KiB
C
235 lines
7.4 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* GPIO driver for the ACCES 104-DIO-48E series
|
|
* Copyright (C) 2016 William Breathitt Gray
|
|
*
|
|
* This driver supports the following ACCES devices: 104-DIO-48E and
|
|
* 104-DIO-24E.
|
|
*/
|
|
#include <linux/bits.h>
|
|
#include <linux/device.h>
|
|
#include <linux/err.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/isa.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/moduleparam.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/types.h>
|
|
|
|
#include "gpio-i8255.h"
|
|
|
|
MODULE_IMPORT_NS(I8255);
|
|
|
|
#define DIO48E_EXTENT 16
|
|
#define MAX_NUM_DIO48E max_num_isa_dev(DIO48E_EXTENT)
|
|
|
|
static unsigned int base[MAX_NUM_DIO48E];
|
|
static unsigned int num_dio48e;
|
|
module_param_hw_array(base, uint, ioport, &num_dio48e, 0);
|
|
MODULE_PARM_DESC(base, "ACCES 104-DIO-48E base addresses");
|
|
|
|
static unsigned int irq[MAX_NUM_DIO48E];
|
|
static unsigned int num_irq;
|
|
module_param_hw_array(irq, uint, irq, &num_irq, 0);
|
|
MODULE_PARM_DESC(irq, "ACCES 104-DIO-48E interrupt line numbers");
|
|
|
|
#define DIO48E_ENABLE_INTERRUPT 0xB
|
|
#define DIO48E_DISABLE_INTERRUPT DIO48E_ENABLE_INTERRUPT
|
|
#define DIO48E_CLEAR_INTERRUPT 0xF
|
|
|
|
#define DIO48E_NUM_PPI 2
|
|
|
|
static const struct regmap_range dio48e_wr_ranges[] = {
|
|
regmap_reg_range(0x0, 0x9), regmap_reg_range(0xB, 0xB),
|
|
regmap_reg_range(0xD, 0xD), regmap_reg_range(0xF, 0xF),
|
|
};
|
|
static const struct regmap_range dio48e_rd_ranges[] = {
|
|
regmap_reg_range(0x0, 0x2), regmap_reg_range(0x4, 0x6),
|
|
regmap_reg_range(0xB, 0xB), regmap_reg_range(0xD, 0xD),
|
|
regmap_reg_range(0xF, 0xF),
|
|
};
|
|
static const struct regmap_range dio48e_volatile_ranges[] = {
|
|
i8255_volatile_regmap_range(0x0), i8255_volatile_regmap_range(0x4),
|
|
regmap_reg_range(0xB, 0xB), regmap_reg_range(0xD, 0xD),
|
|
regmap_reg_range(0xF, 0xF),
|
|
};
|
|
static const struct regmap_range dio48e_precious_ranges[] = {
|
|
regmap_reg_range(0xB, 0xB), regmap_reg_range(0xD, 0xD),
|
|
regmap_reg_range(0xF, 0xF),
|
|
};
|
|
static const struct regmap_access_table dio48e_wr_table = {
|
|
.yes_ranges = dio48e_wr_ranges,
|
|
.n_yes_ranges = ARRAY_SIZE(dio48e_wr_ranges),
|
|
};
|
|
static const struct regmap_access_table dio48e_rd_table = {
|
|
.yes_ranges = dio48e_rd_ranges,
|
|
.n_yes_ranges = ARRAY_SIZE(dio48e_rd_ranges),
|
|
};
|
|
static const struct regmap_access_table dio48e_volatile_table = {
|
|
.yes_ranges = dio48e_volatile_ranges,
|
|
.n_yes_ranges = ARRAY_SIZE(dio48e_volatile_ranges),
|
|
};
|
|
static const struct regmap_access_table dio48e_precious_table = {
|
|
.yes_ranges = dio48e_precious_ranges,
|
|
.n_yes_ranges = ARRAY_SIZE(dio48e_precious_ranges),
|
|
};
|
|
static const struct regmap_config dio48e_regmap_config = {
|
|
.reg_bits = 8,
|
|
.reg_stride = 1,
|
|
.val_bits = 8,
|
|
.io_port = true,
|
|
.max_register = 0xF,
|
|
.wr_table = &dio48e_wr_table,
|
|
.rd_table = &dio48e_rd_table,
|
|
.volatile_table = &dio48e_volatile_table,
|
|
.precious_table = &dio48e_precious_table,
|
|
.cache_type = REGCACHE_FLAT,
|
|
.use_raw_spinlock = true,
|
|
};
|
|
|
|
/* only bit 3 on each respective Port C supports interrupts */
|
|
#define DIO48E_REGMAP_IRQ(_ppi) \
|
|
[19 + (_ppi) * 24] = { \
|
|
.mask = BIT(_ppi), \
|
|
.type = { .types_supported = IRQ_TYPE_EDGE_RISING }, \
|
|
}
|
|
|
|
static const struct regmap_irq dio48e_regmap_irqs[] = {
|
|
DIO48E_REGMAP_IRQ(0), DIO48E_REGMAP_IRQ(1),
|
|
};
|
|
|
|
static int dio48e_handle_mask_sync(struct regmap *const map, const int index,
|
|
const unsigned int mask_buf_def,
|
|
const unsigned int mask_buf,
|
|
void *const irq_drv_data)
|
|
{
|
|
unsigned int *const irq_mask = irq_drv_data;
|
|
const unsigned int prev_mask = *irq_mask;
|
|
int err;
|
|
unsigned int val;
|
|
|
|
/* exit early if no change since the previous mask */
|
|
if (mask_buf == prev_mask)
|
|
return 0;
|
|
|
|
/* remember the current mask for the next mask sync */
|
|
*irq_mask = mask_buf;
|
|
|
|
/* if all previously masked, enable interrupts when unmasking */
|
|
if (prev_mask == mask_buf_def) {
|
|
err = regmap_write(map, DIO48E_CLEAR_INTERRUPT, 0x00);
|
|
if (err)
|
|
return err;
|
|
return regmap_write(map, DIO48E_ENABLE_INTERRUPT, 0x00);
|
|
}
|
|
|
|
/* if all are currently masked, disable interrupts */
|
|
if (mask_buf == mask_buf_def)
|
|
return regmap_read(map, DIO48E_DISABLE_INTERRUPT, &val);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define DIO48E_NGPIO 48
|
|
static const char *dio48e_names[DIO48E_NGPIO] = {
|
|
"PPI Group 0 Port A 0", "PPI Group 0 Port A 1", "PPI Group 0 Port A 2",
|
|
"PPI Group 0 Port A 3", "PPI Group 0 Port A 4", "PPI Group 0 Port A 5",
|
|
"PPI Group 0 Port A 6", "PPI Group 0 Port A 7", "PPI Group 0 Port B 0",
|
|
"PPI Group 0 Port B 1", "PPI Group 0 Port B 2", "PPI Group 0 Port B 3",
|
|
"PPI Group 0 Port B 4", "PPI Group 0 Port B 5", "PPI Group 0 Port B 6",
|
|
"PPI Group 0 Port B 7", "PPI Group 0 Port C 0", "PPI Group 0 Port C 1",
|
|
"PPI Group 0 Port C 2", "PPI Group 0 Port C 3", "PPI Group 0 Port C 4",
|
|
"PPI Group 0 Port C 5", "PPI Group 0 Port C 6", "PPI Group 0 Port C 7",
|
|
"PPI Group 1 Port A 0", "PPI Group 1 Port A 1", "PPI Group 1 Port A 2",
|
|
"PPI Group 1 Port A 3", "PPI Group 1 Port A 4", "PPI Group 1 Port A 5",
|
|
"PPI Group 1 Port A 6", "PPI Group 1 Port A 7", "PPI Group 1 Port B 0",
|
|
"PPI Group 1 Port B 1", "PPI Group 1 Port B 2", "PPI Group 1 Port B 3",
|
|
"PPI Group 1 Port B 4", "PPI Group 1 Port B 5", "PPI Group 1 Port B 6",
|
|
"PPI Group 1 Port B 7", "PPI Group 1 Port C 0", "PPI Group 1 Port C 1",
|
|
"PPI Group 1 Port C 2", "PPI Group 1 Port C 3", "PPI Group 1 Port C 4",
|
|
"PPI Group 1 Port C 5", "PPI Group 1 Port C 6", "PPI Group 1 Port C 7"
|
|
};
|
|
|
|
static int dio48e_irq_init_hw(struct regmap *const map)
|
|
{
|
|
unsigned int val;
|
|
|
|
/* Disable IRQ by default */
|
|
return regmap_read(map, DIO48E_DISABLE_INTERRUPT, &val);
|
|
}
|
|
|
|
static int dio48e_probe(struct device *dev, unsigned int id)
|
|
{
|
|
const char *const name = dev_name(dev);
|
|
struct i8255_regmap_config config = {};
|
|
void __iomem *regs;
|
|
struct regmap *map;
|
|
int err;
|
|
struct regmap_irq_chip *chip;
|
|
unsigned int irq_mask;
|
|
struct regmap_irq_chip_data *chip_data;
|
|
|
|
if (!devm_request_region(dev, base[id], DIO48E_EXTENT, name)) {
|
|
dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
|
|
base[id], base[id] + DIO48E_EXTENT);
|
|
return -EBUSY;
|
|
}
|
|
|
|
regs = devm_ioport_map(dev, base[id], DIO48E_EXTENT);
|
|
if (!regs)
|
|
return -ENOMEM;
|
|
|
|
map = devm_regmap_init_mmio(dev, regs, &dio48e_regmap_config);
|
|
if (IS_ERR(map))
|
|
return dev_err_probe(dev, PTR_ERR(map),
|
|
"Unable to initialize register map\n");
|
|
|
|
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
|
|
if (!chip)
|
|
return -ENOMEM;
|
|
|
|
chip->irq_drv_data = devm_kzalloc(dev, sizeof(irq_mask), GFP_KERNEL);
|
|
if (!chip->irq_drv_data)
|
|
return -ENOMEM;
|
|
|
|
chip->name = name;
|
|
chip->mask_base = DIO48E_ENABLE_INTERRUPT;
|
|
chip->ack_base = DIO48E_CLEAR_INTERRUPT;
|
|
chip->no_status = true;
|
|
chip->num_regs = 1;
|
|
chip->irqs = dio48e_regmap_irqs;
|
|
chip->num_irqs = ARRAY_SIZE(dio48e_regmap_irqs);
|
|
chip->handle_mask_sync = dio48e_handle_mask_sync;
|
|
|
|
/* Initialize to prevent spurious interrupts before we're ready */
|
|
err = dio48e_irq_init_hw(map);
|
|
if (err)
|
|
return err;
|
|
|
|
err = devm_regmap_add_irq_chip(dev, map, irq[id], 0, 0, chip, &chip_data);
|
|
if (err)
|
|
return dev_err_probe(dev, err, "IRQ registration failed\n");
|
|
|
|
config.parent = dev;
|
|
config.map = map;
|
|
config.num_ppi = DIO48E_NUM_PPI;
|
|
config.names = dio48e_names;
|
|
config.domain = regmap_irq_get_domain(chip_data);
|
|
|
|
return devm_i8255_regmap_register(dev, &config);
|
|
}
|
|
|
|
static struct isa_driver dio48e_driver = {
|
|
.probe = dio48e_probe,
|
|
.driver = {
|
|
.name = "104-dio-48e"
|
|
},
|
|
};
|
|
module_isa_driver_with_irq(dio48e_driver, num_dio48e, num_irq);
|
|
|
|
MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
|
|
MODULE_DESCRIPTION("ACCES 104-DIO-48E GPIO driver");
|
|
MODULE_LICENSE("GPL v2");
|