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 20 #define GPIO_MOCKUP_NAME "gpio-mockup" 21 #define GPIO_MOCKUP_MAX_GC 10 22 23 enum { 24 DIR_IN = 0, 25 DIR_OUT, 26 }; 27 28 /* 29 * struct gpio_pin_status - structure describing a GPIO status 30 * @dir: Configures direction of gpio as "in" or "out", 0=in, 1=out 31 * @value: Configures status of the gpio as 0(low) or 1(high) 32 */ 33 struct gpio_mockup_line_status { 34 int dir; 35 bool value; 36 }; 37 38 struct gpio_mockup_chip { 39 struct gpio_chip gc; 40 struct gpio_mockup_line_status *lines; 41 }; 42 43 static int gpio_mockup_ranges[GPIO_MOCKUP_MAX_GC << 1]; 44 static int gpio_mockup_params_nr; 45 module_param_array(gpio_mockup_ranges, int, &gpio_mockup_params_nr, 0400); 46 47 static bool gpio_mockup_named_lines; 48 module_param_named(gpio_mockup_named_lines, 49 gpio_mockup_named_lines, bool, 0400); 50 51 static const char gpio_mockup_name_start = 'A'; 52 53 static int gpio_mockup_get(struct gpio_chip *gc, unsigned int offset) 54 { 55 struct gpio_mockup_chip *chip = gpiochip_get_data(gc); 56 57 return chip->lines[offset].value; 58 } 59 60 static void gpio_mockup_set(struct gpio_chip *gc, unsigned int offset, 61 int value) 62 { 63 struct gpio_mockup_chip *chip = gpiochip_get_data(gc); 64 65 chip->lines[offset].value = !!value; 66 } 67 68 static int gpio_mockup_dirout(struct gpio_chip *gc, unsigned int offset, 69 int value) 70 { 71 struct gpio_mockup_chip *chip = gpiochip_get_data(gc); 72 73 gpio_mockup_set(gc, offset, value); 74 chip->lines[offset].dir = DIR_OUT; 75 76 return 0; 77 } 78 79 static int gpio_mockup_dirin(struct gpio_chip *gc, unsigned int offset) 80 { 81 struct gpio_mockup_chip *chip = gpiochip_get_data(gc); 82 83 chip->lines[offset].dir = DIR_IN; 84 85 return 0; 86 } 87 88 static int gpio_mockup_get_direction(struct gpio_chip *gc, unsigned int offset) 89 { 90 struct gpio_mockup_chip *chip = gpiochip_get_data(gc); 91 92 return chip->lines[offset].dir; 93 } 94 95 static int gpio_mockup_name_lines(struct device *dev, 96 struct gpio_mockup_chip *chip) 97 { 98 struct gpio_chip *gc = &chip->gc; 99 char **names; 100 int i; 101 102 names = devm_kzalloc(dev, sizeof(char *) * gc->ngpio, GFP_KERNEL); 103 if (!names) 104 return -ENOMEM; 105 106 for (i = 0; i < gc->ngpio; i++) { 107 names[i] = devm_kasprintf(dev, GFP_KERNEL, 108 "%s-%d", gc->label, i); 109 if (!names[i]) 110 return -ENOMEM; 111 } 112 113 gc->names = (const char *const *)names; 114 115 return 0; 116 } 117 118 static int gpio_mockup_add(struct device *dev, 119 struct gpio_mockup_chip *chip, 120 const char *name, int base, int ngpio) 121 { 122 struct gpio_chip *gc = &chip->gc; 123 int ret; 124 125 gc->base = base; 126 gc->ngpio = ngpio; 127 gc->label = name; 128 gc->owner = THIS_MODULE; 129 gc->parent = dev; 130 gc->get = gpio_mockup_get; 131 gc->set = gpio_mockup_set; 132 gc->direction_output = gpio_mockup_dirout; 133 gc->direction_input = gpio_mockup_dirin; 134 gc->get_direction = gpio_mockup_get_direction; 135 136 chip->lines = devm_kzalloc(dev, sizeof(*chip->lines) * gc->ngpio, 137 GFP_KERNEL); 138 if (!chip->lines) 139 return -ENOMEM; 140 141 if (gpio_mockup_named_lines) { 142 ret = gpio_mockup_name_lines(dev, chip); 143 if (ret) 144 return ret; 145 } 146 147 return devm_gpiochip_add_data(dev, &chip->gc, chip); 148 } 149 150 static int gpio_mockup_probe(struct platform_device *pdev) 151 { 152 struct gpio_mockup_chip *chips; 153 struct device *dev = &pdev->dev; 154 int ret, i, base, ngpio; 155 char *chip_name; 156 157 if (gpio_mockup_params_nr < 2) 158 return -EINVAL; 159 160 chips = devm_kzalloc(dev, 161 sizeof(*chips) * (gpio_mockup_params_nr >> 1), 162 GFP_KERNEL); 163 if (!chips) 164 return -ENOMEM; 165 166 platform_set_drvdata(pdev, chips); 167 168 for (i = 0; i < gpio_mockup_params_nr >> 1; i++) { 169 base = gpio_mockup_ranges[i * 2]; 170 171 if (base == -1) 172 ngpio = gpio_mockup_ranges[i * 2 + 1]; 173 else 174 ngpio = gpio_mockup_ranges[i * 2 + 1] - base; 175 176 if (ngpio >= 0) { 177 chip_name = devm_kasprintf(dev, GFP_KERNEL, 178 "%s-%c", GPIO_MOCKUP_NAME, 179 gpio_mockup_name_start + i); 180 if (!chip_name) 181 return -ENOMEM; 182 183 ret = gpio_mockup_add(dev, &chips[i], 184 chip_name, base, ngpio); 185 } else { 186 ret = -1; 187 } 188 189 if (ret) { 190 dev_err(dev, "gpio<%d..%d> add failed\n", 191 base, base < 0 ? ngpio : base + ngpio); 192 193 return ret; 194 } 195 196 dev_info(dev, "gpio<%d..%d> add successful!", 197 base, base + ngpio); 198 } 199 200 return 0; 201 } 202 203 static struct platform_driver gpio_mockup_driver = { 204 .driver = { 205 .name = GPIO_MOCKUP_NAME, 206 }, 207 .probe = gpio_mockup_probe, 208 }; 209 210 static struct platform_device *pdev; 211 static int __init mock_device_init(void) 212 { 213 int err; 214 215 pdev = platform_device_alloc(GPIO_MOCKUP_NAME, -1); 216 if (!pdev) 217 return -ENOMEM; 218 219 err = platform_device_add(pdev); 220 if (err) { 221 platform_device_put(pdev); 222 return err; 223 } 224 225 err = platform_driver_register(&gpio_mockup_driver); 226 if (err) { 227 platform_device_unregister(pdev); 228 return err; 229 } 230 231 return 0; 232 } 233 234 static void __exit mock_device_exit(void) 235 { 236 platform_driver_unregister(&gpio_mockup_driver); 237 platform_device_unregister(pdev); 238 } 239 240 module_init(mock_device_init); 241 module_exit(mock_device_exit); 242 243 MODULE_AUTHOR("Kamlakant Patel <kamlakant.patel@broadcom.com>"); 244 MODULE_AUTHOR("Bamvor Jian Zhang <bamvor.zhangjian@linaro.org>"); 245 MODULE_DESCRIPTION("GPIO Testing driver"); 246 MODULE_LICENSE("GPL v2"); 247