| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 1 | /* | 
| Guennadi Liakhovetski | f5e8ff4 | 2008-02-06 01:39:04 -0800 | [diff] [blame] | 2 | *  pca953x.c - 4/8/16 bit I/O ports | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 3 | * | 
|  | 4 | *  Copyright (C) 2005 Ben Gardner <bgardner@wabtec.com> | 
|  | 5 | *  Copyright (C) 2007 Marvell International Ltd. | 
|  | 6 | * | 
|  | 7 | *  Derived from drivers/i2c/chips/pca9539.c | 
|  | 8 | * | 
|  | 9 | *  This program is free software; you can redistribute it and/or modify | 
|  | 10 | *  it under the terms of the GNU General Public License as published by | 
|  | 11 | *  the Free Software Foundation; version 2 of the License. | 
|  | 12 | */ | 
|  | 13 |  | 
|  | 14 | #include <linux/module.h> | 
|  | 15 | #include <linux/init.h> | 
|  | 16 | #include <linux/i2c.h> | 
| Guennadi Liakhovetski | d1c057e3 | 2008-02-06 01:39:02 -0800 | [diff] [blame] | 17 | #include <linux/i2c/pca953x.h> | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 18 |  | 
|  | 19 | #include <asm/gpio.h> | 
|  | 20 |  | 
| Guennadi Liakhovetski | f5e8ff4 | 2008-02-06 01:39:04 -0800 | [diff] [blame] | 21 | #define PCA953X_INPUT          0 | 
|  | 22 | #define PCA953X_OUTPUT         1 | 
|  | 23 | #define PCA953X_INVERT         2 | 
|  | 24 | #define PCA953X_DIRECTION      3 | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 25 |  | 
| Jean Delvare | 3760f73 | 2008-04-29 23:11:40 +0200 | [diff] [blame] | 26 | static const struct i2c_device_id pca953x_id[] = { | 
| Guennadi Liakhovetski | f5e8ff4 | 2008-02-06 01:39:04 -0800 | [diff] [blame] | 27 | { "pca9534", 8, }, | 
|  | 28 | { "pca9535", 16, }, | 
|  | 29 | { "pca9536", 4, }, | 
|  | 30 | { "pca9537", 4, }, | 
|  | 31 | { "pca9538", 8, }, | 
|  | 32 | { "pca9539", 16, }, | 
| David Brownell | 69292b3 | 2008-05-23 13:04:42 -0700 | [diff] [blame] | 33 | { "pca9554", 8, }, | 
| Will Newton | f39e578 | 2008-05-01 04:35:10 -0700 | [diff] [blame] | 34 | { "pca9555", 16, }, | 
|  | 35 | { "pca9557", 8, }, | 
| David Brownell | ab5dc37 | 2009-01-06 14:42:27 -0800 | [diff] [blame] | 36 |  | 
| David Brownell | 7059d4b | 2008-07-04 09:59:37 -0700 | [diff] [blame] | 37 | { "max7310", 8, }, | 
| David Brownell | ab5dc37 | 2009-01-06 14:42:27 -0800 | [diff] [blame] | 38 | { "pca6107", 8, }, | 
|  | 39 | { "tca6408", 8, }, | 
|  | 40 | { "tca6416", 16, }, | 
|  | 41 | /* NYET:  { "tca6424", 24, }, */ | 
| Jean Delvare | 3760f73 | 2008-04-29 23:11:40 +0200 | [diff] [blame] | 42 | { } | 
| Guennadi Liakhovetski | f5e8ff4 | 2008-02-06 01:39:04 -0800 | [diff] [blame] | 43 | }; | 
| Jean Delvare | 3760f73 | 2008-04-29 23:11:40 +0200 | [diff] [blame] | 44 | MODULE_DEVICE_TABLE(i2c, pca953x_id); | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 45 |  | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 46 | struct pca953x_chip { | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 47 | unsigned gpio_start; | 
|  | 48 | uint16_t reg_output; | 
|  | 49 | uint16_t reg_direction; | 
|  | 50 |  | 
|  | 51 | struct i2c_client *client; | 
|  | 52 | struct gpio_chip gpio_chip; | 
|  | 53 | }; | 
|  | 54 |  | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 55 | static int pca953x_write_reg(struct pca953x_chip *chip, int reg, uint16_t val) | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 56 | { | 
| Guennadi Liakhovetski | f5e8ff4 | 2008-02-06 01:39:04 -0800 | [diff] [blame] | 57 | int ret; | 
|  | 58 |  | 
|  | 59 | if (chip->gpio_chip.ngpio <= 8) | 
|  | 60 | ret = i2c_smbus_write_byte_data(chip->client, reg, val); | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 61 | else | 
| Guennadi Liakhovetski | f5e8ff4 | 2008-02-06 01:39:04 -0800 | [diff] [blame] | 62 | ret = i2c_smbus_write_word_data(chip->client, reg << 1, val); | 
|  | 63 |  | 
|  | 64 | if (ret < 0) { | 
|  | 65 | dev_err(&chip->client->dev, "failed writing register\n"); | 
| David Brownell | ab5dc37 | 2009-01-06 14:42:27 -0800 | [diff] [blame] | 66 | return ret; | 
| Guennadi Liakhovetski | f5e8ff4 | 2008-02-06 01:39:04 -0800 | [diff] [blame] | 67 | } | 
|  | 68 |  | 
|  | 69 | return 0; | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 70 | } | 
|  | 71 |  | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 72 | static int pca953x_read_reg(struct pca953x_chip *chip, int reg, uint16_t *val) | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 73 | { | 
|  | 74 | int ret; | 
|  | 75 |  | 
| Guennadi Liakhovetski | f5e8ff4 | 2008-02-06 01:39:04 -0800 | [diff] [blame] | 76 | if (chip->gpio_chip.ngpio <= 8) | 
|  | 77 | ret = i2c_smbus_read_byte_data(chip->client, reg); | 
|  | 78 | else | 
|  | 79 | ret = i2c_smbus_read_word_data(chip->client, reg << 1); | 
|  | 80 |  | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 81 | if (ret < 0) { | 
|  | 82 | dev_err(&chip->client->dev, "failed reading register\n"); | 
| David Brownell | ab5dc37 | 2009-01-06 14:42:27 -0800 | [diff] [blame] | 83 | return ret; | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 84 | } | 
|  | 85 |  | 
|  | 86 | *val = (uint16_t)ret; | 
|  | 87 | return 0; | 
|  | 88 | } | 
|  | 89 |  | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 90 | static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off) | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 91 | { | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 92 | struct pca953x_chip *chip; | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 93 | uint16_t reg_val; | 
|  | 94 | int ret; | 
|  | 95 |  | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 96 | chip = container_of(gc, struct pca953x_chip, gpio_chip); | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 97 |  | 
|  | 98 | reg_val = chip->reg_direction | (1u << off); | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 99 | ret = pca953x_write_reg(chip, PCA953X_DIRECTION, reg_val); | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 100 | if (ret) | 
|  | 101 | return ret; | 
|  | 102 |  | 
|  | 103 | chip->reg_direction = reg_val; | 
|  | 104 | return 0; | 
|  | 105 | } | 
|  | 106 |  | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 107 | static int pca953x_gpio_direction_output(struct gpio_chip *gc, | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 108 | unsigned off, int val) | 
|  | 109 | { | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 110 | struct pca953x_chip *chip; | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 111 | uint16_t reg_val; | 
|  | 112 | int ret; | 
|  | 113 |  | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 114 | chip = container_of(gc, struct pca953x_chip, gpio_chip); | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 115 |  | 
|  | 116 | /* set output level */ | 
|  | 117 | if (val) | 
|  | 118 | reg_val = chip->reg_output | (1u << off); | 
|  | 119 | else | 
|  | 120 | reg_val = chip->reg_output & ~(1u << off); | 
|  | 121 |  | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 122 | ret = pca953x_write_reg(chip, PCA953X_OUTPUT, reg_val); | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 123 | if (ret) | 
|  | 124 | return ret; | 
|  | 125 |  | 
|  | 126 | chip->reg_output = reg_val; | 
|  | 127 |  | 
|  | 128 | /* then direction */ | 
|  | 129 | reg_val = chip->reg_direction & ~(1u << off); | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 130 | ret = pca953x_write_reg(chip, PCA953X_DIRECTION, reg_val); | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 131 | if (ret) | 
|  | 132 | return ret; | 
|  | 133 |  | 
|  | 134 | chip->reg_direction = reg_val; | 
|  | 135 | return 0; | 
|  | 136 | } | 
|  | 137 |  | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 138 | static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off) | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 139 | { | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 140 | struct pca953x_chip *chip; | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 141 | uint16_t reg_val; | 
|  | 142 | int ret; | 
|  | 143 |  | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 144 | chip = container_of(gc, struct pca953x_chip, gpio_chip); | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 145 |  | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 146 | ret = pca953x_read_reg(chip, PCA953X_INPUT, ®_val); | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 147 | if (ret < 0) { | 
|  | 148 | /* NOTE:  diagnostic already emitted; that's all we should | 
|  | 149 | * do unless gpio_*_value_cansleep() calls become different | 
|  | 150 | * from their nonsleeping siblings (and report faults). | 
|  | 151 | */ | 
|  | 152 | return 0; | 
|  | 153 | } | 
|  | 154 |  | 
|  | 155 | return (reg_val & (1u << off)) ? 1 : 0; | 
|  | 156 | } | 
|  | 157 |  | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 158 | static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val) | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 159 | { | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 160 | struct pca953x_chip *chip; | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 161 | uint16_t reg_val; | 
|  | 162 | int ret; | 
|  | 163 |  | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 164 | chip = container_of(gc, struct pca953x_chip, gpio_chip); | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 165 |  | 
|  | 166 | if (val) | 
|  | 167 | reg_val = chip->reg_output | (1u << off); | 
|  | 168 | else | 
|  | 169 | reg_val = chip->reg_output & ~(1u << off); | 
|  | 170 |  | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 171 | ret = pca953x_write_reg(chip, PCA953X_OUTPUT, reg_val); | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 172 | if (ret) | 
|  | 173 | return; | 
|  | 174 |  | 
|  | 175 | chip->reg_output = reg_val; | 
|  | 176 | } | 
|  | 177 |  | 
| Guennadi Liakhovetski | f5e8ff4 | 2008-02-06 01:39:04 -0800 | [diff] [blame] | 178 | static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios) | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 179 | { | 
|  | 180 | struct gpio_chip *gc; | 
|  | 181 |  | 
|  | 182 | gc = &chip->gpio_chip; | 
|  | 183 |  | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 184 | gc->direction_input  = pca953x_gpio_direction_input; | 
|  | 185 | gc->direction_output = pca953x_gpio_direction_output; | 
|  | 186 | gc->get = pca953x_gpio_get_value; | 
|  | 187 | gc->set = pca953x_gpio_set_value; | 
| Arnaud Patard | 8420780 | 2008-03-10 11:43:48 -0700 | [diff] [blame] | 188 | gc->can_sleep = 1; | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 189 |  | 
|  | 190 | gc->base = chip->gpio_start; | 
| Guennadi Liakhovetski | f5e8ff4 | 2008-02-06 01:39:04 -0800 | [diff] [blame] | 191 | gc->ngpio = gpios; | 
|  | 192 | gc->label = chip->client->name; | 
| David Brownell | d8f388d | 2008-07-25 01:46:07 -0700 | [diff] [blame] | 193 | gc->dev = &chip->client->dev; | 
| Guennadi Liakhovetski | d72cbed | 2008-04-28 02:14:45 -0700 | [diff] [blame] | 194 | gc->owner = THIS_MODULE; | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 195 | } | 
|  | 196 |  | 
| Jean Delvare | d2653e9 | 2008-04-29 23:11:39 +0200 | [diff] [blame] | 197 | static int __devinit pca953x_probe(struct i2c_client *client, | 
| Jean Delvare | 3760f73 | 2008-04-29 23:11:40 +0200 | [diff] [blame] | 198 | const struct i2c_device_id *id) | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 199 | { | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 200 | struct pca953x_platform_data *pdata; | 
|  | 201 | struct pca953x_chip *chip; | 
| Will Newton | f39e578 | 2008-05-01 04:35:10 -0700 | [diff] [blame] | 202 | int ret; | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 203 |  | 
|  | 204 | pdata = client->dev.platform_data; | 
| Ben Dooks | a342d21 | 2009-01-15 13:50:45 -0800 | [diff] [blame] | 205 | if (pdata == NULL) { | 
|  | 206 | dev_dbg(&client->dev, "no platform data\n"); | 
|  | 207 | return -EINVAL; | 
|  | 208 | } | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 209 |  | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 210 | chip = kzalloc(sizeof(struct pca953x_chip), GFP_KERNEL); | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 211 | if (chip == NULL) | 
|  | 212 | return -ENOMEM; | 
|  | 213 |  | 
|  | 214 | chip->client = client; | 
|  | 215 |  | 
|  | 216 | chip->gpio_start = pdata->gpio_base; | 
|  | 217 |  | 
|  | 218 | /* initialize cached registers from their original values. | 
|  | 219 | * we can't share this chip with another i2c master. | 
|  | 220 | */ | 
| Guennadi Liakhovetski | f5e8ff4 | 2008-02-06 01:39:04 -0800 | [diff] [blame] | 221 | pca953x_setup_gpio(chip, id->driver_data); | 
|  | 222 |  | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 223 | ret = pca953x_read_reg(chip, PCA953X_OUTPUT, &chip->reg_output); | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 224 | if (ret) | 
|  | 225 | goto out_failed; | 
|  | 226 |  | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 227 | ret = pca953x_read_reg(chip, PCA953X_DIRECTION, &chip->reg_direction); | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 228 | if (ret) | 
|  | 229 | goto out_failed; | 
|  | 230 |  | 
|  | 231 | /* set platform specific polarity inversion */ | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 232 | ret = pca953x_write_reg(chip, PCA953X_INVERT, pdata->invert); | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 233 | if (ret) | 
|  | 234 | goto out_failed; | 
|  | 235 |  | 
| Guennadi Liakhovetski | f5e8ff4 | 2008-02-06 01:39:04 -0800 | [diff] [blame] | 236 |  | 
|  | 237 | ret = gpiochip_add(&chip->gpio_chip); | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 238 | if (ret) | 
|  | 239 | goto out_failed; | 
|  | 240 |  | 
|  | 241 | if (pdata->setup) { | 
|  | 242 | ret = pdata->setup(client, chip->gpio_chip.base, | 
|  | 243 | chip->gpio_chip.ngpio, pdata->context); | 
|  | 244 | if (ret < 0) | 
|  | 245 | dev_warn(&client->dev, "setup failed, %d\n", ret); | 
|  | 246 | } | 
|  | 247 |  | 
|  | 248 | i2c_set_clientdata(client, chip); | 
|  | 249 | return 0; | 
|  | 250 |  | 
|  | 251 | out_failed: | 
|  | 252 | kfree(chip); | 
|  | 253 | return ret; | 
|  | 254 | } | 
|  | 255 |  | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 256 | static int pca953x_remove(struct i2c_client *client) | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 257 | { | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 258 | struct pca953x_platform_data *pdata = client->dev.platform_data; | 
|  | 259 | struct pca953x_chip *chip = i2c_get_clientdata(client); | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 260 | int ret = 0; | 
|  | 261 |  | 
|  | 262 | if (pdata->teardown) { | 
|  | 263 | ret = pdata->teardown(client, chip->gpio_chip.base, | 
|  | 264 | chip->gpio_chip.ngpio, pdata->context); | 
|  | 265 | if (ret < 0) { | 
|  | 266 | dev_err(&client->dev, "%s failed, %d\n", | 
|  | 267 | "teardown", ret); | 
|  | 268 | return ret; | 
|  | 269 | } | 
|  | 270 | } | 
|  | 271 |  | 
|  | 272 | ret = gpiochip_remove(&chip->gpio_chip); | 
|  | 273 | if (ret) { | 
|  | 274 | dev_err(&client->dev, "%s failed, %d\n", | 
|  | 275 | "gpiochip_remove()", ret); | 
|  | 276 | return ret; | 
|  | 277 | } | 
|  | 278 |  | 
|  | 279 | kfree(chip); | 
|  | 280 | return 0; | 
|  | 281 | } | 
|  | 282 |  | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 283 | static struct i2c_driver pca953x_driver = { | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 284 | .driver = { | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 285 | .name	= "pca953x", | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 286 | }, | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 287 | .probe		= pca953x_probe, | 
|  | 288 | .remove		= pca953x_remove, | 
| Jean Delvare | 3760f73 | 2008-04-29 23:11:40 +0200 | [diff] [blame] | 289 | .id_table	= pca953x_id, | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 290 | }; | 
|  | 291 |  | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 292 | static int __init pca953x_init(void) | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 293 | { | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 294 | return i2c_add_driver(&pca953x_driver); | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 295 | } | 
| David Brownell | 2f8d119 | 2008-10-15 22:03:13 -0700 | [diff] [blame] | 296 | /* register after i2c postcore initcall and before | 
|  | 297 | * subsys initcalls that may rely on these GPIOs | 
|  | 298 | */ | 
|  | 299 | subsys_initcall(pca953x_init); | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 300 |  | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 301 | static void __exit pca953x_exit(void) | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 302 | { | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 303 | i2c_del_driver(&pca953x_driver); | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 304 | } | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 305 | module_exit(pca953x_exit); | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 306 |  | 
|  | 307 | MODULE_AUTHOR("eric miao <eric.miao@marvell.com>"); | 
| Guennadi Liakhovetski | f3dc363 | 2008-02-06 01:39:03 -0800 | [diff] [blame] | 308 | MODULE_DESCRIPTION("GPIO expander driver for PCA953x"); | 
| eric miao | 9e60fdc | 2008-02-04 22:28:26 -0800 | [diff] [blame] | 309 | MODULE_LICENSE("GPL"); |