161 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * sl28cpld GPIO driver4 *5 * Copyright 2020 Michael Walle <michael@walle.cc>6 */7 8#include <linux/device.h>9#include <linux/gpio/driver.h>10#include <linux/gpio/regmap.h>11#include <linux/interrupt.h>12#include <linux/kernel.h>13#include <linux/mod_devicetable.h>14#include <linux/module.h>15#include <linux/platform_device.h>16#include <linux/regmap.h>17 18/* GPIO flavor */19#define GPIO_REG_DIR 0x0020#define GPIO_REG_OUT 0x0121#define GPIO_REG_IN 0x0222#define GPIO_REG_IE 0x0323#define GPIO_REG_IP 0x0424 25/* input-only flavor */26#define GPI_REG_IN 0x0027 28/* output-only flavor */29#define GPO_REG_OUT 0x0030 31enum sl28cpld_gpio_type {32 SL28CPLD_GPIO = 1,33 SL28CPLD_GPI,34 SL28CPLD_GPO,35};36 37static const struct regmap_irq sl28cpld_gpio_irqs[] = {38 REGMAP_IRQ_REG_LINE(0, 8),39 REGMAP_IRQ_REG_LINE(1, 8),40 REGMAP_IRQ_REG_LINE(2, 8),41 REGMAP_IRQ_REG_LINE(3, 8),42 REGMAP_IRQ_REG_LINE(4, 8),43 REGMAP_IRQ_REG_LINE(5, 8),44 REGMAP_IRQ_REG_LINE(6, 8),45 REGMAP_IRQ_REG_LINE(7, 8),46};47 48static int sl28cpld_gpio_irq_init(struct platform_device *pdev,49 unsigned int base,50 struct gpio_regmap_config *config)51{52 struct regmap_irq_chip_data *irq_data;53 struct regmap_irq_chip *irq_chip;54 struct device *dev = &pdev->dev;55 int irq, ret;56 57 if (!device_property_read_bool(dev, "interrupt-controller"))58 return 0;59 60 irq = platform_get_irq(pdev, 0);61 if (irq < 0)62 return irq;63 64 irq_chip = devm_kzalloc(dev, sizeof(*irq_chip), GFP_KERNEL);65 if (!irq_chip)66 return -ENOMEM;67 68 irq_chip->name = "sl28cpld-gpio-irq";69 irq_chip->irqs = sl28cpld_gpio_irqs;70 irq_chip->num_irqs = ARRAY_SIZE(sl28cpld_gpio_irqs);71 irq_chip->num_regs = 1;72 irq_chip->status_base = base + GPIO_REG_IP;73 irq_chip->unmask_base = base + GPIO_REG_IE;74 irq_chip->ack_base = base + GPIO_REG_IP;75 76 ret = devm_regmap_add_irq_chip_fwnode(dev, dev_fwnode(dev),77 config->regmap, irq,78 IRQF_SHARED | IRQF_ONESHOT,79 0, irq_chip, &irq_data);80 if (ret)81 return ret;82 83 config->irq_domain = regmap_irq_get_domain(irq_data);84 85 return 0;86}87 88static int sl28cpld_gpio_probe(struct platform_device *pdev)89{90 struct gpio_regmap_config config = {0};91 enum sl28cpld_gpio_type type;92 struct regmap *regmap;93 u32 base;94 int ret;95 96 if (!pdev->dev.parent)97 return -ENODEV;98 99 type = (uintptr_t)device_get_match_data(&pdev->dev);100 if (!type)101 return -ENODEV;102 103 ret = device_property_read_u32(&pdev->dev, "reg", &base);104 if (ret)105 return -EINVAL;106 107 regmap = dev_get_regmap(pdev->dev.parent, NULL);108 if (!regmap)109 return -ENODEV;110 111 config.regmap = regmap;112 config.parent = &pdev->dev;113 config.ngpio = 8;114 115 switch (type) {116 case SL28CPLD_GPIO:117 config.reg_dat_base = base + GPIO_REG_IN;118 config.reg_set_base = base + GPIO_REG_OUT;119 /* reg_dir_out_base might be zero */120 config.reg_dir_out_base = GPIO_REGMAP_ADDR(base + GPIO_REG_DIR);121 122 /* This type supports interrupts */123 ret = sl28cpld_gpio_irq_init(pdev, base, &config);124 if (ret)125 return ret;126 break;127 case SL28CPLD_GPO:128 config.reg_set_base = base + GPO_REG_OUT;129 break;130 case SL28CPLD_GPI:131 config.reg_dat_base = base + GPI_REG_IN;132 break;133 default:134 dev_err(&pdev->dev, "unknown type %d\n", type);135 return -ENODEV;136 }137 138 return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(&pdev->dev, &config));139}140 141static const struct of_device_id sl28cpld_gpio_of_match[] = {142 { .compatible = "kontron,sl28cpld-gpio", .data = (void *)SL28CPLD_GPIO },143 { .compatible = "kontron,sl28cpld-gpi", .data = (void *)SL28CPLD_GPI },144 { .compatible = "kontron,sl28cpld-gpo", .data = (void *)SL28CPLD_GPO },145 {}146};147MODULE_DEVICE_TABLE(of, sl28cpld_gpio_of_match);148 149static struct platform_driver sl28cpld_gpio_driver = {150 .probe = sl28cpld_gpio_probe,151 .driver = {152 .name = "sl28cpld-gpio",153 .of_match_table = sl28cpld_gpio_of_match,154 },155};156module_platform_driver(sl28cpld_gpio_driver);157 158MODULE_DESCRIPTION("sl28cpld GPIO Driver");159MODULE_AUTHOR("Michael Walle <michael@walle.cc>");160MODULE_LICENSE("GPL");161