Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.0-rc4 182 lines 4.2 kB view raw
1/* 2 * 74Hx164 - Generic serial-in/parallel-out 8-bits shift register GPIO driver 3 * 4 * Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org> 5 * Copyright (C) 2010 Miguel Gaio <miguel.gaio@efixo.com> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 */ 11 12#include <linux/init.h> 13#include <linux/mutex.h> 14#include <linux/spi/spi.h> 15#include <linux/spi/74x164.h> 16#include <linux/gpio.h> 17#include <linux/slab.h> 18 19#define GEN_74X164_GPIO_COUNT 8 20 21 22struct gen_74x164_chip { 23 struct spi_device *spi; 24 struct gpio_chip gpio_chip; 25 struct mutex lock; 26 u8 port_config; 27}; 28 29static void gen_74x164_set_value(struct gpio_chip *, unsigned, int); 30 31static struct gen_74x164_chip *gpio_to_chip(struct gpio_chip *gc) 32{ 33 return container_of(gc, struct gen_74x164_chip, gpio_chip); 34} 35 36static int __gen_74x164_write_config(struct gen_74x164_chip *chip) 37{ 38 return spi_write(chip->spi, 39 &chip->port_config, sizeof(chip->port_config)); 40} 41 42static int gen_74x164_direction_output(struct gpio_chip *gc, 43 unsigned offset, int val) 44{ 45 gen_74x164_set_value(gc, offset, val); 46 return 0; 47} 48 49static int gen_74x164_get_value(struct gpio_chip *gc, unsigned offset) 50{ 51 struct gen_74x164_chip *chip = gpio_to_chip(gc); 52 int ret; 53 54 mutex_lock(&chip->lock); 55 ret = (chip->port_config >> offset) & 0x1; 56 mutex_unlock(&chip->lock); 57 58 return ret; 59} 60 61static void gen_74x164_set_value(struct gpio_chip *gc, 62 unsigned offset, int val) 63{ 64 struct gen_74x164_chip *chip = gpio_to_chip(gc); 65 66 mutex_lock(&chip->lock); 67 if (val) 68 chip->port_config |= (1 << offset); 69 else 70 chip->port_config &= ~(1 << offset); 71 72 __gen_74x164_write_config(chip); 73 mutex_unlock(&chip->lock); 74} 75 76static int __devinit gen_74x164_probe(struct spi_device *spi) 77{ 78 struct gen_74x164_chip *chip; 79 struct gen_74x164_chip_platform_data *pdata; 80 int ret; 81 82 pdata = spi->dev.platform_data; 83 if (!pdata || !pdata->base) { 84 dev_dbg(&spi->dev, "incorrect or missing platform data\n"); 85 return -EINVAL; 86 } 87 88 /* 89 * bits_per_word cannot be configured in platform data 90 */ 91 spi->bits_per_word = 8; 92 93 ret = spi_setup(spi); 94 if (ret < 0) 95 return ret; 96 97 chip = kzalloc(sizeof(*chip), GFP_KERNEL); 98 if (!chip) 99 return -ENOMEM; 100 101 mutex_init(&chip->lock); 102 103 dev_set_drvdata(&spi->dev, chip); 104 105 chip->spi = spi; 106 107 chip->gpio_chip.label = GEN_74X164_DRIVER_NAME, 108 chip->gpio_chip.direction_output = gen_74x164_direction_output; 109 chip->gpio_chip.get = gen_74x164_get_value; 110 chip->gpio_chip.set = gen_74x164_set_value; 111 chip->gpio_chip.base = pdata->base; 112 chip->gpio_chip.ngpio = GEN_74X164_GPIO_COUNT; 113 chip->gpio_chip.can_sleep = 1; 114 chip->gpio_chip.dev = &spi->dev; 115 chip->gpio_chip.owner = THIS_MODULE; 116 117 ret = __gen_74x164_write_config(chip); 118 if (ret) { 119 dev_err(&spi->dev, "Failed writing: %d\n", ret); 120 goto exit_destroy; 121 } 122 123 ret = gpiochip_add(&chip->gpio_chip); 124 if (ret) 125 goto exit_destroy; 126 127 return ret; 128 129exit_destroy: 130 dev_set_drvdata(&spi->dev, NULL); 131 mutex_destroy(&chip->lock); 132 kfree(chip); 133 return ret; 134} 135 136static int __devexit gen_74x164_remove(struct spi_device *spi) 137{ 138 struct gen_74x164_chip *chip; 139 int ret; 140 141 chip = dev_get_drvdata(&spi->dev); 142 if (chip == NULL) 143 return -ENODEV; 144 145 dev_set_drvdata(&spi->dev, NULL); 146 147 ret = gpiochip_remove(&chip->gpio_chip); 148 if (!ret) { 149 mutex_destroy(&chip->lock); 150 kfree(chip); 151 } else 152 dev_err(&spi->dev, "Failed to remove the GPIO controller: %d\n", 153 ret); 154 155 return ret; 156} 157 158static struct spi_driver gen_74x164_driver = { 159 .driver = { 160 .name = GEN_74X164_DRIVER_NAME, 161 .owner = THIS_MODULE, 162 }, 163 .probe = gen_74x164_probe, 164 .remove = __devexit_p(gen_74x164_remove), 165}; 166 167static int __init gen_74x164_init(void) 168{ 169 return spi_register_driver(&gen_74x164_driver); 170} 171subsys_initcall(gen_74x164_init); 172 173static void __exit gen_74x164_exit(void) 174{ 175 spi_unregister_driver(&gen_74x164_driver); 176} 177module_exit(gen_74x164_exit); 178 179MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); 180MODULE_AUTHOR("Miguel Gaio <miguel.gaio@efixo.com>"); 181MODULE_DESCRIPTION("GPIO expander driver for 74X164 8-bits shift register"); 182MODULE_LICENSE("GPL v2");