| Gregory Bean | c34f16b | 2010-08-10 18:02:27 -0700 | [diff] [blame] | 1 | /* Copyright (c) 2010, Code Aurora Forum. All rights reserved. | 
 | 2 |  * | 
 | 3 |  * This program is free software; you can redistribute it and/or modify | 
 | 4 |  * it under the terms of the GNU General Public License version 2 and | 
 | 5 |  * only version 2 as published by the Free Software Foundation. | 
 | 6 |  * | 
 | 7 |  * This program is distributed in the hope that it will be useful, | 
 | 8 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 | 9 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 | 10 |  * GNU General Public License for more details. | 
 | 11 |  * | 
 | 12 |  * You should have received a copy of the GNU General Public License | 
 | 13 |  * along with this program; if not, write to the Free Software | 
 | 14 |  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | 
 | 15 |  * 02110-1301, USA. | 
 | 16 |  */ | 
 | 17 | #include <linux/gpio.h> | 
 | 18 | #include <linux/i2c.h> | 
 | 19 | #include <linux/init.h> | 
 | 20 | #include <linux/interrupt.h> | 
 | 21 | #include <linux/irq.h> | 
 | 22 | #include <linux/module.h> | 
 | 23 | #include <linux/mutex.h> | 
 | 24 | #include <linux/slab.h> | 
 | 25 | #include <linux/workqueue.h> | 
 | 26 | #include <linux/i2c/sx150x.h> | 
 | 27 |  | 
| Thomas Gleixner | 0ff56cd | 2011-02-14 18:08:51 +0100 | [diff] [blame] | 28 | #define NO_UPDATE_PENDING	-1 | 
 | 29 |  | 
| Gregory Bean | c34f16b | 2010-08-10 18:02:27 -0700 | [diff] [blame] | 30 | struct sx150x_device_data { | 
 | 31 | 	u8 reg_pullup; | 
 | 32 | 	u8 reg_pulldn; | 
 | 33 | 	u8 reg_drain; | 
 | 34 | 	u8 reg_polarity; | 
 | 35 | 	u8 reg_dir; | 
 | 36 | 	u8 reg_data; | 
 | 37 | 	u8 reg_irq_mask; | 
 | 38 | 	u8 reg_irq_src; | 
 | 39 | 	u8 reg_sense; | 
 | 40 | 	u8 reg_clock; | 
 | 41 | 	u8 reg_misc; | 
 | 42 | 	u8 reg_reset; | 
 | 43 | 	u8 ngpios; | 
 | 44 | }; | 
 | 45 |  | 
 | 46 | struct sx150x_chip { | 
 | 47 | 	struct gpio_chip                 gpio_chip; | 
 | 48 | 	struct i2c_client               *client; | 
 | 49 | 	const struct sx150x_device_data *dev_cfg; | 
 | 50 | 	int                              irq_summary; | 
 | 51 | 	int                              irq_base; | 
| Thomas Gleixner | 0ff56cd | 2011-02-14 18:08:51 +0100 | [diff] [blame] | 52 | 	int				 irq_update; | 
| Gregory Bean | c34f16b | 2010-08-10 18:02:27 -0700 | [diff] [blame] | 53 | 	u32                              irq_sense; | 
| Thomas Gleixner | 0ff56cd | 2011-02-14 18:08:51 +0100 | [diff] [blame] | 54 | 	u32				 irq_masked; | 
 | 55 | 	u32				 dev_sense; | 
 | 56 | 	u32				 dev_masked; | 
| Gregory Bean | c34f16b | 2010-08-10 18:02:27 -0700 | [diff] [blame] | 57 | 	struct irq_chip                  irq_chip; | 
 | 58 | 	struct mutex                     lock; | 
 | 59 | }; | 
 | 60 |  | 
 | 61 | static const struct sx150x_device_data sx150x_devices[] = { | 
 | 62 | 	[0] = { /* sx1508q */ | 
 | 63 | 		.reg_pullup   = 0x03, | 
 | 64 | 		.reg_pulldn   = 0x04, | 
 | 65 | 		.reg_drain    = 0x05, | 
 | 66 | 		.reg_polarity = 0x06, | 
 | 67 | 		.reg_dir      = 0x07, | 
 | 68 | 		.reg_data     = 0x08, | 
 | 69 | 		.reg_irq_mask = 0x09, | 
 | 70 | 		.reg_irq_src  = 0x0c, | 
 | 71 | 		.reg_sense    = 0x0b, | 
 | 72 | 		.reg_clock    = 0x0f, | 
 | 73 | 		.reg_misc     = 0x10, | 
 | 74 | 		.reg_reset    = 0x7d, | 
 | 75 | 		.ngpios       = 8 | 
 | 76 | 	}, | 
 | 77 | 	[1] = { /* sx1509q */ | 
 | 78 | 		.reg_pullup   = 0x07, | 
 | 79 | 		.reg_pulldn   = 0x09, | 
 | 80 | 		.reg_drain    = 0x0b, | 
 | 81 | 		.reg_polarity = 0x0d, | 
 | 82 | 		.reg_dir      = 0x0f, | 
 | 83 | 		.reg_data     = 0x11, | 
 | 84 | 		.reg_irq_mask = 0x13, | 
 | 85 | 		.reg_irq_src  = 0x19, | 
 | 86 | 		.reg_sense    = 0x17, | 
 | 87 | 		.reg_clock    = 0x1e, | 
 | 88 | 		.reg_misc     = 0x1f, | 
 | 89 | 		.reg_reset    = 0x7d, | 
 | 90 | 		.ngpios       = 16 | 
 | 91 | 	}, | 
 | 92 | }; | 
 | 93 |  | 
 | 94 | static const struct i2c_device_id sx150x_id[] = { | 
 | 95 | 	{"sx1508q", 0}, | 
 | 96 | 	{"sx1509q", 1}, | 
 | 97 | 	{} | 
 | 98 | }; | 
 | 99 | MODULE_DEVICE_TABLE(i2c, sx150x_id); | 
 | 100 |  | 
 | 101 | static s32 sx150x_i2c_write(struct i2c_client *client, u8 reg, u8 val) | 
 | 102 | { | 
 | 103 | 	s32 err = i2c_smbus_write_byte_data(client, reg, val); | 
 | 104 |  | 
 | 105 | 	if (err < 0) | 
 | 106 | 		dev_warn(&client->dev, | 
 | 107 | 			"i2c write fail: can't write %02x to %02x: %d\n", | 
 | 108 | 			val, reg, err); | 
 | 109 | 	return err; | 
 | 110 | } | 
 | 111 |  | 
 | 112 | static s32 sx150x_i2c_read(struct i2c_client *client, u8 reg, u8 *val) | 
 | 113 | { | 
 | 114 | 	s32 err = i2c_smbus_read_byte_data(client, reg); | 
 | 115 |  | 
 | 116 | 	if (err >= 0) | 
 | 117 | 		*val = err; | 
 | 118 | 	else | 
 | 119 | 		dev_warn(&client->dev, | 
 | 120 | 			"i2c read fail: can't read from %02x: %d\n", | 
 | 121 | 			reg, err); | 
 | 122 | 	return err; | 
 | 123 | } | 
 | 124 |  | 
 | 125 | static inline bool offset_is_oscio(struct sx150x_chip *chip, unsigned offset) | 
 | 126 | { | 
 | 127 | 	return (chip->dev_cfg->ngpios == offset); | 
 | 128 | } | 
 | 129 |  | 
 | 130 | /* | 
 | 131 |  * These utility functions solve the common problem of locating and setting | 
 | 132 |  * configuration bits.  Configuration bits are grouped into registers | 
 | 133 |  * whose indexes increase downwards.  For example, with eight-bit registers, | 
 | 134 |  * sixteen gpios would have their config bits grouped in the following order: | 
 | 135 |  * REGISTER N-1 [ f e d c b a 9 8 ] | 
 | 136 |  *          N   [ 7 6 5 4 3 2 1 0 ] | 
 | 137 |  * | 
 | 138 |  * For multi-bit configurations, the pattern gets wider: | 
 | 139 |  * REGISTER N-3 [ f f e e d d c c ] | 
 | 140 |  *          N-2 [ b b a a 9 9 8 8 ] | 
 | 141 |  *          N-1 [ 7 7 6 6 5 5 4 4 ] | 
 | 142 |  *          N   [ 3 3 2 2 1 1 0 0 ] | 
 | 143 |  * | 
 | 144 |  * Given the address of the starting register 'N', the index of the gpio | 
 | 145 |  * whose configuration we seek to change, and the width in bits of that | 
 | 146 |  * configuration, these functions allow us to locate the correct | 
 | 147 |  * register and mask the correct bits. | 
 | 148 |  */ | 
 | 149 | static inline void sx150x_find_cfg(u8 offset, u8 width, | 
 | 150 | 				u8 *reg, u8 *mask, u8 *shift) | 
 | 151 | { | 
 | 152 | 	*reg   -= offset * width / 8; | 
 | 153 | 	*mask   = (1 << width) - 1; | 
 | 154 | 	*shift  = (offset * width) % 8; | 
 | 155 | 	*mask <<= *shift; | 
 | 156 | } | 
 | 157 |  | 
 | 158 | static s32 sx150x_write_cfg(struct sx150x_chip *chip, | 
 | 159 | 			u8 offset, u8 width, u8 reg, u8 val) | 
 | 160 | { | 
 | 161 | 	u8  mask; | 
 | 162 | 	u8  data; | 
 | 163 | 	u8  shift; | 
 | 164 | 	s32 err; | 
 | 165 |  | 
 | 166 | 	sx150x_find_cfg(offset, width, ®, &mask, &shift); | 
 | 167 | 	err = sx150x_i2c_read(chip->client, reg, &data); | 
 | 168 | 	if (err < 0) | 
 | 169 | 		return err; | 
 | 170 |  | 
 | 171 | 	data &= ~mask; | 
 | 172 | 	data |= (val << shift) & mask; | 
 | 173 | 	return sx150x_i2c_write(chip->client, reg, data); | 
 | 174 | } | 
 | 175 |  | 
 | 176 | static int sx150x_get_io(struct sx150x_chip *chip, unsigned offset) | 
 | 177 | { | 
 | 178 | 	u8  reg = chip->dev_cfg->reg_data; | 
 | 179 | 	u8  mask; | 
 | 180 | 	u8  data; | 
 | 181 | 	u8  shift; | 
 | 182 | 	s32 err; | 
 | 183 |  | 
 | 184 | 	sx150x_find_cfg(offset, 1, ®, &mask, &shift); | 
 | 185 | 	err = sx150x_i2c_read(chip->client, reg, &data); | 
 | 186 | 	if (err >= 0) | 
 | 187 | 		err = (data & mask) != 0 ? 1 : 0; | 
 | 188 |  | 
 | 189 | 	return err; | 
 | 190 | } | 
 | 191 |  | 
 | 192 | static void sx150x_set_oscio(struct sx150x_chip *chip, int val) | 
 | 193 | { | 
 | 194 | 	sx150x_i2c_write(chip->client, | 
 | 195 | 			chip->dev_cfg->reg_clock, | 
 | 196 | 			(val ? 0x1f : 0x10)); | 
 | 197 | } | 
 | 198 |  | 
 | 199 | static void sx150x_set_io(struct sx150x_chip *chip, unsigned offset, int val) | 
 | 200 | { | 
 | 201 | 	sx150x_write_cfg(chip, | 
 | 202 | 			offset, | 
 | 203 | 			1, | 
 | 204 | 			chip->dev_cfg->reg_data, | 
 | 205 | 			(val ? 1 : 0)); | 
 | 206 | } | 
 | 207 |  | 
 | 208 | static int sx150x_io_input(struct sx150x_chip *chip, unsigned offset) | 
 | 209 | { | 
 | 210 | 	return sx150x_write_cfg(chip, | 
 | 211 | 				offset, | 
 | 212 | 				1, | 
 | 213 | 				chip->dev_cfg->reg_dir, | 
 | 214 | 				1); | 
 | 215 | } | 
 | 216 |  | 
 | 217 | static int sx150x_io_output(struct sx150x_chip *chip, unsigned offset, int val) | 
 | 218 | { | 
 | 219 | 	int err; | 
 | 220 |  | 
 | 221 | 	err = sx150x_write_cfg(chip, | 
 | 222 | 			offset, | 
 | 223 | 			1, | 
 | 224 | 			chip->dev_cfg->reg_data, | 
 | 225 | 			(val ? 1 : 0)); | 
 | 226 | 	if (err >= 0) | 
 | 227 | 		err = sx150x_write_cfg(chip, | 
 | 228 | 				offset, | 
 | 229 | 				1, | 
 | 230 | 				chip->dev_cfg->reg_dir, | 
 | 231 | 				0); | 
 | 232 | 	return err; | 
 | 233 | } | 
 | 234 |  | 
 | 235 | static int sx150x_gpio_get(struct gpio_chip *gc, unsigned offset) | 
 | 236 | { | 
 | 237 | 	struct sx150x_chip *chip; | 
 | 238 | 	int status = -EINVAL; | 
 | 239 |  | 
 | 240 | 	chip = container_of(gc, struct sx150x_chip, gpio_chip); | 
 | 241 |  | 
 | 242 | 	if (!offset_is_oscio(chip, offset)) { | 
 | 243 | 		mutex_lock(&chip->lock); | 
 | 244 | 		status = sx150x_get_io(chip, offset); | 
 | 245 | 		mutex_unlock(&chip->lock); | 
 | 246 | 	} | 
 | 247 |  | 
 | 248 | 	return status; | 
 | 249 | } | 
 | 250 |  | 
 | 251 | static void sx150x_gpio_set(struct gpio_chip *gc, unsigned offset, int val) | 
 | 252 | { | 
 | 253 | 	struct sx150x_chip *chip; | 
 | 254 |  | 
 | 255 | 	chip = container_of(gc, struct sx150x_chip, gpio_chip); | 
 | 256 |  | 
 | 257 | 	mutex_lock(&chip->lock); | 
 | 258 | 	if (offset_is_oscio(chip, offset)) | 
 | 259 | 		sx150x_set_oscio(chip, val); | 
 | 260 | 	else | 
 | 261 | 		sx150x_set_io(chip, offset, val); | 
 | 262 | 	mutex_unlock(&chip->lock); | 
 | 263 | } | 
 | 264 |  | 
 | 265 | static int sx150x_gpio_direction_input(struct gpio_chip *gc, unsigned offset) | 
 | 266 | { | 
 | 267 | 	struct sx150x_chip *chip; | 
 | 268 | 	int status = -EINVAL; | 
 | 269 |  | 
 | 270 | 	chip = container_of(gc, struct sx150x_chip, gpio_chip); | 
 | 271 |  | 
 | 272 | 	if (!offset_is_oscio(chip, offset)) { | 
 | 273 | 		mutex_lock(&chip->lock); | 
 | 274 | 		status = sx150x_io_input(chip, offset); | 
 | 275 | 		mutex_unlock(&chip->lock); | 
 | 276 | 	} | 
 | 277 | 	return status; | 
 | 278 | } | 
 | 279 |  | 
 | 280 | static int sx150x_gpio_direction_output(struct gpio_chip *gc, | 
 | 281 | 					unsigned offset, | 
 | 282 | 					int val) | 
 | 283 | { | 
 | 284 | 	struct sx150x_chip *chip; | 
 | 285 | 	int status = 0; | 
 | 286 |  | 
 | 287 | 	chip = container_of(gc, struct sx150x_chip, gpio_chip); | 
 | 288 |  | 
 | 289 | 	if (!offset_is_oscio(chip, offset)) { | 
 | 290 | 		mutex_lock(&chip->lock); | 
 | 291 | 		status = sx150x_io_output(chip, offset, val); | 
 | 292 | 		mutex_unlock(&chip->lock); | 
 | 293 | 	} | 
 | 294 | 	return status; | 
 | 295 | } | 
 | 296 |  | 
 | 297 | static int sx150x_gpio_to_irq(struct gpio_chip *gc, unsigned offset) | 
 | 298 | { | 
 | 299 | 	struct sx150x_chip *chip; | 
 | 300 |  | 
 | 301 | 	chip = container_of(gc, struct sx150x_chip, gpio_chip); | 
 | 302 |  | 
 | 303 | 	if (offset >= chip->dev_cfg->ngpios) | 
 | 304 | 		return -EINVAL; | 
 | 305 |  | 
 | 306 | 	if (chip->irq_base < 0) | 
 | 307 | 		return -EINVAL; | 
 | 308 |  | 
 | 309 | 	return chip->irq_base + offset; | 
 | 310 | } | 
 | 311 |  | 
| Lennert Buytenhek | 673860c | 2011-01-12 17:00:18 -0800 | [diff] [blame] | 312 | static void sx150x_irq_mask(struct irq_data *d) | 
| Gregory Bean | c34f16b | 2010-08-10 18:02:27 -0700 | [diff] [blame] | 313 | { | 
| Axel Lin | a3b9308 | 2012-09-04 22:06:19 +0800 | [diff] [blame] | 314 | 	struct sx150x_chip *chip = irq_data_get_irq_chip_data(d); | 
| Gregory Bean | c34f16b | 2010-08-10 18:02:27 -0700 | [diff] [blame] | 315 | 	unsigned n; | 
 | 316 |  | 
| Lennert Buytenhek | 673860c | 2011-01-12 17:00:18 -0800 | [diff] [blame] | 317 | 	n = d->irq - chip->irq_base; | 
| Thomas Gleixner | 0ff56cd | 2011-02-14 18:08:51 +0100 | [diff] [blame] | 318 | 	chip->irq_masked |= (1 << n); | 
 | 319 | 	chip->irq_update = n; | 
| Gregory Bean | c34f16b | 2010-08-10 18:02:27 -0700 | [diff] [blame] | 320 | } | 
 | 321 |  | 
| Lennert Buytenhek | 673860c | 2011-01-12 17:00:18 -0800 | [diff] [blame] | 322 | static void sx150x_irq_unmask(struct irq_data *d) | 
| Gregory Bean | c34f16b | 2010-08-10 18:02:27 -0700 | [diff] [blame] | 323 | { | 
| Axel Lin | a3b9308 | 2012-09-04 22:06:19 +0800 | [diff] [blame] | 324 | 	struct sx150x_chip *chip = irq_data_get_irq_chip_data(d); | 
| Gregory Bean | c34f16b | 2010-08-10 18:02:27 -0700 | [diff] [blame] | 325 | 	unsigned n; | 
 | 326 |  | 
| Lennert Buytenhek | 673860c | 2011-01-12 17:00:18 -0800 | [diff] [blame] | 327 | 	n = d->irq - chip->irq_base; | 
| Thomas Gleixner | 0ff56cd | 2011-02-14 18:08:51 +0100 | [diff] [blame] | 328 | 	chip->irq_masked &= ~(1 << n); | 
 | 329 | 	chip->irq_update = n; | 
| Gregory Bean | c34f16b | 2010-08-10 18:02:27 -0700 | [diff] [blame] | 330 | } | 
 | 331 |  | 
| Lennert Buytenhek | 673860c | 2011-01-12 17:00:18 -0800 | [diff] [blame] | 332 | static int sx150x_irq_set_type(struct irq_data *d, unsigned int flow_type) | 
| Gregory Bean | c34f16b | 2010-08-10 18:02:27 -0700 | [diff] [blame] | 333 | { | 
| Axel Lin | a3b9308 | 2012-09-04 22:06:19 +0800 | [diff] [blame] | 334 | 	struct sx150x_chip *chip = irq_data_get_irq_chip_data(d); | 
| Gregory Bean | c34f16b | 2010-08-10 18:02:27 -0700 | [diff] [blame] | 335 | 	unsigned n, val = 0; | 
 | 336 |  | 
 | 337 | 	if (flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) | 
 | 338 | 		return -EINVAL; | 
 | 339 |  | 
| Lennert Buytenhek | 673860c | 2011-01-12 17:00:18 -0800 | [diff] [blame] | 340 | 	n = d->irq - chip->irq_base; | 
| Gregory Bean | c34f16b | 2010-08-10 18:02:27 -0700 | [diff] [blame] | 341 |  | 
 | 342 | 	if (flow_type & IRQ_TYPE_EDGE_RISING) | 
 | 343 | 		val |= 0x1; | 
 | 344 | 	if (flow_type & IRQ_TYPE_EDGE_FALLING) | 
 | 345 | 		val |= 0x2; | 
 | 346 |  | 
 | 347 | 	chip->irq_sense &= ~(3UL << (n * 2)); | 
 | 348 | 	chip->irq_sense |= val << (n * 2); | 
| Thomas Gleixner | 0ff56cd | 2011-02-14 18:08:51 +0100 | [diff] [blame] | 349 | 	chip->irq_update = n; | 
| Gregory Bean | c34f16b | 2010-08-10 18:02:27 -0700 | [diff] [blame] | 350 | 	return 0; | 
 | 351 | } | 
 | 352 |  | 
 | 353 | static irqreturn_t sx150x_irq_thread_fn(int irq, void *dev_id) | 
 | 354 | { | 
 | 355 | 	struct sx150x_chip *chip = (struct sx150x_chip *)dev_id; | 
 | 356 | 	unsigned nhandled = 0; | 
 | 357 | 	unsigned sub_irq; | 
 | 358 | 	unsigned n; | 
 | 359 | 	s32 err; | 
 | 360 | 	u8 val; | 
 | 361 | 	int i; | 
 | 362 |  | 
 | 363 | 	for (i = (chip->dev_cfg->ngpios / 8) - 1; i >= 0; --i) { | 
 | 364 | 		err = sx150x_i2c_read(chip->client, | 
 | 365 | 				      chip->dev_cfg->reg_irq_src - i, | 
 | 366 | 				      &val); | 
 | 367 | 		if (err < 0) | 
 | 368 | 			continue; | 
 | 369 |  | 
 | 370 | 		sx150x_i2c_write(chip->client, | 
 | 371 | 				chip->dev_cfg->reg_irq_src - i, | 
 | 372 | 				val); | 
 | 373 | 		for (n = 0; n < 8; ++n) { | 
 | 374 | 			if (val & (1 << n)) { | 
 | 375 | 				sub_irq = chip->irq_base + (i * 8) + n; | 
 | 376 | 				handle_nested_irq(sub_irq); | 
 | 377 | 				++nhandled; | 
 | 378 | 			} | 
 | 379 | 		} | 
 | 380 | 	} | 
 | 381 |  | 
 | 382 | 	return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE); | 
 | 383 | } | 
 | 384 |  | 
| Lennert Buytenhek | 673860c | 2011-01-12 17:00:18 -0800 | [diff] [blame] | 385 | static void sx150x_irq_bus_lock(struct irq_data *d) | 
| Gregory Bean | c34f16b | 2010-08-10 18:02:27 -0700 | [diff] [blame] | 386 | { | 
| Axel Lin | a3b9308 | 2012-09-04 22:06:19 +0800 | [diff] [blame] | 387 | 	struct sx150x_chip *chip = irq_data_get_irq_chip_data(d); | 
| Gregory Bean | c34f16b | 2010-08-10 18:02:27 -0700 | [diff] [blame] | 388 |  | 
 | 389 | 	mutex_lock(&chip->lock); | 
 | 390 | } | 
 | 391 |  | 
| Lennert Buytenhek | 673860c | 2011-01-12 17:00:18 -0800 | [diff] [blame] | 392 | static void sx150x_irq_bus_sync_unlock(struct irq_data *d) | 
| Gregory Bean | c34f16b | 2010-08-10 18:02:27 -0700 | [diff] [blame] | 393 | { | 
| Axel Lin | a3b9308 | 2012-09-04 22:06:19 +0800 | [diff] [blame] | 394 | 	struct sx150x_chip *chip = irq_data_get_irq_chip_data(d); | 
| Gregory Bean | c34f16b | 2010-08-10 18:02:27 -0700 | [diff] [blame] | 395 | 	unsigned n; | 
 | 396 |  | 
| Thomas Gleixner | 0ff56cd | 2011-02-14 18:08:51 +0100 | [diff] [blame] | 397 | 	if (chip->irq_update == NO_UPDATE_PENDING) | 
 | 398 | 		goto out; | 
| Gregory Bean | c34f16b | 2010-08-10 18:02:27 -0700 | [diff] [blame] | 399 |  | 
| Thomas Gleixner | 0ff56cd | 2011-02-14 18:08:51 +0100 | [diff] [blame] | 400 | 	n = chip->irq_update; | 
 | 401 | 	chip->irq_update = NO_UPDATE_PENDING; | 
 | 402 |  | 
 | 403 | 	/* Avoid updates if nothing changed */ | 
 | 404 | 	if (chip->dev_sense == chip->irq_sense && | 
 | 405 | 	    chip->dev_sense == chip->irq_masked) | 
 | 406 | 		goto out; | 
 | 407 |  | 
 | 408 | 	chip->dev_sense = chip->irq_sense; | 
 | 409 | 	chip->dev_masked = chip->irq_masked; | 
 | 410 |  | 
 | 411 | 	if (chip->irq_masked & (1 << n)) { | 
 | 412 | 		sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 1); | 
 | 413 | 		sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense, 0); | 
 | 414 | 	} else { | 
 | 415 | 		sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 0); | 
 | 416 | 		sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense, | 
 | 417 | 				 chip->irq_sense >> (n * 2)); | 
 | 418 | 	} | 
 | 419 | out: | 
| Gregory Bean | c34f16b | 2010-08-10 18:02:27 -0700 | [diff] [blame] | 420 | 	mutex_unlock(&chip->lock); | 
 | 421 | } | 
 | 422 |  | 
 | 423 | static void sx150x_init_chip(struct sx150x_chip *chip, | 
 | 424 | 			struct i2c_client *client, | 
 | 425 | 			kernel_ulong_t driver_data, | 
 | 426 | 			struct sx150x_platform_data *pdata) | 
 | 427 | { | 
 | 428 | 	mutex_init(&chip->lock); | 
 | 429 |  | 
 | 430 | 	chip->client                     = client; | 
 | 431 | 	chip->dev_cfg                    = &sx150x_devices[driver_data]; | 
 | 432 | 	chip->gpio_chip.label            = client->name; | 
 | 433 | 	chip->gpio_chip.direction_input  = sx150x_gpio_direction_input; | 
 | 434 | 	chip->gpio_chip.direction_output = sx150x_gpio_direction_output; | 
 | 435 | 	chip->gpio_chip.get              = sx150x_gpio_get; | 
 | 436 | 	chip->gpio_chip.set              = sx150x_gpio_set; | 
 | 437 | 	chip->gpio_chip.to_irq           = sx150x_gpio_to_irq; | 
 | 438 | 	chip->gpio_chip.base             = pdata->gpio_base; | 
 | 439 | 	chip->gpio_chip.can_sleep        = 1; | 
 | 440 | 	chip->gpio_chip.ngpio            = chip->dev_cfg->ngpios; | 
 | 441 | 	if (pdata->oscio_is_gpo) | 
 | 442 | 		++chip->gpio_chip.ngpio; | 
 | 443 |  | 
| Lennert Buytenhek | 673860c | 2011-01-12 17:00:18 -0800 | [diff] [blame] | 444 | 	chip->irq_chip.name                = client->name; | 
 | 445 | 	chip->irq_chip.irq_mask            = sx150x_irq_mask; | 
 | 446 | 	chip->irq_chip.irq_unmask          = sx150x_irq_unmask; | 
 | 447 | 	chip->irq_chip.irq_set_type        = sx150x_irq_set_type; | 
 | 448 | 	chip->irq_chip.irq_bus_lock        = sx150x_irq_bus_lock; | 
 | 449 | 	chip->irq_chip.irq_bus_sync_unlock = sx150x_irq_bus_sync_unlock; | 
 | 450 | 	chip->irq_summary                  = -1; | 
 | 451 | 	chip->irq_base                     = -1; | 
| Thomas Gleixner | 0ff56cd | 2011-02-14 18:08:51 +0100 | [diff] [blame] | 452 | 	chip->irq_masked                   = ~0; | 
| Lennert Buytenhek | 673860c | 2011-01-12 17:00:18 -0800 | [diff] [blame] | 453 | 	chip->irq_sense                    = 0; | 
| Thomas Gleixner | 0ff56cd | 2011-02-14 18:08:51 +0100 | [diff] [blame] | 454 | 	chip->dev_masked                   = ~0; | 
 | 455 | 	chip->dev_sense                    = 0; | 
 | 456 | 	chip->irq_update		   = NO_UPDATE_PENDING; | 
| Gregory Bean | c34f16b | 2010-08-10 18:02:27 -0700 | [diff] [blame] | 457 | } | 
 | 458 |  | 
 | 459 | static int sx150x_init_io(struct sx150x_chip *chip, u8 base, u16 cfg) | 
 | 460 | { | 
 | 461 | 	int err = 0; | 
 | 462 | 	unsigned n; | 
 | 463 |  | 
 | 464 | 	for (n = 0; err >= 0 && n < (chip->dev_cfg->ngpios / 8); ++n) | 
 | 465 | 		err = sx150x_i2c_write(chip->client, base - n, cfg >> (n * 8)); | 
 | 466 | 	return err; | 
 | 467 | } | 
 | 468 |  | 
| Gregory Bean | 5affb60 | 2010-09-09 16:38:02 -0700 | [diff] [blame] | 469 | static int sx150x_reset(struct sx150x_chip *chip) | 
 | 470 | { | 
 | 471 | 	int err; | 
 | 472 |  | 
 | 473 | 	err = i2c_smbus_write_byte_data(chip->client, | 
 | 474 | 					chip->dev_cfg->reg_reset, | 
 | 475 | 					0x12); | 
 | 476 | 	if (err < 0) | 
 | 477 | 		return err; | 
 | 478 |  | 
 | 479 | 	err = i2c_smbus_write_byte_data(chip->client, | 
 | 480 | 					chip->dev_cfg->reg_reset, | 
 | 481 | 					0x34); | 
 | 482 | 	return err; | 
 | 483 | } | 
 | 484 |  | 
| Gregory Bean | c34f16b | 2010-08-10 18:02:27 -0700 | [diff] [blame] | 485 | static int sx150x_init_hw(struct sx150x_chip *chip, | 
 | 486 | 			struct sx150x_platform_data *pdata) | 
 | 487 | { | 
 | 488 | 	int err = 0; | 
 | 489 |  | 
| Gregory Bean | 5affb60 | 2010-09-09 16:38:02 -0700 | [diff] [blame] | 490 | 	if (pdata->reset_during_probe) { | 
 | 491 | 		err = sx150x_reset(chip); | 
 | 492 | 		if (err < 0) | 
 | 493 | 			return err; | 
 | 494 | 	} | 
| Gregory Bean | c34f16b | 2010-08-10 18:02:27 -0700 | [diff] [blame] | 495 |  | 
 | 496 | 	err = sx150x_i2c_write(chip->client, | 
 | 497 | 			chip->dev_cfg->reg_misc, | 
 | 498 | 			0x01); | 
 | 499 | 	if (err < 0) | 
 | 500 | 		return err; | 
 | 501 |  | 
 | 502 | 	err = sx150x_init_io(chip, chip->dev_cfg->reg_pullup, | 
 | 503 | 			pdata->io_pullup_ena); | 
 | 504 | 	if (err < 0) | 
 | 505 | 		return err; | 
 | 506 |  | 
 | 507 | 	err = sx150x_init_io(chip, chip->dev_cfg->reg_pulldn, | 
 | 508 | 			pdata->io_pulldn_ena); | 
 | 509 | 	if (err < 0) | 
 | 510 | 		return err; | 
 | 511 |  | 
 | 512 | 	err = sx150x_init_io(chip, chip->dev_cfg->reg_drain, | 
 | 513 | 			pdata->io_open_drain_ena); | 
 | 514 | 	if (err < 0) | 
 | 515 | 		return err; | 
 | 516 |  | 
 | 517 | 	err = sx150x_init_io(chip, chip->dev_cfg->reg_polarity, | 
 | 518 | 			pdata->io_polarity); | 
 | 519 | 	if (err < 0) | 
 | 520 | 		return err; | 
 | 521 |  | 
 | 522 | 	if (pdata->oscio_is_gpo) | 
 | 523 | 		sx150x_set_oscio(chip, 0); | 
 | 524 |  | 
 | 525 | 	return err; | 
 | 526 | } | 
 | 527 |  | 
 | 528 | static int sx150x_install_irq_chip(struct sx150x_chip *chip, | 
 | 529 | 				int irq_summary, | 
 | 530 | 				int irq_base) | 
 | 531 | { | 
 | 532 | 	int err; | 
 | 533 | 	unsigned n; | 
 | 534 | 	unsigned irq; | 
 | 535 |  | 
 | 536 | 	chip->irq_summary = irq_summary; | 
 | 537 | 	chip->irq_base    = irq_base; | 
 | 538 |  | 
 | 539 | 	for (n = 0; n < chip->dev_cfg->ngpios; ++n) { | 
 | 540 | 		irq = irq_base + n; | 
| Axel Lin | a3b9308 | 2012-09-04 22:06:19 +0800 | [diff] [blame] | 541 | 		irq_set_chip_data(irq, chip); | 
| Thomas Gleixner | b51804b | 2011-03-24 21:27:36 +0000 | [diff] [blame] | 542 | 		irq_set_chip_and_handler(irq, &chip->irq_chip, handle_edge_irq); | 
 | 543 | 		irq_set_nested_thread(irq, 1); | 
| Gregory Bean | c34f16b | 2010-08-10 18:02:27 -0700 | [diff] [blame] | 544 | #ifdef CONFIG_ARM | 
 | 545 | 		set_irq_flags(irq, IRQF_VALID); | 
 | 546 | #else | 
| Thomas Gleixner | b51804b | 2011-03-24 21:27:36 +0000 | [diff] [blame] | 547 | 		irq_set_noprobe(irq); | 
| Gregory Bean | c34f16b | 2010-08-10 18:02:27 -0700 | [diff] [blame] | 548 | #endif | 
 | 549 | 	} | 
 | 550 |  | 
 | 551 | 	err = request_threaded_irq(irq_summary, | 
 | 552 | 				NULL, | 
 | 553 | 				sx150x_irq_thread_fn, | 
 | 554 | 				IRQF_SHARED | IRQF_TRIGGER_FALLING, | 
 | 555 | 				chip->irq_chip.name, | 
 | 556 | 				chip); | 
 | 557 | 	if (err < 0) { | 
 | 558 | 		chip->irq_summary = -1; | 
 | 559 | 		chip->irq_base    = -1; | 
 | 560 | 	} | 
 | 561 |  | 
 | 562 | 	return err; | 
 | 563 | } | 
 | 564 |  | 
 | 565 | static void sx150x_remove_irq_chip(struct sx150x_chip *chip) | 
 | 566 | { | 
 | 567 | 	unsigned n; | 
 | 568 | 	unsigned irq; | 
 | 569 |  | 
 | 570 | 	free_irq(chip->irq_summary, chip); | 
 | 571 |  | 
 | 572 | 	for (n = 0; n < chip->dev_cfg->ngpios; ++n) { | 
 | 573 | 		irq = chip->irq_base + n; | 
| Thomas Gleixner | 08f1b80 | 2011-03-24 21:27:37 +0000 | [diff] [blame] | 574 | 		irq_set_chip_and_handler(irq, NULL, NULL); | 
| Gregory Bean | c34f16b | 2010-08-10 18:02:27 -0700 | [diff] [blame] | 575 | 	} | 
 | 576 | } | 
 | 577 |  | 
| Bill Pemberton | 3836309 | 2012-11-19 13:22:34 -0500 | [diff] [blame] | 578 | static int sx150x_probe(struct i2c_client *client, | 
| Gregory Bean | c34f16b | 2010-08-10 18:02:27 -0700 | [diff] [blame] | 579 | 				const struct i2c_device_id *id) | 
 | 580 | { | 
 | 581 | 	static const u32 i2c_funcs = I2C_FUNC_SMBUS_BYTE_DATA | | 
 | 582 | 				     I2C_FUNC_SMBUS_WRITE_WORD_DATA; | 
 | 583 | 	struct sx150x_platform_data *pdata; | 
 | 584 | 	struct sx150x_chip *chip; | 
 | 585 | 	int rc; | 
 | 586 |  | 
 | 587 | 	pdata = client->dev.platform_data; | 
 | 588 | 	if (!pdata) | 
 | 589 | 		return -EINVAL; | 
 | 590 |  | 
 | 591 | 	if (!i2c_check_functionality(client->adapter, i2c_funcs)) | 
 | 592 | 		return -ENOSYS; | 
 | 593 |  | 
 | 594 | 	chip = kzalloc(sizeof(struct sx150x_chip), GFP_KERNEL); | 
 | 595 | 	if (!chip) | 
 | 596 | 		return -ENOMEM; | 
 | 597 |  | 
 | 598 | 	sx150x_init_chip(chip, client, id->driver_data, pdata); | 
 | 599 | 	rc = sx150x_init_hw(chip, pdata); | 
 | 600 | 	if (rc < 0) | 
 | 601 | 		goto probe_fail_pre_gpiochip_add; | 
 | 602 |  | 
 | 603 | 	rc = gpiochip_add(&chip->gpio_chip); | 
 | 604 | 	if (rc < 0) | 
 | 605 | 		goto probe_fail_pre_gpiochip_add; | 
 | 606 |  | 
 | 607 | 	if (pdata->irq_summary >= 0) { | 
 | 608 | 		rc = sx150x_install_irq_chip(chip, | 
 | 609 | 					pdata->irq_summary, | 
 | 610 | 					pdata->irq_base); | 
 | 611 | 		if (rc < 0) | 
 | 612 | 			goto probe_fail_post_gpiochip_add; | 
 | 613 | 	} | 
 | 614 |  | 
 | 615 | 	i2c_set_clientdata(client, chip); | 
 | 616 |  | 
 | 617 | 	return 0; | 
 | 618 | probe_fail_post_gpiochip_add: | 
 | 619 | 	WARN_ON(gpiochip_remove(&chip->gpio_chip) < 0); | 
 | 620 | probe_fail_pre_gpiochip_add: | 
 | 621 | 	kfree(chip); | 
 | 622 | 	return rc; | 
 | 623 | } | 
 | 624 |  | 
| Bill Pemberton | 206210c | 2012-11-19 13:25:50 -0500 | [diff] [blame] | 625 | static int sx150x_remove(struct i2c_client *client) | 
| Gregory Bean | c34f16b | 2010-08-10 18:02:27 -0700 | [diff] [blame] | 626 | { | 
 | 627 | 	struct sx150x_chip *chip; | 
 | 628 | 	int rc; | 
 | 629 |  | 
 | 630 | 	chip = i2c_get_clientdata(client); | 
 | 631 | 	rc = gpiochip_remove(&chip->gpio_chip); | 
 | 632 | 	if (rc < 0) | 
 | 633 | 		return rc; | 
 | 634 |  | 
 | 635 | 	if (chip->irq_summary >= 0) | 
 | 636 | 		sx150x_remove_irq_chip(chip); | 
 | 637 |  | 
 | 638 | 	kfree(chip); | 
 | 639 |  | 
 | 640 | 	return 0; | 
 | 641 | } | 
 | 642 |  | 
 | 643 | static struct i2c_driver sx150x_driver = { | 
 | 644 | 	.driver = { | 
 | 645 | 		.name = "sx150x", | 
 | 646 | 		.owner = THIS_MODULE | 
 | 647 | 	}, | 
 | 648 | 	.probe    = sx150x_probe, | 
| Bill Pemberton | 8283c4f | 2012-11-19 13:20:08 -0500 | [diff] [blame] | 649 | 	.remove   = sx150x_remove, | 
| Gregory Bean | c34f16b | 2010-08-10 18:02:27 -0700 | [diff] [blame] | 650 | 	.id_table = sx150x_id, | 
 | 651 | }; | 
 | 652 |  | 
 | 653 | static int __init sx150x_init(void) | 
 | 654 | { | 
 | 655 | 	return i2c_add_driver(&sx150x_driver); | 
 | 656 | } | 
 | 657 | subsys_initcall(sx150x_init); | 
 | 658 |  | 
 | 659 | static void __exit sx150x_exit(void) | 
 | 660 | { | 
 | 661 | 	return i2c_del_driver(&sx150x_driver); | 
 | 662 | } | 
 | 663 | module_exit(sx150x_exit); | 
 | 664 |  | 
 | 665 | MODULE_AUTHOR("Gregory Bean <gbean@codeaurora.org>"); | 
 | 666 | MODULE_DESCRIPTION("Driver for Semtech SX150X I2C GPIO Expanders"); | 
 | 667 | MODULE_LICENSE("GPL v2"); | 
 | 668 | MODULE_ALIAS("i2c:sx150x"); |