1 /* 2 * GPIO Testing Device Driver 3 * 4 * Copyright (C) 2014 Kamlakant Patel <kamlakant.patel@broadcom.com> 5 * Copyright (C) 2015-2016 Bamvor Jian Zhang <bamvor.zhangjian@linaro.org> 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the 9 * Free Software Foundation; either version 2 of the License, or (at your 10 * option) any later version. 11 * 12 */ 13 14 #include <linux/init.h> 15 #include <linux/module.h> 16 #include <linux/gpio/driver.h> 17 #include <linux/platform_device.h> 18 #include <linux/slab.h> 19 #include <linux/interrupt.h> 20 #include <linux/irq.h> 21 #include <linux/irq_work.h> 22 23 #define GPIO_MOCKUP_NAME "gpio-mockup" 24 #define GPIO_MOCKUP_MAX_GC 10 25 26 enum { 27 DIR_IN = 0, 28 DIR_OUT, 29 }; 30 31 /* 32 * struct gpio_pin_status - structure describing a GPIO status 33 * @dir: Configures direction of gpio as "in" or "out", 0=in, 1=out 34 * @value: Configures status of the gpio as 0(low) or 1(high) 35 */ 36 struct gpio_mockup_line_status { 37 int dir; 38 bool value; 39 }; 40 41 struct gpio_mockup_irq_context { 42 struct irq_work work; 43 int irq; 44 }; 45 46 struct gpio_mockup_chip { 47 struct gpio_chip gc; 48 struct gpio_mockup_line_status *lines; 49 struct gpio_mockup_irq_context irq_ctx; 50 }; 51 52 static int gpio_mockup_ranges[GPIO_MOCKUP_MAX_GC << 1]; 53 static int gpio_mockup_params_nr; 54 module_param_array(gpio_mockup_ranges, int, &gpio_mockup_params_nr, 0400); 55 56 static bool gpio_mockup_named_lines; 57 module_param_named(gpio_mockup_named_lines, 58 gpio_mockup_named_lines, bool, 0400); 59 60 static const char gpio_mockup_name_start = 'A'; 61 62 static int gpio_mockup_get(struct gpio_chip *gc, unsigned int offset) 63 { 64 struct gpio_mockup_chip *chip = gpiochip_get_data(gc); 65 66 return chip->lines[offset].value; 67 } 68 69 static void gpio_mockup_set(struct gpio_chip *gc, unsigned int offset, 70 int value) 71 { 72 struct gpio_mockup_chip *chip = gpiochip_get_data(gc); 73 74 chip->lines[offset].value = !!value; 75 } 76 77 static int gpio_mockup_dirout(struct gpio_chip *gc, unsigned int offset, 78 int value) 79 { 80 struct gpio_mockup_chip *chip = gpiochip_get_data(gc); 81 82 gpio_mockup_set(gc, offset, value); 83 chip->lines[offset].dir = DIR_OUT; 84 85 return 0; 86 } 87 88 static int gpio_mockup_dirin(struct gpio_chip *gc, unsigned int offset) 89 { 90 struct gpio_mockup_chip *chip = gpiochip_get_data(gc); 91 92 chip->lines[offset].dir = DIR_IN; 93 94 return 0; 95 } 96 97 static int gpio_mockup_get_direction(struct gpio_chip *gc, unsigned int offset) 98 { 99 struct gpio_mockup_chip *chip = gpiochip_get_data(gc); 100 101 return chip->lines[offset].dir; 102 } 103 104 static int gpio_mockup_name_lines(struct device *dev, 105 struct gpio_mockup_chip *chip) 106 { 107 struct gpio_chip *gc = &chip->gc; 108 char **names; 109 int i; 110 111 names = devm_kzalloc(dev, sizeof(char *) * gc->ngpio, GFP_KERNEL); 112 if (!names) 113 return -ENOMEM; 114 115 for (i = 0; i < gc->ngpio; i++) { 116 names[i] = devm_kasprintf(dev, GFP_KERNEL, 117 "%s-%d", gc->label, i); 118 if (!names[i]) 119 return -ENOMEM; 120 } 121 122 gc->names = (const char *const *)names; 123 124 return 0; 125 } 126 127 static int gpio_mockup_to_irq(struct gpio_chip *chip, unsigned int offset) 128 { 129 return chip->irq_base + offset; 130 } 131 132 /* 133 * While we should generally support irqmask and irqunmask, this driver is 134 * for testing purposes only so we don't care. 135 */ 136 static void gpio_mockup_irqmask(struct irq_data *d) { } 137 static void gpio_mockup_irqunmask(struct irq_data *d) { } 138 139 static struct irq_chip gpio_mockup_irqchip = { 140 .name = GPIO_MOCKUP_NAME, 141 .irq_mask = gpio_mockup_irqmask, 142 .irq_unmask = gpio_mockup_irqunmask, 143 }; 144 145 static void gpio_mockup_handle_irq(struct irq_work *work) 146 { 147 struct gpio_mockup_irq_context *irq_ctx; 148 149 irq_ctx = container_of(work, struct gpio_mockup_irq_context, work); 150 handle_simple_irq(irq_to_desc(irq_ctx->irq)); 151 } 152 153 static int gpio_mockup_irqchip_setup(struct device *dev, 154 struct gpio_mockup_chip *chip) 155 { 156 struct gpio_chip *gc = &chip->gc; 157 int irq_base, i; 158 159 irq_base = irq_alloc_descs(-1, 0, gc->ngpio, 0); 160 if (irq_base < 0) 161 return irq_base; 162 163 gc->irq_base = irq_base; 164 gc->irqchip = &gpio_mockup_irqchip; 165 166 for (i = 0; i < gc->ngpio; i++) { 167 irq_set_chip(irq_base + i, gc->irqchip); 168 irq_set_handler(irq_base + i, &handle_simple_irq); 169 irq_modify_status(irq_base + i, 170 IRQ_NOREQUEST | IRQ_NOAUTOEN, IRQ_NOPROBE); 171 } 172 173 init_irq_work(&chip->irq_ctx.work, gpio_mockup_handle_irq); 174 175 return 0; 176 } 177 178 static int gpio_mockup_add(struct device *dev, 179 struct gpio_mockup_chip *chip, 180 const char *name, int base, int ngpio) 181 { 182 struct gpio_chip *gc = &chip->gc; 183 int ret; 184 185 gc->base = base; 186 gc->ngpio = ngpio; 187 gc->label = name; 188 gc->owner = THIS_MODULE; 189 gc->parent = dev; 190 gc->get = gpio_mockup_get; 191 gc->set = gpio_mockup_set; 192 gc->direction_output = gpio_mockup_dirout; 193 gc->direction_input = gpio_mockup_dirin; 194 gc->get_direction = gpio_mockup_get_direction; 195 gc->to_irq = gpio_mockup_to_irq; 196 197 chip->lines = devm_kzalloc(dev, sizeof(*chip->lines) * gc->ngpio, 198 GFP_KERNEL); 199 if (!chip->lines) 200 return -ENOMEM; 201 202 if (gpio_mockup_named_lines) { 203 ret = gpio_mockup_name_lines(dev, chip); 204 if (ret) 205 return ret; 206 } 207 208 ret = gpio_mockup_irqchip_setup(dev, chip); 209 if (ret) 210 return ret; 211 212 return devm_gpiochip_add_data(dev, &chip->gc, chip); 213 } 214 215 static int gpio_mockup_probe(struct platform_device *pdev) 216 { 217 struct gpio_mockup_chip *chips; 218 struct device *dev = &pdev->dev; 219 int ret, i, base, ngpio; 220 char *chip_name; 221 222 if (gpio_mockup_params_nr < 2) 223 return -EINVAL; 224 225 chips = devm_kzalloc(dev, 226 sizeof(*chips) * (gpio_mockup_params_nr >> 1), 227 GFP_KERNEL); 228 if (!chips) 229 return -ENOMEM; 230 231 platform_set_drvdata(pdev, chips); 232 233 for (i = 0; i < gpio_mockup_params_nr >> 1; i++) { 234 base = gpio_mockup_ranges[i * 2]; 235 236 if (base == -1) 237 ngpio = gpio_mockup_ranges[i * 2 + 1]; 238 else 239 ngpio = gpio_mockup_ranges[i * 2 + 1] - base; 240 241 if (ngpio >= 0) { 242 chip_name = devm_kasprintf(dev, GFP_KERNEL, 243 "%s-%c", GPIO_MOCKUP_NAME, 244 gpio_mockup_name_start + i); 245 if (!chip_name) 246 return -ENOMEM; 247 248 ret = gpio_mockup_add(dev, &chips[i], 249 chip_name, base, ngpio); 250 } else { 251 ret = -1; 252 } 253 254 if (ret) { 255 dev_err(dev, "gpio<%d..%d> add failed\n", 256 base, base < 0 ? ngpio : base + ngpio); 257 258 return ret; 259 } 260 261 dev_info(dev, "gpio<%d..%d> add successful!", 262 base, base + ngpio); 263 } 264 265 return 0; 266 } 267 268 static int gpio_mockup_remove(struct platform_device *pdev) 269 { 270 struct gpio_mockup_chip *chips; 271 int i; 272 273 chips = platform_get_drvdata(pdev); 274 275 for (i = 0; i < gpio_mockup_params_nr >> 1; i++) 276 irq_free_descs(chips[i].gc.irq_base, chips[i].gc.ngpio); 277 278 return 0; 279 } 280 281 static struct platform_driver gpio_mockup_driver = { 282 .driver = { 283 .name = GPIO_MOCKUP_NAME, 284 }, 285 .probe = gpio_mockup_probe, 286 .remove = gpio_mockup_remove, 287 }; 288 289 static struct platform_device *pdev; 290 static int __init mock_device_init(void) 291 { 292 int err; 293 294 pdev = platform_device_alloc(GPIO_MOCKUP_NAME, -1); 295 if (!pdev) 296 return -ENOMEM; 297 298 err = platform_device_add(pdev); 299 if (err) { 300 platform_device_put(pdev); 301 return err; 302 } 303 304 err = platform_driver_register(&gpio_mockup_driver); 305 if (err) { 306 platform_device_unregister(pdev); 307 return err; 308 } 309 310 return 0; 311 } 312 313 static void __exit mock_device_exit(void) 314 { 315 platform_driver_unregister(&gpio_mockup_driver); 316 platform_device_unregister(pdev); 317 } 318 319 module_init(mock_device_init); 320 module_exit(mock_device_exit); 321 322 MODULE_AUTHOR("Kamlakant Patel <kamlakant.patel@broadcom.com>"); 323 MODULE_AUTHOR("Bamvor Jian Zhang <bamvor.zhangjian@linaro.org>"); 324 MODULE_DESCRIPTION("GPIO Testing driver"); 325 MODULE_LICENSE("GPL v2"); 326