gpio-twl4030.c 15.0 KB
Newer Older
D
David Brownell 已提交
1
/*
G
Grant Likely 已提交
2
 * Access to GPIOs on TWL4030/TPS659x0 chips
D
David Brownell 已提交
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
 *
 * Copyright (C) 2006-2007 Texas Instruments, Inc.
 * Copyright (C) 2006 MontaVista Software, Inc.
 *
 * Code re-arranged and cleaned up by:
 *	Syed Mohammed Khasim <x0khasim@ti.com>
 *
 * Initial Code:
 *	Andy Lowe / Nishanth Menon
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kthread.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
35 36
#include <linux/of.h>
#include <linux/irqdomain.h>
D
David Brownell 已提交
37

38
#include <linux/i2c/twl.h>
D
David Brownell 已提交
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65

/*
 * The GPIO "subchip" supports 18 GPIOs which can be configured as
 * inputs or outputs, with pullups or pulldowns on each pin.  Each
 * GPIO can trigger interrupts on either or both edges.
 *
 * GPIO interrupts can be fed to either of two IRQ lines; this is
 * intended to support multiple hosts.
 *
 * There are also two LED pins used sometimes as output-only GPIOs.
 */

/* genirq interfaces are not available to modules */
#ifdef MODULE
#define is_module()	true
#else
#define is_module()	false
#endif

/* GPIO_CTRL Fields */
#define MASK_GPIO_CTRL_GPIO0CD1		BIT(0)
#define MASK_GPIO_CTRL_GPIO1CD2		BIT(1)
#define MASK_GPIO_CTRL_GPIO_ON		BIT(2)

/* Mask for GPIO registers when aggregated into a 32-bit integer */
#define GPIO_32_MASK			0x0003ffff

66 67
struct gpio_twl4030_priv {
	struct gpio_chip gpio_chip;
68
	struct mutex mutex;
69 70
	int irq_base;

71
	/* Bitfields for state caching */
72
	unsigned int usage_count;
73 74
	unsigned int direction;
	unsigned int out_state;
75
};
D
David Brownell 已提交
76 77 78

/*----------------------------------------------------------------------*/

79 80 81 82 83
static inline struct gpio_twl4030_priv *to_gpio_twl4030(struct gpio_chip *chip)
{
	return container_of(chip, struct gpio_twl4030_priv, gpio_chip);
}

D
David Brownell 已提交
84 85 86 87 88
/*
 * To configure TWL4030 GPIO module registers
 */
static inline int gpio_twl4030_write(u8 address, u8 data)
{
B
Balaji T K 已提交
89
	return twl_i2c_write_u8(TWL4030_MODULE_GPIO, data, address);
D
David Brownell 已提交
90 91 92 93 94
}

/*----------------------------------------------------------------------*/

/*
95
 * LED register offsets from TWL_MODULE_LED base
D
David Brownell 已提交
96 97 98
 * PWMs A and B are dedicated to LEDs A and B, respectively.
 */

99 100 101 102 103
#define TWL4030_LED_LEDEN_REG	0x00
#define TWL4030_PWMAON_REG	0x01
#define TWL4030_PWMAOFF_REG	0x02
#define TWL4030_PWMBON_REG	0x03
#define TWL4030_PWMBOFF_REG	0x04
D
David Brownell 已提交
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126

/* LEDEN bits */
#define LEDEN_LEDAON		BIT(0)
#define LEDEN_LEDBON		BIT(1)
#define LEDEN_LEDAEXT		BIT(2)
#define LEDEN_LEDBEXT		BIT(3)
#define LEDEN_LEDAPWM		BIT(4)
#define LEDEN_LEDBPWM		BIT(5)
#define LEDEN_PWM_LENGTHA	BIT(6)
#define LEDEN_PWM_LENGTHB	BIT(7)

#define PWMxON_LENGTH		BIT(7)

/*----------------------------------------------------------------------*/

/*
 * To read a TWL4030 GPIO module register
 */
static inline int gpio_twl4030_read(u8 address)
{
	u8 data;
	int ret = 0;

B
Balaji T K 已提交
127
	ret = twl_i2c_read_u8(TWL4030_MODULE_GPIO, &data, address);
D
David Brownell 已提交
128 129 130 131 132
	return (ret < 0) ? ret : data;
}

/*----------------------------------------------------------------------*/

133
static u8 cached_leden;
D
David Brownell 已提交
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150

/* The LED lines are open drain outputs ... a FET pulls to GND, so an
 * external pullup is needed.  We could also expose the integrated PWM
 * as a LED brightness control; we initialize it as "always on".
 */
static void twl4030_led_set_value(int led, int value)
{
	u8 mask = LEDEN_LEDAON | LEDEN_LEDAPWM;
	int status;

	if (led)
		mask <<= 1;

	if (value)
		cached_leden &= ~mask;
	else
		cached_leden |= mask;
B
Balaji T K 已提交
151
	status = twl_i2c_write_u8(TWL4030_MODULE_LED, cached_leden,
152
				  TWL4030_LED_LEDEN_REG);
D
David Brownell 已提交
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
}

static int twl4030_set_gpio_direction(int gpio, int is_input)
{
	u8 d_bnk = gpio >> 3;
	u8 d_msk = BIT(gpio & 0x7);
	u8 reg = 0;
	u8 base = REG_GPIODATADIR1 + d_bnk;
	int ret = 0;

	ret = gpio_twl4030_read(base);
	if (ret >= 0) {
		if (is_input)
			reg = ret & ~d_msk;
		else
			reg = ret | d_msk;

		ret = gpio_twl4030_write(base, reg);
	}
	return ret;
}

static int twl4030_set_gpio_dataout(int gpio, int enable)
{
	u8 d_bnk = gpio >> 3;
	u8 d_msk = BIT(gpio & 0x7);
	u8 base = 0;

	if (enable)
		base = REG_SETGPIODATAOUT1 + d_bnk;
	else
		base = REG_CLEARGPIODATAOUT1 + d_bnk;

	return gpio_twl4030_write(base, d_msk);
}

static int twl4030_get_gpio_datain(int gpio)
{
	u8 d_bnk = gpio >> 3;
	u8 d_off = gpio & 0x7;
	u8 base = 0;
	int ret = 0;

	base = REG_GPIODATAIN1 + d_bnk;
	ret = gpio_twl4030_read(base);
	if (ret > 0)
		ret = (ret >> d_off) & 0x1;

	return ret;
}

/*----------------------------------------------------------------------*/

static int twl_request(struct gpio_chip *chip, unsigned offset)
{
208
	struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip);
D
David Brownell 已提交
209 210
	int status = 0;

211
	mutex_lock(&priv->mutex);
D
David Brownell 已提交
212 213 214 215 216

	/* Support the two LED outputs as output-only GPIOs. */
	if (offset >= TWL4030_GPIO_MAX) {
		u8	ledclr_mask = LEDEN_LEDAON | LEDEN_LEDAEXT
				| LEDEN_LEDAPWM | LEDEN_PWM_LENGTHA;
217
		u8	reg = TWL4030_PWMAON_REG;
D
David Brownell 已提交
218 219 220 221

		offset -= TWL4030_GPIO_MAX;
		if (offset) {
			ledclr_mask <<= 1;
222
			reg = TWL4030_PWMBON_REG;
D
David Brownell 已提交
223 224 225
		}

		/* initialize PWM to always-drive */
226 227
		/* Configure PWM OFF register first */
		status = twl_i2c_write_u8(TWL4030_MODULE_LED, 0x7f, reg + 1);
D
David Brownell 已提交
228 229
		if (status < 0)
			goto done;
230 231 232

		/* Followed by PWM ON register */
		status = twl_i2c_write_u8(TWL4030_MODULE_LED, 0x7f, reg);
D
David Brownell 已提交
233 234 235 236
		if (status < 0)
			goto done;

		/* init LED to not-driven (high) */
237 238
		status = twl_i2c_read_u8(TWL4030_MODULE_LED, &cached_leden,
					 TWL4030_LED_LEDEN_REG);
D
David Brownell 已提交
239 240 241
		if (status < 0)
			goto done;
		cached_leden &= ~ledclr_mask;
242 243
		status = twl_i2c_write_u8(TWL4030_MODULE_LED, cached_leden,
					  TWL4030_LED_LEDEN_REG);
D
David Brownell 已提交
244 245 246 247 248 249 250 251
		if (status < 0)
			goto done;

		status = 0;
		goto done;
	}

	/* on first use, turn GPIO module "on" */
252
	if (!priv->usage_count) {
D
David Brownell 已提交
253 254 255 256 257 258
		struct twl4030_gpio_platform_data *pdata;
		u8 value = MASK_GPIO_CTRL_GPIO_ON;

		/* optionally have the first two GPIOs switch vMMC1
		 * and vMMC2 power supplies based on card presence.
		 */
J
Jingoo Han 已提交
259
		pdata = dev_get_platdata(chip->dev);
260 261
		if (pdata)
			value |= pdata->mmc_cd & 0x03;
D
David Brownell 已提交
262 263 264 265

		status = gpio_twl4030_write(REG_GPIO_CTRL, value);
	}

266
done:
D
David Brownell 已提交
267
	if (!status)
268
		priv->usage_count |= BIT(offset);
D
David Brownell 已提交
269

270
	mutex_unlock(&priv->mutex);
D
David Brownell 已提交
271 272 273 274 275
	return status;
}

static void twl_free(struct gpio_chip *chip, unsigned offset)
{
276 277
	struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip);

278
	mutex_lock(&priv->mutex);
D
David Brownell 已提交
279 280
	if (offset >= TWL4030_GPIO_MAX) {
		twl4030_led_set_value(offset - TWL4030_GPIO_MAX, 1);
281
		goto out;
D
David Brownell 已提交
282 283
	}

284
	priv->usage_count &= ~BIT(offset);
D
David Brownell 已提交
285 286

	/* on last use, switch off GPIO module */
287
	if (!priv->usage_count)
D
David Brownell 已提交
288 289
		gpio_twl4030_write(REG_GPIO_CTRL, 0x0);

290 291
out:
	mutex_unlock(&priv->mutex);
D
David Brownell 已提交
292 293 294 295
}

static int twl_direction_in(struct gpio_chip *chip, unsigned offset)
{
296 297 298 299 300 301 302
	struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip);
	int ret;

	mutex_lock(&priv->mutex);
	if (offset < TWL4030_GPIO_MAX)
		ret = twl4030_set_gpio_direction(offset, 1);
	else
303
		ret = -EINVAL;	/* LED outputs can't be set as input */
304 305 306 307 308 309 310

	if (!ret)
		priv->direction &= ~BIT(offset);

	mutex_unlock(&priv->mutex);

	return ret;
D
David Brownell 已提交
311 312 313 314
}

static int twl_get(struct gpio_chip *chip, unsigned offset)
{
315
	struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip);
316
	int ret;
D
David Brownell 已提交
317 318
	int status = 0;

319 320 321 322 323
	mutex_lock(&priv->mutex);
	if (!(priv->usage_count & BIT(offset))) {
		ret = -EPERM;
		goto out;
	}
324

325 326
	if (priv->direction & BIT(offset))
		status = priv->out_state & BIT(offset);
D
David Brownell 已提交
327
	else
328
		status = twl4030_get_gpio_datain(offset);
329

330 331 332 333
	ret = (status <= 0) ? 0 : 1;
out:
	mutex_unlock(&priv->mutex);
	return ret;
D
David Brownell 已提交
334 335
}

336
static void twl_set(struct gpio_chip *chip, unsigned offset, int value)
D
David Brownell 已提交
337
{
338 339 340 341
	struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip);

	mutex_lock(&priv->mutex);
	if (offset < TWL4030_GPIO_MAX)
D
David Brownell 已提交
342
		twl4030_set_gpio_dataout(offset, value);
343
	else
D
David Brownell 已提交
344
		twl4030_led_set_value(offset - TWL4030_GPIO_MAX, value);
345 346 347 348 349 350 351

	if (value)
		priv->out_state |= BIT(offset);
	else
		priv->out_state &= ~BIT(offset);

	mutex_unlock(&priv->mutex);
D
David Brownell 已提交
352 353
}

354
static int twl_direction_out(struct gpio_chip *chip, unsigned offset, int value)
D
David Brownell 已提交
355
{
356
	struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip);
357
	int ret = 0;
358 359

	mutex_lock(&priv->mutex);
360
	if (offset < TWL4030_GPIO_MAX) {
361
		ret = twl4030_set_gpio_direction(offset, 0);
362 363 364 365 366 367 368 369 370
		if (ret) {
			mutex_unlock(&priv->mutex);
			return ret;
		}
	}

	/*
	 *  LED gpios i.e. offset >= TWL4030_GPIO_MAX are always output
	 */
371 372 373 374 375 376

	priv->direction |= BIT(offset);
	mutex_unlock(&priv->mutex);

	twl_set(chip, offset, value);

377
	return ret;
D
David Brownell 已提交
378 379 380 381
}

static int twl_to_irq(struct gpio_chip *chip, unsigned offset)
{
382 383 384 385
	struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip);

	return (priv->irq_base && (offset < TWL4030_GPIO_MAX))
		? (priv->irq_base + offset)
D
David Brownell 已提交
386 387 388
		: -EINVAL;
}

389
static struct gpio_chip template_chip = {
D
David Brownell 已提交
390 391 392 393 394 395 396 397 398 399 400 401 402 403
	.label			= "twl4030",
	.owner			= THIS_MODULE,
	.request		= twl_request,
	.free			= twl_free,
	.direction_input	= twl_direction_in,
	.get			= twl_get,
	.direction_output	= twl_direction_out,
	.set			= twl_set,
	.to_irq			= twl_to_irq,
	.can_sleep		= 1,
};

/*----------------------------------------------------------------------*/

B
Bill Pemberton 已提交
404
static int gpio_twl4030_pulls(u32 ups, u32 downs)
D
David Brownell 已提交
405
{
406
	u8		message[5];
D
David Brownell 已提交
407 408 409 410 411
	unsigned	i, gpio_bit;

	/* For most pins, a pulldown was enabled by default.
	 * We should have data that's specific to this board.
	 */
412
	for (gpio_bit = 1, i = 0; i < 5; i++) {
D
David Brownell 已提交
413 414 415 416 417 418 419 420 421 422 423 424
		u8		bit_mask;
		unsigned	j;

		for (bit_mask = 0, j = 0; j < 8; j += 2, gpio_bit <<= 1) {
			if (ups & gpio_bit)
				bit_mask |= 1 << (j + 1);
			else if (downs & gpio_bit)
				bit_mask |= 1 << (j + 0);
		}
		message[i] = bit_mask;
	}

B
Balaji T K 已提交
425
	return twl_i2c_write(TWL4030_MODULE_GPIO, message,
D
David Brownell 已提交
426 427 428
				REG_GPIOPUPDCTR1, 5);
}

B
Bill Pemberton 已提交
429
static int gpio_twl4030_debounce(u32 debounce, u8 mmc_cd)
D
David Brownell 已提交
430
{
431
	u8		message[3];
D
David Brownell 已提交
432 433 434 435

	/* 30 msec of debouncing is always used for MMC card detect,
	 * and is optional for everything else.
	 */
436
	message[0] = (debounce & 0xff) | (mmc_cd & 0x03);
D
David Brownell 已提交
437
	debounce >>= 8;
438
	message[1] = (debounce & 0xff);
D
David Brownell 已提交
439
	debounce >>= 8;
440
	message[2] = (debounce & 0x03);
D
David Brownell 已提交
441

B
Balaji T K 已提交
442
	return twl_i2c_write(TWL4030_MODULE_GPIO, message,
D
David Brownell 已提交
443 444 445
				REG_GPIO_DEBEN1, 3);
}

D
David Brownell 已提交
446 447
static int gpio_twl4030_remove(struct platform_device *pdev);

448 449
static struct twl4030_gpio_platform_data *of_gpio_twl4030(struct device *dev,
				struct twl4030_gpio_platform_data *pdata)
450 451 452 453 454 455 456
{
	struct twl4030_gpio_platform_data *omap_twl_info;

	omap_twl_info = devm_kzalloc(dev, sizeof(*omap_twl_info), GFP_KERNEL);
	if (!omap_twl_info)
		return NULL;

457 458 459
	if (pdata)
		*omap_twl_info = *pdata;

460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
	omap_twl_info->use_leds = of_property_read_bool(dev->of_node,
			"ti,use-leds");

	of_property_read_u32(dev->of_node, "ti,debounce",
			     &omap_twl_info->debounce);
	of_property_read_u32(dev->of_node, "ti,mmc-cd",
			     (u32 *)&omap_twl_info->mmc_cd);
	of_property_read_u32(dev->of_node, "ti,pullups",
			     &omap_twl_info->pullups);
	of_property_read_u32(dev->of_node, "ti,pulldowns",
			     &omap_twl_info->pulldowns);

	return omap_twl_info;
}

B
Bill Pemberton 已提交
475
static int gpio_twl4030_probe(struct platform_device *pdev)
D
David Brownell 已提交
476
{
J
Jingoo Han 已提交
477
	struct twl4030_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev);
478
	struct device_node *node = pdev->dev.of_node;
479
	struct gpio_twl4030_priv *priv;
480
	int ret, irq_base;
D
David Brownell 已提交
481

482 483 484 485 486
	priv = devm_kzalloc(&pdev->dev, sizeof(struct gpio_twl4030_priv),
			    GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

D
David Brownell 已提交
487
	/* maybe setup IRQs */
488 489 490 491 492 493 494 495 496
	if (is_module()) {
		dev_err(&pdev->dev, "can't dispatch IRQs from modules\n");
		goto no_irqs;
	}

	irq_base = irq_alloc_descs(-1, 0, TWL4030_GPIO_MAX, 0);
	if (irq_base < 0) {
		dev_err(&pdev->dev, "Failed to alloc irq_descs\n");
		return irq_base;
D
David Brownell 已提交
497 498
	}

499 500 501
	irq_domain_add_legacy(node, TWL4030_GPIO_MAX, irq_base, 0,
			      &irq_domain_simple_ops, NULL);

502 503 504 505
	ret = twl4030_sih_setup(&pdev->dev, TWL4030_MODULE_GPIO, irq_base);
	if (ret < 0)
		return ret;

506
	priv->irq_base = irq_base;
507

D
David Brownell 已提交
508
no_irqs:
509 510 511 512
	priv->gpio_chip = template_chip;
	priv->gpio_chip.base = -1;
	priv->gpio_chip.ngpio = TWL4030_GPIO_MAX;
	priv->gpio_chip.dev = &pdev->dev;
D
David Brownell 已提交
513

514 515
	mutex_init(&priv->mutex);

516
	if (node)
517
		pdata = of_gpio_twl4030(&pdev->dev, pdata);
518

519 520 521
	if (pdata == NULL) {
		dev_err(&pdev->dev, "Platform data is missing\n");
		return -ENXIO;
522
	}
D
David Brownell 已提交
523

524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544
	/*
	 * NOTE:  boards may waste power if they don't set pullups
	 * and pulldowns correctly ... default for non-ULPI pins is
	 * pulldown, and some other pins may have external pullups
	 * or pulldowns.  Careful!
	 */
	ret = gpio_twl4030_pulls(pdata->pullups, pdata->pulldowns);
	if (ret)
		dev_dbg(&pdev->dev, "pullups %.05x %.05x --> %d\n",
			pdata->pullups, pdata->pulldowns, ret);

	ret = gpio_twl4030_debounce(pdata->debounce, pdata->mmc_cd);
	if (ret)
		dev_dbg(&pdev->dev, "debounce %.03x %.01x --> %d\n",
			pdata->debounce, pdata->mmc_cd, ret);

	/*
	 * NOTE: we assume VIBRA_CTL.VIBRA_EN, in MODULE_AUDIO_VOICE,
	 * is (still) clear if use_leds is set.
	 */
	if (pdata->use_leds)
545
		priv->gpio_chip.ngpio += 2;
546

547
	ret = gpiochip_add(&priv->gpio_chip);
D
David Brownell 已提交
548
	if (ret < 0) {
549
		dev_err(&pdev->dev, "could not register gpiochip, %d\n", ret);
550
		priv->gpio_chip.ngpio = 0;
D
David Brownell 已提交
551
		gpio_twl4030_remove(pdev);
552 553 554
		goto out;
	}

555
	platform_set_drvdata(pdev, priv);
556 557

	if (pdata && pdata->setup) {
D
David Brownell 已提交
558 559
		int status;

560 561
		status = pdata->setup(&pdev->dev, priv->gpio_chip.base,
				      TWL4030_GPIO_MAX);
D
David Brownell 已提交
562 563 564 565
		if (status)
			dev_dbg(&pdev->dev, "setup --> %d\n", status);
	}

566
out:
D
David Brownell 已提交
567 568 569
	return ret;
}

B
Bill Pemberton 已提交
570
/* Cannot use as gpio_twl4030_probe() calls us */
571
static int gpio_twl4030_remove(struct platform_device *pdev)
D
David Brownell 已提交
572
{
J
Jingoo Han 已提交
573
	struct twl4030_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev);
574
	struct gpio_twl4030_priv *priv = platform_get_drvdata(pdev);
D
David Brownell 已提交
575 576
	int status;

577
	if (pdata && pdata->teardown) {
578 579
		status = pdata->teardown(&pdev->dev, priv->gpio_chip.base,
					 TWL4030_GPIO_MAX);
D
David Brownell 已提交
580 581 582 583 584 585
		if (status) {
			dev_dbg(&pdev->dev, "teardown --> %d\n", status);
			return status;
		}
	}

586
	status = gpiochip_remove(&priv->gpio_chip);
D
David Brownell 已提交
587 588 589 590 591 592 593 594 595 596 597
	if (status < 0)
		return status;

	if (is_module())
		return 0;

	/* REVISIT no support yet for deregistering all the IRQs */
	WARN_ON(1);
	return -EIO;
}

598 599 600 601 602 603
static const struct of_device_id twl_gpio_match[] = {
	{ .compatible = "ti,twl4030-gpio", },
	{ },
};
MODULE_DEVICE_TABLE(of, twl_gpio_match);

D
David Brownell 已提交
604 605 606 607
/* Note:  this hardware lives inside an I2C-based multi-function device. */
MODULE_ALIAS("platform:twl4030_gpio");

static struct platform_driver gpio_twl4030_driver = {
608 609 610
	.driver = {
		.name	= "twl4030_gpio",
		.owner	= THIS_MODULE,
611
		.of_match_table = twl_gpio_match,
612
	},
D
David Brownell 已提交
613
	.probe		= gpio_twl4030_probe,
614
	.remove		= gpio_twl4030_remove,
D
David Brownell 已提交
615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631
};

static int __init gpio_twl4030_init(void)
{
	return platform_driver_register(&gpio_twl4030_driver);
}
subsys_initcall(gpio_twl4030_init);

static void __exit gpio_twl4030_exit(void)
{
	platform_driver_unregister(&gpio_twl4030_driver);
}
module_exit(gpio_twl4030_exit);

MODULE_AUTHOR("Texas Instruments, Inc.");
MODULE_DESCRIPTION("GPIO interface for TWL4030");
MODULE_LICENSE("GPL");