1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (C) 2019 Bootlin 4 * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> 5 */ 6 7 #include <linux/err.h> 8 #include <linux/gpio/driver.h> 9 #include <linux/module.h> 10 #include <linux/of.h> 11 #include <linux/of_device.h> 12 #include <linux/of_address.h> 13 #include <linux/platform_device.h> 14 #include <linux/regmap.h> 15 #include <linux/mfd/syscon.h> 16 17 #define LOGICVC_CTRL_REG 0x40 18 #define LOGICVC_CTRL_GPIO_SHIFT 11 19 #define LOGICVC_CTRL_GPIO_BITS 5 20 21 #define LOGICVC_POWER_CTRL_REG 0x78 22 #define LOGICVC_POWER_CTRL_GPIO_SHIFT 0 23 #define LOGICVC_POWER_CTRL_GPIO_BITS 4 24 25 struct logicvc_gpio { 26 struct gpio_chip chip; 27 struct regmap *regmap; 28 }; 29 30 static void logicvc_gpio_offset(struct logicvc_gpio *logicvc, unsigned offset, 31 unsigned int *reg, unsigned int *bit) 32 { 33 if (offset >= LOGICVC_CTRL_GPIO_BITS) { 34 *reg = LOGICVC_POWER_CTRL_REG; 35 36 /* To the (virtual) power ctrl offset. */ 37 offset -= LOGICVC_CTRL_GPIO_BITS; 38 /* To the actual bit offset in reg. */ 39 offset += LOGICVC_POWER_CTRL_GPIO_SHIFT; 40 } else { 41 *reg = LOGICVC_CTRL_REG; 42 43 /* To the actual bit offset in reg. */ 44 offset += LOGICVC_CTRL_GPIO_SHIFT; 45 } 46 47 *bit = BIT(offset); 48 } 49 50 static int logicvc_gpio_get(struct gpio_chip *chip, unsigned offset) 51 { 52 struct logicvc_gpio *logicvc = gpiochip_get_data(chip); 53 unsigned int reg, bit, value; 54 int ret; 55 56 logicvc_gpio_offset(logicvc, offset, ®, &bit); 57 58 ret = regmap_read(logicvc->regmap, reg, &value); 59 if (ret) 60 return ret; 61 62 return !!(value & bit); 63 } 64 65 static void logicvc_gpio_set(struct gpio_chip *chip, unsigned offset, int value) 66 { 67 struct logicvc_gpio *logicvc = gpiochip_get_data(chip); 68 unsigned int reg, bit; 69 70 logicvc_gpio_offset(logicvc, offset, ®, &bit); 71 72 regmap_update_bits(logicvc->regmap, reg, bit, value ? bit : 0); 73 } 74 75 static int logicvc_gpio_direction_output(struct gpio_chip *chip, 76 unsigned offset, int value) 77 { 78 /* Pins are always configured as output, so just set the value. */ 79 logicvc_gpio_set(chip, offset, value); 80 81 return 0; 82 } 83 84 static struct regmap_config logicvc_gpio_regmap_config = { 85 .reg_bits = 32, 86 .val_bits = 32, 87 .reg_stride = 4, 88 .name = "logicvc-gpio", 89 }; 90 91 static int logicvc_gpio_probe(struct platform_device *pdev) 92 { 93 struct device *dev = &pdev->dev; 94 struct device_node *of_node = dev->of_node; 95 struct logicvc_gpio *logicvc; 96 int ret; 97 98 logicvc = devm_kzalloc(dev, sizeof(*logicvc), GFP_KERNEL); 99 if (!logicvc) 100 return -ENOMEM; 101 102 /* Try to get regmap from parent first. */ 103 logicvc->regmap = syscon_node_to_regmap(of_node->parent); 104 105 /* Grab our own regmap if that fails. */ 106 if (IS_ERR(logicvc->regmap)) { 107 struct resource res; 108 void __iomem *base; 109 110 ret = of_address_to_resource(of_node, 0, &res); 111 if (ret) { 112 dev_err(dev, "Failed to get resource from address\n"); 113 return ret; 114 } 115 116 base = devm_ioremap_resource(dev, &res); 117 if (IS_ERR(base)) { 118 dev_err(dev, "Failed to map I/O base\n"); 119 return PTR_ERR(base); 120 } 121 122 logicvc_gpio_regmap_config.max_register = resource_size(&res) - 123 logicvc_gpio_regmap_config.reg_stride; 124 125 logicvc->regmap = 126 devm_regmap_init_mmio(dev, base, 127 &logicvc_gpio_regmap_config); 128 if (IS_ERR(logicvc->regmap)) { 129 dev_err(dev, "Failed to create regmap for I/O\n"); 130 return PTR_ERR(logicvc->regmap); 131 } 132 } 133 134 logicvc->chip.parent = dev; 135 logicvc->chip.owner = THIS_MODULE; 136 logicvc->chip.label = dev_name(dev); 137 logicvc->chip.base = -1; 138 logicvc->chip.ngpio = LOGICVC_CTRL_GPIO_BITS + 139 LOGICVC_POWER_CTRL_GPIO_BITS; 140 logicvc->chip.get = logicvc_gpio_get; 141 logicvc->chip.set = logicvc_gpio_set; 142 logicvc->chip.direction_output = logicvc_gpio_direction_output; 143 144 platform_set_drvdata(pdev, logicvc); 145 146 return devm_gpiochip_add_data(dev, &logicvc->chip, logicvc); 147 } 148 149 static const struct of_device_id logicivc_gpio_of_table[] = { 150 { 151 .compatible = "xylon,logicvc-3.02.a-gpio", 152 }, 153 { } 154 }; 155 156 MODULE_DEVICE_TABLE(of, logicivc_gpio_of_table); 157 158 static struct platform_driver logicvc_gpio_driver = { 159 .driver = { 160 .name = "gpio-logicvc", 161 .of_match_table = logicivc_gpio_of_table, 162 }, 163 .probe = logicvc_gpio_probe, 164 }; 165 166 module_platform_driver(logicvc_gpio_driver); 167 168 MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>"); 169 MODULE_DESCRIPTION("Xylon LogiCVC GPIO driver"); 170 MODULE_LICENSE("GPL"); 171