1*0f98dd1bSBamvor Jian Zhang /* 2*0f98dd1bSBamvor Jian Zhang * GPIO Testing Device Driver 3*0f98dd1bSBamvor Jian Zhang * 4*0f98dd1bSBamvor Jian Zhang * Copyright (C) 2014 Kamlakant Patel <kamlakant.patel@broadcom.com> 5*0f98dd1bSBamvor Jian Zhang * Copyright (C) 2015-2016 Bamvor Jian Zhang <bamvor.zhangjian@linaro.org> 6*0f98dd1bSBamvor Jian Zhang * 7*0f98dd1bSBamvor Jian Zhang * This program is free software; you can redistribute it and/or modify it 8*0f98dd1bSBamvor Jian Zhang * under the terms of the GNU General Public License as published by the 9*0f98dd1bSBamvor Jian Zhang * Free Software Foundation; either version 2 of the License, or (at your 10*0f98dd1bSBamvor Jian Zhang * option) any later version. 11*0f98dd1bSBamvor Jian Zhang * 12*0f98dd1bSBamvor Jian Zhang */ 13*0f98dd1bSBamvor Jian Zhang 14*0f98dd1bSBamvor Jian Zhang #include <linux/init.h> 15*0f98dd1bSBamvor Jian Zhang #include <linux/module.h> 16*0f98dd1bSBamvor Jian Zhang #include <linux/gpio/driver.h> 17*0f98dd1bSBamvor Jian Zhang #include <linux/platform_device.h> 18*0f98dd1bSBamvor Jian Zhang 19*0f98dd1bSBamvor Jian Zhang #define GPIO_NAME "gpio-mockup" 20*0f98dd1bSBamvor Jian Zhang #define MAX_GC 10 21*0f98dd1bSBamvor Jian Zhang 22*0f98dd1bSBamvor Jian Zhang enum direction { 23*0f98dd1bSBamvor Jian Zhang OUT, 24*0f98dd1bSBamvor Jian Zhang IN 25*0f98dd1bSBamvor Jian Zhang }; 26*0f98dd1bSBamvor Jian Zhang 27*0f98dd1bSBamvor Jian Zhang /* 28*0f98dd1bSBamvor Jian Zhang * struct gpio_pin_status - structure describing a GPIO status 29*0f98dd1bSBamvor Jian Zhang * @dir: Configures direction of gpio as "in" or "out", 0=in, 1=out 30*0f98dd1bSBamvor Jian Zhang * @value: Configures status of the gpio as 0(low) or 1(high) 31*0f98dd1bSBamvor Jian Zhang */ 32*0f98dd1bSBamvor Jian Zhang struct gpio_pin_status { 33*0f98dd1bSBamvor Jian Zhang enum direction dir; 34*0f98dd1bSBamvor Jian Zhang bool value; 35*0f98dd1bSBamvor Jian Zhang }; 36*0f98dd1bSBamvor Jian Zhang 37*0f98dd1bSBamvor Jian Zhang struct mockup_gpio_controller { 38*0f98dd1bSBamvor Jian Zhang struct gpio_chip gc; 39*0f98dd1bSBamvor Jian Zhang struct gpio_pin_status *stats; 40*0f98dd1bSBamvor Jian Zhang }; 41*0f98dd1bSBamvor Jian Zhang 42*0f98dd1bSBamvor Jian Zhang static int gpio_mockup_ranges[MAX_GC << 1]; 43*0f98dd1bSBamvor Jian Zhang static int gpio_mockup_params_nr; 44*0f98dd1bSBamvor Jian Zhang module_param_array(gpio_mockup_ranges, int, &gpio_mockup_params_nr, 0400); 45*0f98dd1bSBamvor Jian Zhang 46*0f98dd1bSBamvor Jian Zhang const char pins_name_start = 'A'; 47*0f98dd1bSBamvor Jian Zhang 48*0f98dd1bSBamvor Jian Zhang static int mockup_gpio_get(struct gpio_chip *gc, unsigned int offset) 49*0f98dd1bSBamvor Jian Zhang { 50*0f98dd1bSBamvor Jian Zhang struct mockup_gpio_controller *cntr = gpiochip_get_data(gc); 51*0f98dd1bSBamvor Jian Zhang 52*0f98dd1bSBamvor Jian Zhang return cntr->stats[offset].value; 53*0f98dd1bSBamvor Jian Zhang } 54*0f98dd1bSBamvor Jian Zhang 55*0f98dd1bSBamvor Jian Zhang static void mockup_gpio_set(struct gpio_chip *gc, unsigned int offset, 56*0f98dd1bSBamvor Jian Zhang int value) 57*0f98dd1bSBamvor Jian Zhang { 58*0f98dd1bSBamvor Jian Zhang struct mockup_gpio_controller *cntr = gpiochip_get_data(gc); 59*0f98dd1bSBamvor Jian Zhang 60*0f98dd1bSBamvor Jian Zhang cntr->stats[offset].value = !!value; 61*0f98dd1bSBamvor Jian Zhang } 62*0f98dd1bSBamvor Jian Zhang 63*0f98dd1bSBamvor Jian Zhang static int mockup_gpio_dirout(struct gpio_chip *gc, unsigned int offset, 64*0f98dd1bSBamvor Jian Zhang int value) 65*0f98dd1bSBamvor Jian Zhang { 66*0f98dd1bSBamvor Jian Zhang struct mockup_gpio_controller *cntr = gpiochip_get_data(gc); 67*0f98dd1bSBamvor Jian Zhang 68*0f98dd1bSBamvor Jian Zhang mockup_gpio_set(gc, offset, value); 69*0f98dd1bSBamvor Jian Zhang cntr->stats[offset].dir = OUT; 70*0f98dd1bSBamvor Jian Zhang return 0; 71*0f98dd1bSBamvor Jian Zhang } 72*0f98dd1bSBamvor Jian Zhang 73*0f98dd1bSBamvor Jian Zhang static int mockup_gpio_dirin(struct gpio_chip *gc, unsigned int offset) 74*0f98dd1bSBamvor Jian Zhang { 75*0f98dd1bSBamvor Jian Zhang struct mockup_gpio_controller *cntr = gpiochip_get_data(gc); 76*0f98dd1bSBamvor Jian Zhang 77*0f98dd1bSBamvor Jian Zhang cntr->stats[offset].dir = IN; 78*0f98dd1bSBamvor Jian Zhang return 0; 79*0f98dd1bSBamvor Jian Zhang } 80*0f98dd1bSBamvor Jian Zhang 81*0f98dd1bSBamvor Jian Zhang static int mockup_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) 82*0f98dd1bSBamvor Jian Zhang { 83*0f98dd1bSBamvor Jian Zhang struct mockup_gpio_controller *cntr = gpiochip_get_data(gc); 84*0f98dd1bSBamvor Jian Zhang 85*0f98dd1bSBamvor Jian Zhang return cntr->stats[offset].dir; 86*0f98dd1bSBamvor Jian Zhang } 87*0f98dd1bSBamvor Jian Zhang 88*0f98dd1bSBamvor Jian Zhang static int mockup_gpio_add(struct device *dev, 89*0f98dd1bSBamvor Jian Zhang struct mockup_gpio_controller *cntr, 90*0f98dd1bSBamvor Jian Zhang const char *name, int base, int ngpio) 91*0f98dd1bSBamvor Jian Zhang { 92*0f98dd1bSBamvor Jian Zhang int ret; 93*0f98dd1bSBamvor Jian Zhang 94*0f98dd1bSBamvor Jian Zhang cntr->gc.base = base; 95*0f98dd1bSBamvor Jian Zhang cntr->gc.ngpio = ngpio; 96*0f98dd1bSBamvor Jian Zhang cntr->gc.label = name; 97*0f98dd1bSBamvor Jian Zhang cntr->gc.owner = THIS_MODULE; 98*0f98dd1bSBamvor Jian Zhang cntr->gc.parent = dev; 99*0f98dd1bSBamvor Jian Zhang cntr->gc.get = mockup_gpio_get; 100*0f98dd1bSBamvor Jian Zhang cntr->gc.set = mockup_gpio_set; 101*0f98dd1bSBamvor Jian Zhang cntr->gc.direction_output = mockup_gpio_dirout; 102*0f98dd1bSBamvor Jian Zhang cntr->gc.direction_input = mockup_gpio_dirin; 103*0f98dd1bSBamvor Jian Zhang cntr->gc.get_direction = mockup_gpio_get_direction; 104*0f98dd1bSBamvor Jian Zhang cntr->stats = devm_kzalloc(dev, sizeof(*cntr->stats) * cntr->gc.ngpio, 105*0f98dd1bSBamvor Jian Zhang GFP_KERNEL); 106*0f98dd1bSBamvor Jian Zhang if (!cntr->stats) { 107*0f98dd1bSBamvor Jian Zhang ret = -ENOMEM; 108*0f98dd1bSBamvor Jian Zhang goto err; 109*0f98dd1bSBamvor Jian Zhang } 110*0f98dd1bSBamvor Jian Zhang ret = devm_gpiochip_add_data(dev, &cntr->gc, cntr); 111*0f98dd1bSBamvor Jian Zhang if (ret) 112*0f98dd1bSBamvor Jian Zhang goto err; 113*0f98dd1bSBamvor Jian Zhang 114*0f98dd1bSBamvor Jian Zhang dev_info(dev, "gpio<%d..%d> add successful!", base, base + ngpio); 115*0f98dd1bSBamvor Jian Zhang return 0; 116*0f98dd1bSBamvor Jian Zhang err: 117*0f98dd1bSBamvor Jian Zhang dev_err(dev, "gpio<%d..%d> add failed!", base, base + ngpio); 118*0f98dd1bSBamvor Jian Zhang return ret; 119*0f98dd1bSBamvor Jian Zhang } 120*0f98dd1bSBamvor Jian Zhang 121*0f98dd1bSBamvor Jian Zhang static int mockup_gpio_probe(struct platform_device *pdev) 122*0f98dd1bSBamvor Jian Zhang { 123*0f98dd1bSBamvor Jian Zhang struct device *dev = &pdev->dev; 124*0f98dd1bSBamvor Jian Zhang struct mockup_gpio_controller *cntr; 125*0f98dd1bSBamvor Jian Zhang int ret; 126*0f98dd1bSBamvor Jian Zhang int i; 127*0f98dd1bSBamvor Jian Zhang int base; 128*0f98dd1bSBamvor Jian Zhang int ngpio; 129*0f98dd1bSBamvor Jian Zhang char chip_name[sizeof(GPIO_NAME) + 3]; 130*0f98dd1bSBamvor Jian Zhang 131*0f98dd1bSBamvor Jian Zhang if (gpio_mockup_params_nr < 2) 132*0f98dd1bSBamvor Jian Zhang return -EINVAL; 133*0f98dd1bSBamvor Jian Zhang 134*0f98dd1bSBamvor Jian Zhang cntr = devm_kzalloc(dev, sizeof(*cntr) * (gpio_mockup_params_nr >> 1), 135*0f98dd1bSBamvor Jian Zhang GFP_KERNEL); 136*0f98dd1bSBamvor Jian Zhang if (!cntr) 137*0f98dd1bSBamvor Jian Zhang return -ENOMEM; 138*0f98dd1bSBamvor Jian Zhang 139*0f98dd1bSBamvor Jian Zhang platform_set_drvdata(pdev, cntr); 140*0f98dd1bSBamvor Jian Zhang 141*0f98dd1bSBamvor Jian Zhang for (i = 0; i < gpio_mockup_params_nr >> 1; i++) { 142*0f98dd1bSBamvor Jian Zhang base = gpio_mockup_ranges[i * 2]; 143*0f98dd1bSBamvor Jian Zhang if (base == -1) 144*0f98dd1bSBamvor Jian Zhang ngpio = gpio_mockup_ranges[i * 2 + 1]; 145*0f98dd1bSBamvor Jian Zhang else 146*0f98dd1bSBamvor Jian Zhang ngpio = gpio_mockup_ranges[i * 2 + 1] - base; 147*0f98dd1bSBamvor Jian Zhang 148*0f98dd1bSBamvor Jian Zhang if (ngpio >= 0) { 149*0f98dd1bSBamvor Jian Zhang sprintf(chip_name, "%s-%c", GPIO_NAME, 150*0f98dd1bSBamvor Jian Zhang pins_name_start + i); 151*0f98dd1bSBamvor Jian Zhang ret = mockup_gpio_add(dev, &cntr[i], 152*0f98dd1bSBamvor Jian Zhang chip_name, base, ngpio); 153*0f98dd1bSBamvor Jian Zhang } else { 154*0f98dd1bSBamvor Jian Zhang ret = -1; 155*0f98dd1bSBamvor Jian Zhang } 156*0f98dd1bSBamvor Jian Zhang if (ret) { 157*0f98dd1bSBamvor Jian Zhang if (base < 0) 158*0f98dd1bSBamvor Jian Zhang dev_err(dev, "gpio<%d..%d> add failed\n", 159*0f98dd1bSBamvor Jian Zhang base, ngpio); 160*0f98dd1bSBamvor Jian Zhang else 161*0f98dd1bSBamvor Jian Zhang dev_err(dev, "gpio<%d..%d> add failed\n", 162*0f98dd1bSBamvor Jian Zhang base, base + ngpio); 163*0f98dd1bSBamvor Jian Zhang 164*0f98dd1bSBamvor Jian Zhang return ret; 165*0f98dd1bSBamvor Jian Zhang } 166*0f98dd1bSBamvor Jian Zhang } 167*0f98dd1bSBamvor Jian Zhang 168*0f98dd1bSBamvor Jian Zhang return 0; 169*0f98dd1bSBamvor Jian Zhang } 170*0f98dd1bSBamvor Jian Zhang 171*0f98dd1bSBamvor Jian Zhang static struct platform_driver mockup_gpio_driver = { 172*0f98dd1bSBamvor Jian Zhang .driver = { 173*0f98dd1bSBamvor Jian Zhang .name = GPIO_NAME, 174*0f98dd1bSBamvor Jian Zhang }, 175*0f98dd1bSBamvor Jian Zhang .probe = mockup_gpio_probe, 176*0f98dd1bSBamvor Jian Zhang }; 177*0f98dd1bSBamvor Jian Zhang 178*0f98dd1bSBamvor Jian Zhang static struct platform_device *pdev; 179*0f98dd1bSBamvor Jian Zhang static int __init mock_device_init(void) 180*0f98dd1bSBamvor Jian Zhang { 181*0f98dd1bSBamvor Jian Zhang int err; 182*0f98dd1bSBamvor Jian Zhang 183*0f98dd1bSBamvor Jian Zhang pdev = platform_device_alloc(GPIO_NAME, -1); 184*0f98dd1bSBamvor Jian Zhang if (!pdev) 185*0f98dd1bSBamvor Jian Zhang return -ENOMEM; 186*0f98dd1bSBamvor Jian Zhang 187*0f98dd1bSBamvor Jian Zhang err = platform_device_add(pdev); 188*0f98dd1bSBamvor Jian Zhang if (err) { 189*0f98dd1bSBamvor Jian Zhang platform_device_put(pdev); 190*0f98dd1bSBamvor Jian Zhang return err; 191*0f98dd1bSBamvor Jian Zhang } 192*0f98dd1bSBamvor Jian Zhang 193*0f98dd1bSBamvor Jian Zhang err = platform_driver_register(&mockup_gpio_driver); 194*0f98dd1bSBamvor Jian Zhang if (err) { 195*0f98dd1bSBamvor Jian Zhang platform_device_unregister(pdev); 196*0f98dd1bSBamvor Jian Zhang return err; 197*0f98dd1bSBamvor Jian Zhang } 198*0f98dd1bSBamvor Jian Zhang 199*0f98dd1bSBamvor Jian Zhang return 0; 200*0f98dd1bSBamvor Jian Zhang } 201*0f98dd1bSBamvor Jian Zhang 202*0f98dd1bSBamvor Jian Zhang static void __exit mock_device_exit(void) 203*0f98dd1bSBamvor Jian Zhang { 204*0f98dd1bSBamvor Jian Zhang platform_driver_unregister(&mockup_gpio_driver); 205*0f98dd1bSBamvor Jian Zhang platform_device_unregister(pdev); 206*0f98dd1bSBamvor Jian Zhang } 207*0f98dd1bSBamvor Jian Zhang 208*0f98dd1bSBamvor Jian Zhang module_init(mock_device_init); 209*0f98dd1bSBamvor Jian Zhang module_exit(mock_device_exit); 210*0f98dd1bSBamvor Jian Zhang 211*0f98dd1bSBamvor Jian Zhang MODULE_AUTHOR("Kamlakant Patel <kamlakant.patel@broadcom.com>"); 212*0f98dd1bSBamvor Jian Zhang MODULE_AUTHOR("Bamvor Jian Zhang <bamvor.zhangjian@linaro.org>"); 213*0f98dd1bSBamvor Jian Zhang MODULE_DESCRIPTION("GPIO Testing driver"); 214*0f98dd1bSBamvor Jian Zhang MODULE_LICENSE("GPL v2"); 215