Archived
14
0
Fork 0
This repository has been archived on 2022-02-17. You can view files and clone it, but cannot push or open issues or pull requests.
linux-2.6/drivers/input/keyboard/adp5588-keys.c
Wolfram Sang fbae3fb154 i2c: Remove all i2c_set_clientdata(client, NULL) in drivers
I2C drivers can use the clientdata-pointer to point to private data. As I2C
devices are not really unregistered, but merely detached from their driver, it
used to be the drivers obligation to clear this pointer during remove() or a
failed probe(). As a couple of drivers forgot to do this, it was agreed that it
was cleaner if the i2c-core does this clearance when appropriate, as there is
no guarantee for the lifetime of the clientdata-pointer after remove() anyhow.
This feature was added to the core with commit
e4a7b9b04d to fix the faulty drivers.

As there is no need anymore to clear the clientdata-pointer, remove all current
occurrences in the drivers to simplify the code and prevent confusion.

Signed-off-by: Wolfram Sang <w.sang@pengutronix.de>
Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
Acked-by: Richard Purdie <rpurdie@linux.intel.com>
Acked-by: Dmitry Torokhov <dtor@mail.ru>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
2010-06-03 11:33:58 +02:00

364 lines
8.4 KiB
C

/*
* File: drivers/input/keyboard/adp5588_keys.c
* Description: keypad driver for ADP5588 and ADP5587
* I2C QWERTY Keypad and IO Expander
* Bugs: Enter bugs at http://blackfin.uclinux.org/
*
* Copyright (C) 2008-2009 Analog Devices Inc.
* Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/workqueue.h>
#include <linux/errno.h>
#include <linux/pm.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/i2c/adp5588.h>
/* Configuration Register1 */
#define AUTO_INC (1 << 7)
#define GPIEM_CFG (1 << 6)
#define OVR_FLOW_M (1 << 5)
#define INT_CFG (1 << 4)
#define OVR_FLOW_IEN (1 << 3)
#define K_LCK_IM (1 << 2)
#define GPI_IEN (1 << 1)
#define KE_IEN (1 << 0)
/* Interrupt Status Register */
#define CMP2_INT (1 << 5)
#define CMP1_INT (1 << 4)
#define OVR_FLOW_INT (1 << 3)
#define K_LCK_INT (1 << 2)
#define GPI_INT (1 << 1)
#define KE_INT (1 << 0)
/* Key Lock and Event Counter Register */
#define K_LCK_EN (1 << 6)
#define LCK21 0x30
#define KEC 0xF
/* Key Event Register xy */
#define KEY_EV_PRESSED (1 << 7)
#define KEY_EV_MASK (0x7F)
#define KP_SEL(x) (0xFFFF >> (16 - x)) /* 2^x-1 */
#define KEYP_MAX_EVENT 10
/*
* Early pre 4.0 Silicon required to delay readout by at least 25ms,
* since the Event Counter Register updated 25ms after the interrupt
* asserted.
*/
#define WA_DELAYED_READOUT_REVID(rev) ((rev) < 4)
struct adp5588_kpad {
struct i2c_client *client;
struct input_dev *input;
struct delayed_work work;
unsigned long delay;
unsigned short keycode[ADP5588_KEYMAPSIZE];
};
static int adp5588_read(struct i2c_client *client, u8 reg)
{
int ret = i2c_smbus_read_byte_data(client, reg);
if (ret < 0)
dev_err(&client->dev, "Read Error\n");
return ret;
}
static int adp5588_write(struct i2c_client *client, u8 reg, u8 val)
{
return i2c_smbus_write_byte_data(client, reg, val);
}
static void adp5588_work(struct work_struct *work)
{
struct adp5588_kpad *kpad = container_of(work,
struct adp5588_kpad, work.work);
struct i2c_client *client = kpad->client;
int i, key, status, ev_cnt;
status = adp5588_read(client, INT_STAT);
if (status & OVR_FLOW_INT) /* Unlikely and should never happen */
dev_err(&client->dev, "Event Overflow Error\n");
if (status & KE_INT) {
ev_cnt = adp5588_read(client, KEY_LCK_EC_STAT) & KEC;
if (ev_cnt) {
for (i = 0; i < ev_cnt; i++) {
key = adp5588_read(client, Key_EVENTA + i);
input_report_key(kpad->input,
kpad->keycode[(key & KEY_EV_MASK) - 1],
key & KEY_EV_PRESSED);
}
input_sync(kpad->input);
}
}
adp5588_write(client, INT_STAT, status); /* Status is W1C */
}
static irqreturn_t adp5588_irq(int irq, void *handle)
{
struct adp5588_kpad *kpad = handle;
/*
* use keventd context to read the event fifo registers
* Schedule readout at least 25ms after notification for
* REVID < 4
*/
schedule_delayed_work(&kpad->work, kpad->delay);
return IRQ_HANDLED;
}
static int __devinit adp5588_setup(struct i2c_client *client)
{
struct adp5588_kpad_platform_data *pdata = client->dev.platform_data;
int i, ret;
ret = adp5588_write(client, KP_GPIO1, KP_SEL(pdata->rows));
ret |= adp5588_write(client, KP_GPIO2, KP_SEL(pdata->cols) & 0xFF);
ret |= adp5588_write(client, KP_GPIO3, KP_SEL(pdata->cols) >> 8);
if (pdata->en_keylock) {
ret |= adp5588_write(client, UNLOCK1, pdata->unlock_key1);
ret |= adp5588_write(client, UNLOCK2, pdata->unlock_key2);
ret |= adp5588_write(client, KEY_LCK_EC_STAT, K_LCK_EN);
}
for (i = 0; i < KEYP_MAX_EVENT; i++)
ret |= adp5588_read(client, Key_EVENTA);
ret |= adp5588_write(client, INT_STAT, CMP2_INT | CMP1_INT |
OVR_FLOW_INT | K_LCK_INT |
GPI_INT | KE_INT); /* Status is W1C */
ret |= adp5588_write(client, CFG, INT_CFG | OVR_FLOW_IEN | KE_IEN);
if (ret < 0) {
dev_err(&client->dev, "Write Error\n");
return ret;
}
return 0;
}
static int __devinit adp5588_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct adp5588_kpad *kpad;
struct adp5588_kpad_platform_data *pdata = client->dev.platform_data;
struct input_dev *input;
unsigned int revid;
int ret, i;
int error;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
return -EIO;
}
if (!pdata) {
dev_err(&client->dev, "no platform data?\n");
return -EINVAL;
}
if (!pdata->rows || !pdata->cols || !pdata->keymap) {
dev_err(&client->dev, "no rows, cols or keymap from pdata\n");
return -EINVAL;
}
if (pdata->keymapsize != ADP5588_KEYMAPSIZE) {
dev_err(&client->dev, "invalid keymapsize\n");
return -EINVAL;
}
if (!client->irq) {
dev_err(&client->dev, "no IRQ?\n");
return -EINVAL;
}
kpad = kzalloc(sizeof(*kpad), GFP_KERNEL);
input = input_allocate_device();
if (!kpad || !input) {
error = -ENOMEM;
goto err_free_mem;
}
kpad->client = client;
kpad->input = input;
INIT_DELAYED_WORK(&kpad->work, adp5588_work);
ret = adp5588_read(client, DEV_ID);
if (ret < 0) {
error = ret;
goto err_free_mem;
}
revid = (u8) ret & ADP5588_DEVICE_ID_MASK;
if (WA_DELAYED_READOUT_REVID(revid))
kpad->delay = msecs_to_jiffies(30);
input->name = client->name;
input->phys = "adp5588-keys/input0";
input->dev.parent = &client->dev;
input_set_drvdata(input, kpad);
input->id.bustype = BUS_I2C;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = revid;
input->keycodesize = sizeof(kpad->keycode[0]);
input->keycodemax = pdata->keymapsize;
input->keycode = kpad->keycode;
memcpy(kpad->keycode, pdata->keymap,
pdata->keymapsize * input->keycodesize);
/* setup input device */
__set_bit(EV_KEY, input->evbit);
if (pdata->repeat)
__set_bit(EV_REP, input->evbit);
for (i = 0; i < input->keycodemax; i++)
__set_bit(kpad->keycode[i] & KEY_MAX, input->keybit);
__clear_bit(KEY_RESERVED, input->keybit);
error = input_register_device(input);
if (error) {
dev_err(&client->dev, "unable to register input device\n");
goto err_free_mem;
}
error = request_irq(client->irq, adp5588_irq,
IRQF_TRIGGER_FALLING | IRQF_DISABLED,
client->dev.driver->name, kpad);
if (error) {
dev_err(&client->dev, "irq %d busy?\n", client->irq);
goto err_unreg_dev;
}
error = adp5588_setup(client);
if (error)
goto err_free_irq;
device_init_wakeup(&client->dev, 1);
i2c_set_clientdata(client, kpad);
dev_info(&client->dev, "Rev.%d keypad, irq %d\n", revid, client->irq);
return 0;
err_free_irq:
free_irq(client->irq, kpad);
err_unreg_dev:
input_unregister_device(input);
input = NULL;
err_free_mem:
input_free_device(input);
kfree(kpad);
return error;
}
static int __devexit adp5588_remove(struct i2c_client *client)
{
struct adp5588_kpad *kpad = i2c_get_clientdata(client);
adp5588_write(client, CFG, 0);
free_irq(client->irq, kpad);
cancel_delayed_work_sync(&kpad->work);
input_unregister_device(kpad->input);
kfree(kpad);
return 0;
}
#ifdef CONFIG_PM
static int adp5588_suspend(struct device *dev)
{
struct adp5588_kpad *kpad = dev_get_drvdata(dev);
struct i2c_client *client = kpad->client;
disable_irq(client->irq);
cancel_delayed_work_sync(&kpad->work);
if (device_may_wakeup(&client->dev))
enable_irq_wake(client->irq);
return 0;
}
static int adp5588_resume(struct device *dev)
{
struct adp5588_kpad *kpad = dev_get_drvdata(dev);
struct i2c_client *client = kpad->client;
if (device_may_wakeup(&client->dev))
disable_irq_wake(client->irq);
enable_irq(client->irq);
return 0;
}
static const struct dev_pm_ops adp5588_dev_pm_ops = {
.suspend = adp5588_suspend,
.resume = adp5588_resume,
};
#endif
static const struct i2c_device_id adp5588_id[] = {
{ KBUILD_MODNAME, 0 },
{ "adp5587-keys", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adp5588_id);
static struct i2c_driver adp5588_driver = {
.driver = {
.name = KBUILD_MODNAME,
#ifdef CONFIG_PM
.pm = &adp5588_dev_pm_ops,
#endif
},
.probe = adp5588_probe,
.remove = __devexit_p(adp5588_remove),
.id_table = adp5588_id,
};
static int __init adp5588_init(void)
{
return i2c_add_driver(&adp5588_driver);
}
module_init(adp5588_init);
static void __exit adp5588_exit(void)
{
i2c_del_driver(&adp5588_driver);
}
module_exit(adp5588_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
MODULE_DESCRIPTION("ADP5588/87 Keypad driver");
MODULE_ALIAS("platform:adp5588-keys");