Commit 6ab0f5cd authored by Matthew Wilcox's avatar Matthew Wilcox Committed by Kyle McMartin

[PARISC] Update parisc specific input code from parisc tree

Update drivers to new input layer changes.
Signed-off-by: default avatarHelge Deller <deller@parisc-linux.org>
Signed-off-by: default avatarMatthew Wilcox <willy@parisc-linux.org>

Reorder code in gscps2_interrupt() and only enable ports when opened.
This fixes issues with hangs booting an SMP kernel on my C360.
Previously serio_interrupt() could be called before the lock in
struct serio was initialised.
Signed-off-by: default avatarRichard Hirst <rhirst@parisc-linux.org>
Signed-off-by: default avatarKyle McMartin <kyle@parisc-linux.org>
parent ae8c75c1
......@@ -204,7 +204,7 @@ static irqreturn_t hil_kbd_interrupt(struct serio *serio,
hil_packet packet;
int idx;
kbd = (struct hil_kbd *)serio->private;
kbd = serio_get_drvdata(serio);
if (kbd == NULL) {
BUG();
return IRQ_HANDLED;
......@@ -234,7 +234,7 @@ static void hil_kbd_disconnect(struct serio *serio)
{
struct hil_kbd *kbd;
kbd = (struct hil_kbd *)serio->private;
kbd = serio_get_drvdata(serio);
if (kbd == NULL) {
BUG();
return;
......@@ -245,20 +245,20 @@ static void hil_kbd_disconnect(struct serio *serio)
kfree(kbd);
}
static void hil_kbd_connect(struct serio *serio, struct serio_driver *drv)
static int hil_kbd_connect(struct serio *serio, struct serio_driver *drv)
{
struct hil_kbd *kbd;
uint8_t did, *idd;
int i;
if (serio->type != (SERIO_HIL_MLC | SERIO_HIL)) return;
if (!(kbd = kmalloc(sizeof(struct hil_kbd), GFP_KERNEL))) return;
kbd = kmalloc(sizeof(*kbd), GFP_KERNEL);
if (!kbd)
return -ENOMEM;
memset(kbd, 0, sizeof(struct hil_kbd));
if (serio_open(serio, drv)) goto bail0;
serio->private = kbd;
serio_set_drvdata(serio, kbd);
kbd->serio = serio;
kbd->dev.private = kbd;
......@@ -342,19 +342,31 @@ static void hil_kbd_connect(struct serio *serio, struct serio_driver *drv)
down(&(kbd->sem));
up(&(kbd->sem));
return;
return 0;
bail1:
serio_close(serio);
bail0:
kfree(kbd);
serio_set_drvdata(serio, NULL);
return -EIO;
}
static struct serio_device_id hil_kbd_ids[] = {
{
.type = SERIO_HIL_MLC,
.proto = SERIO_HIL,
.id = SERIO_ANY,
.extra = SERIO_ANY,
},
{ 0 }
};
struct serio_driver hil_kbd_serio_drv = {
.driver = {
.name = "hil_kbd",
},
.description = "HP HIL keyboard driver",
.id_table = hil_kbd_ids,
.connect = hil_kbd_connect,
.disconnect = hil_kbd_disconnect,
.interrupt = hil_kbd_interrupt
......
......@@ -22,7 +22,7 @@
#include <linux/errno.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/hil.h>
#include <linux/spinlock.h>
......
......@@ -196,7 +196,7 @@ static irqreturn_t hil_ptr_interrupt(struct serio *serio,
hil_packet packet;
int idx;
ptr = (struct hil_ptr *)serio->private;
ptr = serio_get_drvdata(serio);
if (ptr == NULL) {
BUG();
return IRQ_HANDLED;
......@@ -227,7 +227,7 @@ static void hil_ptr_disconnect(struct serio *serio)
{
struct hil_ptr *ptr;
ptr = (struct hil_ptr *)serio->private;
ptr = serio_get_drvdata(serio);
if (ptr == NULL) {
BUG();
return;
......@@ -238,21 +238,19 @@ static void hil_ptr_disconnect(struct serio *serio)
kfree(ptr);
}
static void hil_ptr_connect(struct serio *serio, struct serio_driver *driver)
static int hil_ptr_connect(struct serio *serio, struct serio_driver *driver)
{
struct hil_ptr *ptr;
char *txt;
unsigned int i, naxsets, btntype;
uint8_t did, *idd;
if (serio->type != (SERIO_HIL_MLC | SERIO_HIL)) return;
if (!(ptr = kmalloc(sizeof(struct hil_ptr), GFP_KERNEL))) return;
if (!(ptr = kmalloc(sizeof(struct hil_ptr), GFP_KERNEL))) return -ENOMEM;
memset(ptr, 0, sizeof(struct hil_ptr));
if (serio_open(serio, driver)) goto bail0;
serio->private = ptr;
serio_set_drvdata(serio, ptr);
ptr->serio = serio;
ptr->dev.private = ptr;
......@@ -380,23 +378,34 @@ static void hil_ptr_connect(struct serio *serio, struct serio_driver *driver)
(btntype == BTN_MOUSE) ? "HIL mouse":"HIL tablet or touchpad",
did);
return;
return 0;
bail1:
serio_close(serio);
bail0:
kfree(ptr);
return;
serio_set_drvdata(serio, NULL);
return -ENODEV;
}
static struct serio_device_id hil_ptr_ids[] = {
{
.type = SERIO_HIL_MLC,
.proto = SERIO_HIL,
.id = SERIO_ANY,
.extra = SERIO_ANY,
},
{ 0 }
};
static struct serio_driver hil_ptr_serio_driver = {
.driver = {
.name = "hil_ptr",
},
.description = "HP HIL mouse/tablet driver",
.connect = hil_ptr_connect,
.disconnect = hil_ptr_disconnect,
.interrupt = hil_ptr_interrupt
.id_table = hil_ptr_ids,
.connect = hil_ptr_connect,
.disconnect = hil_ptr_disconnect,
.interrupt = hil_ptr_interrupt
};
static int __init hil_ptr_init(void)
......
......@@ -211,9 +211,6 @@ static void gscps2_reset(struct gscps2port *ps2port)
writeb(0xff, addr+GSC_RESET);
gscps2_flush(ps2port);
spin_unlock_irqrestore(&ps2port->lock, flags);
/* enable it */
gscps2_enable(ps2port, ENABLE);
}
static LIST_HEAD(ps2port_list);
......@@ -307,6 +304,9 @@ static int gscps2_open(struct serio *port)
gscps2_reset(ps2port);
/* enable it */
gscps2_enable(ps2port, ENABLE);
gscps2_interrupt(0, NULL, NULL);
return 0;
......@@ -370,8 +370,6 @@ static int __init gscps2_probe(struct parisc_device *dev)
serio->port_data = ps2port;
serio->dev.parent = &dev->dev;
list_add_tail(&ps2port->node, &ps2port_list);
ret = -EBUSY;
if (request_irq(dev->irq, gscps2_interrupt, SA_SHIRQ, ps2port->port->name, ps2port))
goto fail_miserably;
......@@ -396,15 +394,16 @@ static int __init gscps2_probe(struct parisc_device *dev)
serio_register_port(ps2port->port);
list_add_tail(&ps2port->node, &ps2port_list);
return 0;
fail:
free_irq(dev->irq, ps2port);
fail_miserably:
list_del(&ps2port->node);
iounmap(ps2port->addr);
release_mem_region(dev->hpa, GSC_STATUS + 4);
release_mem_region(dev->hpa.start, GSC_STATUS + 4);
fail_nomem:
kfree(ps2port);
......
......@@ -801,7 +801,8 @@ static int hil_mlc_serio_open(struct serio *serio) {
struct hil_mlc_serio_map *map;
struct hil_mlc *mlc;
if (serio->private != NULL) return -EBUSY;
if (serio_get_drvdata(serio) != NULL)
return -EBUSY;
map = serio->port_data;
if (map == NULL) {
......@@ -832,11 +833,18 @@ static void hil_mlc_serio_close(struct serio *serio) {
return;
}
serio->private = NULL;
serio_set_drvdata(serio, NULL);
serio->drv = NULL;
/* TODO wake up interruptable */
}
static struct serio_device_id hil_mlc_serio_id = {
.type = SERIO_HIL_MLC,
.proto = SERIO_HIL,
.extra = SERIO_ANY,
.id = SERIO_ANY,
};
int hil_mlc_register(hil_mlc *mlc) {
int i;
unsigned long flags;
......@@ -867,7 +875,7 @@ int hil_mlc_register(hil_mlc *mlc) {
mlc_serio = kmalloc(sizeof(*mlc_serio), GFP_KERNEL);
mlc->serio[i] = mlc_serio;
memset(mlc_serio, 0, sizeof(*mlc_serio));
mlc_serio->type = SERIO_HIL | SERIO_HIL_MLC;
mlc_serio->id = hil_mlc_serio_id;
mlc_serio->write = hil_mlc_serio_write;
mlc_serio->open = hil_mlc_serio_open;
mlc_serio->close = hil_mlc_serio_close;
......
This diff is collapsed.
/*
* HP Human Interface Loop Master Link Controller driver.
*
* Copyright (c) 2001 Brian S. Julin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL").
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
*
* References:
* HP-HIL Technical Reference Manual. Hewlett Packard Product No. 45918A
*
*/
#include <linux/hil.h>
#include <linux/time.h>
#include <linux/interrupt.h>
#include <asm/semaphore.h>
#include <linux/serio.h>
#include <linux/list.h>
typedef struct hil_mlc hil_mlc;
/* The HIL has a complicated state engine.
* We define the structure of nodes in the state engine here.
*/
enum hilse_act {
/* HILSE_OUT prepares to receive input if the next node
* is an IN or EXPECT, and then sends the given packet.
*/
HILSE_OUT = 0,
/* HILSE_CTS checks if the loop is busy. */
HILSE_CTS,
/* HILSE_OUT_LAST sends the given command packet to
* the last configured/running device on the loop.
*/
HILSE_OUT_LAST,
/* HILSE_OUT_DISC sends the given command packet to
* the next device past the last configured/running one.
*/
HILSE_OUT_DISC,
/* HILSE_FUNC runs a callback function with given arguments.
* a positive return value causes the "ugly" branch to be taken.
*/
HILSE_FUNC,
/* HILSE_IN simply expects any non-errored packet to arrive
* within arg usecs.
*/
HILSE_IN = 0x100,
/* HILSE_EXPECT expects a particular packet to arrive
* within arg usecs, any other packet is considered an error.
*/
HILSE_EXPECT,
/* HILSE_EXPECT_LAST as above but dev field should be last
* discovered/operational device.
*/
HILSE_EXPECT_LAST,
/* HILSE_EXPECT_LAST as above but dev field should be first
* undiscovered/inoperational device.
*/
HILSE_EXPECT_DISC
};
typedef int (hilse_func) (hil_mlc *mlc, int arg);
struct hilse_node {
enum hilse_act act; /* How to process this node */
union {
hilse_func *func; /* Function to call if HILSE_FUNC */
hil_packet packet; /* Packet to send or to compare */
} object;
int arg; /* Timeout in usec or parm for func */
int good; /* Node to jump to on success */
int bad; /* Node to jump to on error */
int ugly; /* Node to jump to on timeout */
};
/* Methods for back-end drivers, e.g. hp_sdc_mlc */
typedef int (hil_mlc_cts) (hil_mlc *mlc);
typedef void (hil_mlc_out) (hil_mlc *mlc);
typedef int (hil_mlc_in) (hil_mlc *mlc, suseconds_t timeout);
struct hil_mlc_devinfo {
uint8_t idd[16]; /* Device ID Byte and Describe Record */
uint8_t rsc[16]; /* Security Code Header and Record */
uint8_t exd[16]; /* Extended Describe Record */
uint8_t rnm[16]; /* Device name as returned by RNM command */
};
struct hil_mlc_serio_map {
hil_mlc *mlc;
int di_revmap;
int didx;
};
/* How many (possibly old/detached) devices the we try to keep track of */
#define HIL_MLC_DEVMEM 16
struct hil_mlc {
struct list_head list; /* hil_mlc is organized as linked list */
rwlock_t lock;
void *priv; /* Data specific to a particular type of MLC */
int seidx; /* Current node in state engine */
int istarted, ostarted;
hil_mlc_cts *cts;
struct semaphore csem; /* Raised when loop idle */
hil_mlc_out *out;
struct semaphore osem; /* Raised when outpacket dispatched */
hil_packet opacket;
hil_mlc_in *in;
struct semaphore isem; /* Raised when a packet arrives */
hil_packet ipacket[16];
hil_packet imatch;
int icount;
struct timeval instart;
suseconds_t intimeout;
int ddi; /* Last operational device id */
int lcv; /* LCV to throttle loops */
struct timeval lcv_tv; /* Time loop was started */
int di_map[7]; /* Maps below items to live devs */
struct hil_mlc_devinfo di[HIL_MLC_DEVMEM];
struct serio *serio[HIL_MLC_DEVMEM];
struct hil_mlc_serio_map serio_map[HIL_MLC_DEVMEM];
hil_packet serio_opacket[HIL_MLC_DEVMEM];
int serio_oidx[HIL_MLC_DEVMEM];
struct hil_mlc_devinfo di_scratch; /* Temporary area */
int opercnt;
struct tasklet_struct *tasklet;
};
int hil_mlc_register(hil_mlc *mlc);
int hil_mlc_unregister(hil_mlc *mlc);
This diff is collapsed.
......@@ -644,6 +644,7 @@ struct input_absinfo {
#define BUS_ADB 0x17
#define BUS_I2C 0x18
#define BUS_HOST 0x19
#define BUS_GSC 0x1A
/*
* Values describing the status of an effect
......
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