Commit 99bb892d authored by Dmitry Torokhov's avatar Dmitry Torokhov

Input: tsc2005 - rework driver initialization code

We need to make sure we have time/work initialized before requesting and
enabling interrupts, otherwise we might start using them way too early.
Tested-by: default avatarAaro Koskinen <aaro.koskinen@nokia.com>
Signed-off-by: default avatarDmitry Torokhov <dtor@mail.ru>
parent f8a67139
...@@ -495,6 +495,16 @@ static void tsc2005_esd_work(struct work_struct *work) ...@@ -495,6 +495,16 @@ static void tsc2005_esd_work(struct work_struct *work)
mutex_unlock(&ts->mutex); mutex_unlock(&ts->mutex);
} }
static struct attribute *tsc2005_attrs[] = {
&dev_attr_disable.attr,
&dev_attr_selftest.attr,
NULL
};
static struct attribute_group tsc2005_attr_group = {
.attrs = tsc2005_attrs,
};
static void __devinit tsc2005_setup_spi_xfer(struct tsc2005 *ts) static void __devinit tsc2005_setup_spi_xfer(struct tsc2005 *ts)
{ {
tsc2005_setup_read(&ts->spi_x, TSC2005_REG_X, 0); tsc2005_setup_read(&ts->spi_x, TSC2005_REG_X, 0);
...@@ -509,144 +519,130 @@ static void __devinit tsc2005_setup_spi_xfer(struct tsc2005 *ts) ...@@ -509,144 +519,130 @@ static void __devinit tsc2005_setup_spi_xfer(struct tsc2005 *ts)
spi_message_add_tail(&ts->spi_z2.spi_xfer, &ts->spi_read_msg); spi_message_add_tail(&ts->spi_z2.spi_xfer, &ts->spi_read_msg);
} }
static struct attribute *tsc2005_attrs[] = { static int __devinit tsc2005_probe(struct spi_device *spi)
&dev_attr_disable.attr,
&dev_attr_selftest.attr,
NULL
};
static struct attribute_group tsc2005_attr_group = {
.attrs = tsc2005_attrs,
};
static int __devinit tsc2005_setup(struct tsc2005 *ts,
struct tsc2005_platform_data *pdata)
{ {
int r; const struct tsc2005_platform_data *pdata = spi->dev.platform_data;
int fudge_x; struct tsc2005 *ts;
int fudge_y; struct input_dev *input_dev;
int fudge_p; unsigned int max_x, max_y, max_p;
int p_max; unsigned int fudge_x, fudge_y, fudge_p;
int x_max; int error;
int y_max;
mutex_init(&ts->mutex);
tsc2005_setup_spi_xfer(ts);
init_timer(&ts->penup_timer); if (!pdata) {
setup_timer(&ts->penup_timer, tsc2005_penup_timer, (unsigned long)ts); dev_dbg(&spi->dev, "no platform data\n");
INIT_WORK(&ts->penup_work, tsc2005_penup_work); return -ENODEV;
}
fudge_x = pdata->ts_x_fudge ? : 4; fudge_x = pdata->ts_x_fudge ? : 4;
fudge_y = pdata->ts_y_fudge ? : 8; fudge_y = pdata->ts_y_fudge ? : 8;
fudge_p = pdata->ts_pressure_fudge ? : 2; fudge_p = pdata->ts_pressure_fudge ? : 2;
x_max = pdata->ts_x_max ? : MAX_12BIT; max_x = pdata->ts_x_max ? : MAX_12BIT;
y_max = pdata->ts_y_max ? : MAX_12BIT; max_y = pdata->ts_y_max ? : MAX_12BIT;
p_max = pdata->ts_pressure_max ? : MAX_12BIT; max_p = pdata->ts_pressure_max ? : MAX_12BIT;
ts->x_plate_ohm = pdata->ts_x_plate_ohm ? : 280;
ts->esd_timeout = pdata->esd_timeout_ms;
ts->set_reset = pdata->set_reset;
ts->idev = input_allocate_device(); if (spi->irq <= 0) {
if (ts->idev == NULL) dev_dbg(&spi->dev, "no irq\n");
return -ENOMEM; return -ENODEV;
ts->idev->name = "TSC2005 touchscreen";
snprintf(ts->phys, sizeof(ts->phys), "%s/input-ts",
dev_name(&ts->spi->dev));
ts->idev->phys = ts->phys;
ts->idev->id.bustype = BUS_SPI;
ts->idev->dev.parent = &ts->spi->dev;
ts->idev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
ts->idev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
ts->idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
input_set_abs_params(ts->idev, ABS_X, 0, x_max, fudge_x, 0);
input_set_abs_params(ts->idev, ABS_Y, 0, y_max, fudge_y, 0);
input_set_abs_params(ts->idev, ABS_PRESSURE, 0, p_max, fudge_p, 0);
r = request_threaded_irq(ts->spi->irq, tsc2005_irq_handler,
tsc2005_irq_thread, IRQF_TRIGGER_RISING,
"tsc2005", ts);
if (r) {
dev_err(&ts->spi->dev, "request_threaded_irq(): %d\n", r);
goto err1;
} }
set_irq_wake(ts->spi->irq, 1);
r = input_register_device(ts->idev); spi->mode = SPI_MODE_0;
if (r) { spi->bits_per_word = 8;
dev_err(&ts->spi->dev, "input_register_device(): %d\n", r); if (!spi->max_speed_hz)
goto err2; spi->max_speed_hz = TSC2005_SPI_MAX_SPEED_HZ;
error = spi_setup(spi);
if (error)
return error;
ts = kzalloc(sizeof(*ts), GFP_KERNEL);
input_dev = input_allocate_device();
if (!ts || !input_dev) {
error = -ENOMEM;
goto err_free_mem;
} }
r = sysfs_create_group(&ts->spi->dev.kobj, &tsc2005_attr_group); ts->spi = spi;
if (r) ts->idev = input_dev;
dev_warn(&ts->spi->dev, "sysfs entry creation failed: %d\n", r);
ts->x_plate_ohm = pdata->ts_x_plate_ohm ? : 280;
ts->esd_timeout = pdata->esd_timeout_ms;
ts->set_reset = pdata->set_reset;
tsc2005_start_scan(ts); mutex_init(&ts->mutex);
if (!ts->esd_timeout || !ts->set_reset) setup_timer(&ts->penup_timer, tsc2005_penup_timer, (unsigned long)ts);
goto done; INIT_WORK(&ts->penup_work, tsc2005_penup_work);
/* start the optional ESD watchdog */
setup_timer(&ts->esd_timer, tsc2005_esd_timer, (unsigned long)ts); setup_timer(&ts->esd_timer, tsc2005_esd_timer, (unsigned long)ts);
INIT_WORK(&ts->esd_work, tsc2005_esd_work); INIT_WORK(&ts->esd_work, tsc2005_esd_work);
mod_timer(&ts->esd_timer,
round_jiffies(jiffies + msecs_to_jiffies(ts->esd_timeout)));
done: tsc2005_setup_spi_xfer(ts);
return 0;
err2:
free_irq(ts->spi->irq, ts);
err1:
input_free_device(ts->idev);
return r;
}
static int __devinit tsc2005_probe(struct spi_device *spi) snprintf(ts->phys, sizeof(ts->phys),
{ "%s/input-ts", dev_name(&spi->dev));
struct tsc2005_platform_data *pdata = spi->dev.platform_data;
struct tsc2005 *ts; input_dev->name = "TSC2005 touchscreen";
int r; input_dev->phys = ts->phys;
input_dev->id.bustype = BUS_SPI;
input_dev->dev.parent = &spi->dev;
input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
input_set_abs_params(input_dev, ABS_X, 0, max_x, fudge_x, 0);
input_set_abs_params(input_dev, ABS_Y, 0, max_y, fudge_y, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, max_p, fudge_p, 0);
error = request_threaded_irq(spi->irq,
tsc2005_irq_handler, tsc2005_irq_thread,
IRQF_TRIGGER_RISING, "tsc2005", ts);
if (error) {
dev_err(&spi->dev, "Failed to request irq, err: %d\n", error);
goto err_free_mem;
}
if (spi->irq < 0) { spi_set_drvdata(spi, ts);
dev_dbg(&spi->dev, "no irq\n"); error = sysfs_create_group(&spi->dev.kobj, &tsc2005_attr_group);
return -ENODEV; if (error) {
dev_err(&spi->dev,
"Failed to create sysfs attributes, err: %d\n", error);
goto err_clear_drvdata;
} }
if (!pdata) { error = input_register_device(ts->idev);
dev_dbg(&spi->dev, "no platform data\n"); if (error) {
return -ENODEV; dev_err(&spi->dev,
"Failed to register input device, err: %d\n", error);
goto err_remove_sysfs;
} }
ts = kzalloc(sizeof(*ts), GFP_KERNEL); tsc2005_start_scan(ts);
if (ts == NULL)
return -ENOMEM;
spi_set_drvdata(spi, ts); if (ts->esd_timeout && ts->set_reset) {
ts->spi = spi; /* start the optional ESD watchdog */
spi->dev.power.power_state = PMSG_ON; mod_timer(&ts->esd_timer, round_jiffies(jiffies +
spi->mode = SPI_MODE_0; msecs_to_jiffies(ts->esd_timeout)));
spi->bits_per_word = 8; }
if (!spi->max_speed_hz)
spi->max_speed_hz = TSC2005_SPI_MAX_SPEED_HZ;
spi_setup(spi);
r = tsc2005_setup(ts, pdata); set_irq_wake(spi->irq, 1);
if (r) { return 0;
kfree(ts);
err_remove_sysfs:
sysfs_remove_group(&spi->dev.kobj, &tsc2005_attr_group);
err_clear_drvdata:
spi_set_drvdata(spi, NULL); spi_set_drvdata(spi, NULL);
} free_irq(spi->irq, ts);
return r; err_free_mem:
input_free_device(input_dev);
kfree(ts);
return error;
} }
static int __devexit tsc2005_remove(struct spi_device *spi) static int __devexit tsc2005_remove(struct spi_device *spi)
{ {
struct tsc2005 *ts = spi_get_drvdata(spi); struct tsc2005 *ts = spi_get_drvdata(spi);
sysfs_remove_group(&ts->spi->dev.kobj, &tsc2005_attr_group);
mutex_lock(&ts->mutex); mutex_lock(&ts->mutex);
tsc2005_disable(ts); tsc2005_disable(ts);
mutex_unlock(&ts->mutex); mutex_unlock(&ts->mutex);
...@@ -658,7 +654,6 @@ static int __devexit tsc2005_remove(struct spi_device *spi) ...@@ -658,7 +654,6 @@ static int __devexit tsc2005_remove(struct spi_device *spi)
flush_work(&ts->esd_work); flush_work(&ts->esd_work);
flush_work(&ts->penup_work); flush_work(&ts->penup_work);
sysfs_remove_group(&ts->spi->dev.kobj, &tsc2005_attr_group);
free_irq(ts->spi->irq, ts); free_irq(ts->spi->irq, ts);
input_unregister_device(ts->idev); input_unregister_device(ts->idev);
kfree(ts); kfree(ts);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment