1 /* 2 * GPIO driver for Analog Devices ADP5520 MFD PMICs 3 * 4 * Copyright 2009 Analog Devices Inc. 5 * 6 * Licensed under the GPL-2 or later. 7 */ 8 9 #include <linux/module.h> 10 #include <linux/slab.h> 11 #include <linux/kernel.h> 12 #include <linux/init.h> 13 #include <linux/platform_device.h> 14 #include <linux/mfd/adp5520.h> 15 #include <linux/gpio/driver.h> 16 17 struct adp5520_gpio { 18 struct device *master; 19 struct gpio_chip gpio_chip; 20 unsigned char lut[ADP5520_MAXGPIOS]; 21 unsigned long output; 22 }; 23 24 static int adp5520_gpio_get_value(struct gpio_chip *chip, unsigned off) 25 { 26 struct adp5520_gpio *dev; 27 uint8_t reg_val; 28 29 dev = gpiochip_get_data(chip); 30 31 /* 32 * There are dedicated registers for GPIO IN/OUT. 33 * Make sure we return the right value, even when configured as output 34 */ 35 36 if (test_bit(off, &dev->output)) 37 adp5520_read(dev->master, ADP5520_GPIO_OUT, ®_val); 38 else 39 adp5520_read(dev->master, ADP5520_GPIO_IN, ®_val); 40 41 return !!(reg_val & dev->lut[off]); 42 } 43 44 static void adp5520_gpio_set_value(struct gpio_chip *chip, 45 unsigned off, int val) 46 { 47 struct adp5520_gpio *dev; 48 dev = gpiochip_get_data(chip); 49 50 if (val) 51 adp5520_set_bits(dev->master, ADP5520_GPIO_OUT, dev->lut[off]); 52 else 53 adp5520_clr_bits(dev->master, ADP5520_GPIO_OUT, dev->lut[off]); 54 } 55 56 static int adp5520_gpio_direction_input(struct gpio_chip *chip, unsigned off) 57 { 58 struct adp5520_gpio *dev; 59 dev = gpiochip_get_data(chip); 60 61 clear_bit(off, &dev->output); 62 63 return adp5520_clr_bits(dev->master, ADP5520_GPIO_CFG_2, 64 dev->lut[off]); 65 } 66 67 static int adp5520_gpio_direction_output(struct gpio_chip *chip, 68 unsigned off, int val) 69 { 70 struct adp5520_gpio *dev; 71 int ret = 0; 72 dev = gpiochip_get_data(chip); 73 74 set_bit(off, &dev->output); 75 76 if (val) 77 ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_OUT, 78 dev->lut[off]); 79 else 80 ret |= adp5520_clr_bits(dev->master, ADP5520_GPIO_OUT, 81 dev->lut[off]); 82 83 ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_CFG_2, 84 dev->lut[off]); 85 86 return ret; 87 } 88 89 static int adp5520_gpio_probe(struct platform_device *pdev) 90 { 91 struct adp5520_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev); 92 struct adp5520_gpio *dev; 93 struct gpio_chip *gc; 94 int ret, i, gpios; 95 unsigned char ctl_mask = 0; 96 97 if (pdata == NULL) { 98 dev_err(&pdev->dev, "missing platform data\n"); 99 return -ENODEV; 100 } 101 102 if (pdev->id != ID_ADP5520) { 103 dev_err(&pdev->dev, "only ADP5520 supports GPIO\n"); 104 return -ENODEV; 105 } 106 107 dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); 108 if (dev == NULL) 109 return -ENOMEM; 110 111 dev->master = pdev->dev.parent; 112 113 for (gpios = 0, i = 0; i < ADP5520_MAXGPIOS; i++) 114 if (pdata->gpio_en_mask & (1 << i)) 115 dev->lut[gpios++] = 1 << i; 116 117 if (gpios < 1) { 118 ret = -EINVAL; 119 goto err; 120 } 121 122 gc = &dev->gpio_chip; 123 gc->direction_input = adp5520_gpio_direction_input; 124 gc->direction_output = adp5520_gpio_direction_output; 125 gc->get = adp5520_gpio_get_value; 126 gc->set = adp5520_gpio_set_value; 127 gc->can_sleep = true; 128 129 gc->base = pdata->gpio_start; 130 gc->ngpio = gpios; 131 gc->label = pdev->name; 132 gc->owner = THIS_MODULE; 133 134 ret = adp5520_clr_bits(dev->master, ADP5520_GPIO_CFG_1, 135 pdata->gpio_en_mask); 136 137 if (pdata->gpio_en_mask & ADP5520_GPIO_C3) 138 ctl_mask |= ADP5520_C3_MODE; 139 140 if (pdata->gpio_en_mask & ADP5520_GPIO_R3) 141 ctl_mask |= ADP5520_R3_MODE; 142 143 if (ctl_mask) 144 ret = adp5520_set_bits(dev->master, ADP5520_LED_CONTROL, 145 ctl_mask); 146 147 ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_PULLUP, 148 pdata->gpio_pullup_mask); 149 150 if (ret) { 151 dev_err(&pdev->dev, "failed to write\n"); 152 goto err; 153 } 154 155 ret = devm_gpiochip_add_data(&pdev->dev, &dev->gpio_chip, dev); 156 if (ret) 157 goto err; 158 159 platform_set_drvdata(pdev, dev); 160 return 0; 161 162 err: 163 return ret; 164 } 165 166 static struct platform_driver adp5520_gpio_driver = { 167 .driver = { 168 .name = "adp5520-gpio", 169 }, 170 .probe = adp5520_gpio_probe, 171 }; 172 173 module_platform_driver(adp5520_gpio_driver); 174 175 MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); 176 MODULE_DESCRIPTION("GPIO ADP5520 Driver"); 177 MODULE_LICENSE("GPL"); 178 MODULE_ALIAS("platform:adp5520-gpio"); 179