1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (C) 2006-2012 Robert Gerlach <khnz@gmx.de>
4*4882a593Smuzhiyun * Copyright (C) 2005-2006 Jan Rychter <jan@rychter.com>
5*4882a593Smuzhiyun */
6*4882a593Smuzhiyun
7*4882a593Smuzhiyun #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include <linux/kernel.h>
10*4882a593Smuzhiyun #include <linux/module.h>
11*4882a593Smuzhiyun #include <linux/init.h>
12*4882a593Smuzhiyun #include <linux/bitops.h>
13*4882a593Smuzhiyun #include <linux/io.h>
14*4882a593Smuzhiyun #include <linux/ioport.h>
15*4882a593Smuzhiyun #include <linux/acpi.h>
16*4882a593Smuzhiyun #include <linux/device.h>
17*4882a593Smuzhiyun #include <linux/interrupt.h>
18*4882a593Smuzhiyun #include <linux/input.h>
19*4882a593Smuzhiyun #include <linux/delay.h>
20*4882a593Smuzhiyun #include <linux/dmi.h>
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun #define MODULENAME "fujitsu-tablet"
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun #define ACPI_FUJITSU_CLASS "fujitsu"
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun #define INVERT_TABLET_MODE_BIT 0x01
27*4882a593Smuzhiyun #define INVERT_DOCK_STATE_BIT 0x02
28*4882a593Smuzhiyun #define FORCE_TABLET_MODE_IF_UNDOCK 0x04
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun #define KEYMAP_LEN 16
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun static const struct acpi_device_id fujitsu_ids[] = {
33*4882a593Smuzhiyun { .id = "FUJ02BD" },
34*4882a593Smuzhiyun { .id = "FUJ02BF" },
35*4882a593Smuzhiyun { .id = "" }
36*4882a593Smuzhiyun };
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun struct fujitsu_config {
39*4882a593Smuzhiyun unsigned short keymap[KEYMAP_LEN];
40*4882a593Smuzhiyun unsigned int quirks;
41*4882a593Smuzhiyun };
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun static unsigned short keymap_Lifebook_Tseries[KEYMAP_LEN] __initdata = {
44*4882a593Smuzhiyun KEY_RESERVED,
45*4882a593Smuzhiyun KEY_RESERVED,
46*4882a593Smuzhiyun KEY_RESERVED,
47*4882a593Smuzhiyun KEY_RESERVED,
48*4882a593Smuzhiyun KEY_SCROLLDOWN,
49*4882a593Smuzhiyun KEY_SCROLLUP,
50*4882a593Smuzhiyun KEY_ROTATE_DISPLAY,
51*4882a593Smuzhiyun KEY_LEFTCTRL,
52*4882a593Smuzhiyun KEY_BRIGHTNESSUP,
53*4882a593Smuzhiyun KEY_BRIGHTNESSDOWN,
54*4882a593Smuzhiyun KEY_BRIGHTNESS_ZERO,
55*4882a593Smuzhiyun KEY_RESERVED,
56*4882a593Smuzhiyun KEY_RESERVED,
57*4882a593Smuzhiyun KEY_RESERVED,
58*4882a593Smuzhiyun KEY_RESERVED,
59*4882a593Smuzhiyun KEY_LEFTALT
60*4882a593Smuzhiyun };
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun static unsigned short keymap_Lifebook_T901[KEYMAP_LEN] __initdata = {
63*4882a593Smuzhiyun KEY_RESERVED,
64*4882a593Smuzhiyun KEY_RESERVED,
65*4882a593Smuzhiyun KEY_RESERVED,
66*4882a593Smuzhiyun KEY_RESERVED,
67*4882a593Smuzhiyun KEY_SCROLLDOWN,
68*4882a593Smuzhiyun KEY_SCROLLUP,
69*4882a593Smuzhiyun KEY_CYCLEWINDOWS,
70*4882a593Smuzhiyun KEY_LEFTCTRL,
71*4882a593Smuzhiyun KEY_RESERVED,
72*4882a593Smuzhiyun KEY_RESERVED,
73*4882a593Smuzhiyun KEY_RESERVED,
74*4882a593Smuzhiyun KEY_RESERVED,
75*4882a593Smuzhiyun KEY_RESERVED,
76*4882a593Smuzhiyun KEY_RESERVED,
77*4882a593Smuzhiyun KEY_RESERVED,
78*4882a593Smuzhiyun KEY_LEFTMETA
79*4882a593Smuzhiyun };
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun static unsigned short keymap_Lifebook_T902[KEYMAP_LEN] __initdata = {
82*4882a593Smuzhiyun KEY_RESERVED,
83*4882a593Smuzhiyun KEY_VOLUMEDOWN,
84*4882a593Smuzhiyun KEY_VOLUMEUP,
85*4882a593Smuzhiyun KEY_CYCLEWINDOWS,
86*4882a593Smuzhiyun KEY_PROG1,
87*4882a593Smuzhiyun KEY_PROG2,
88*4882a593Smuzhiyun KEY_LEFTMETA,
89*4882a593Smuzhiyun KEY_RESERVED,
90*4882a593Smuzhiyun KEY_RESERVED,
91*4882a593Smuzhiyun KEY_RESERVED,
92*4882a593Smuzhiyun KEY_RESERVED,
93*4882a593Smuzhiyun KEY_RESERVED,
94*4882a593Smuzhiyun KEY_RESERVED,
95*4882a593Smuzhiyun KEY_RESERVED,
96*4882a593Smuzhiyun KEY_RESERVED,
97*4882a593Smuzhiyun KEY_RESERVED,
98*4882a593Smuzhiyun };
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun static unsigned short keymap_Lifebook_U810[KEYMAP_LEN] __initdata = {
101*4882a593Smuzhiyun KEY_RESERVED,
102*4882a593Smuzhiyun KEY_RESERVED,
103*4882a593Smuzhiyun KEY_RESERVED,
104*4882a593Smuzhiyun KEY_RESERVED,
105*4882a593Smuzhiyun KEY_PROG1,
106*4882a593Smuzhiyun KEY_PROG2,
107*4882a593Smuzhiyun KEY_ROTATE_DISPLAY,
108*4882a593Smuzhiyun KEY_RESERVED,
109*4882a593Smuzhiyun KEY_RESERVED,
110*4882a593Smuzhiyun KEY_RESERVED,
111*4882a593Smuzhiyun KEY_UP,
112*4882a593Smuzhiyun KEY_DOWN,
113*4882a593Smuzhiyun KEY_RESERVED,
114*4882a593Smuzhiyun KEY_RESERVED,
115*4882a593Smuzhiyun KEY_LEFTCTRL,
116*4882a593Smuzhiyun KEY_LEFTALT
117*4882a593Smuzhiyun };
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun static unsigned short keymap_Stylistic_Tseries[KEYMAP_LEN] __initdata = {
120*4882a593Smuzhiyun KEY_RESERVED,
121*4882a593Smuzhiyun KEY_RESERVED,
122*4882a593Smuzhiyun KEY_RESERVED,
123*4882a593Smuzhiyun KEY_RESERVED,
124*4882a593Smuzhiyun KEY_PRINT,
125*4882a593Smuzhiyun KEY_BACKSPACE,
126*4882a593Smuzhiyun KEY_SPACE,
127*4882a593Smuzhiyun KEY_ENTER,
128*4882a593Smuzhiyun KEY_BRIGHTNESSUP,
129*4882a593Smuzhiyun KEY_BRIGHTNESSDOWN,
130*4882a593Smuzhiyun KEY_DOWN,
131*4882a593Smuzhiyun KEY_UP,
132*4882a593Smuzhiyun KEY_SCROLLUP,
133*4882a593Smuzhiyun KEY_SCROLLDOWN,
134*4882a593Smuzhiyun KEY_LEFTCTRL,
135*4882a593Smuzhiyun KEY_LEFTALT
136*4882a593Smuzhiyun };
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun static unsigned short keymap_Stylistic_ST5xxx[KEYMAP_LEN] __initdata = {
139*4882a593Smuzhiyun KEY_RESERVED,
140*4882a593Smuzhiyun KEY_RESERVED,
141*4882a593Smuzhiyun KEY_RESERVED,
142*4882a593Smuzhiyun KEY_RESERVED,
143*4882a593Smuzhiyun KEY_MAIL,
144*4882a593Smuzhiyun KEY_ROTATE_DISPLAY,
145*4882a593Smuzhiyun KEY_ESC,
146*4882a593Smuzhiyun KEY_ENTER,
147*4882a593Smuzhiyun KEY_BRIGHTNESSUP,
148*4882a593Smuzhiyun KEY_BRIGHTNESSDOWN,
149*4882a593Smuzhiyun KEY_DOWN,
150*4882a593Smuzhiyun KEY_UP,
151*4882a593Smuzhiyun KEY_SCROLLUP,
152*4882a593Smuzhiyun KEY_SCROLLDOWN,
153*4882a593Smuzhiyun KEY_LEFTCTRL,
154*4882a593Smuzhiyun KEY_LEFTALT
155*4882a593Smuzhiyun };
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun static struct {
158*4882a593Smuzhiyun struct input_dev *idev;
159*4882a593Smuzhiyun struct fujitsu_config config;
160*4882a593Smuzhiyun unsigned long prev_keymask;
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun char phys[21];
163*4882a593Smuzhiyun
164*4882a593Smuzhiyun int irq;
165*4882a593Smuzhiyun int io_base;
166*4882a593Smuzhiyun int io_length;
167*4882a593Smuzhiyun } fujitsu;
168*4882a593Smuzhiyun
fujitsu_ack(void)169*4882a593Smuzhiyun static u8 fujitsu_ack(void)
170*4882a593Smuzhiyun {
171*4882a593Smuzhiyun return inb(fujitsu.io_base + 2);
172*4882a593Smuzhiyun }
173*4882a593Smuzhiyun
fujitsu_status(void)174*4882a593Smuzhiyun static u8 fujitsu_status(void)
175*4882a593Smuzhiyun {
176*4882a593Smuzhiyun return inb(fujitsu.io_base + 6);
177*4882a593Smuzhiyun }
178*4882a593Smuzhiyun
fujitsu_read_register(const u8 addr)179*4882a593Smuzhiyun static u8 fujitsu_read_register(const u8 addr)
180*4882a593Smuzhiyun {
181*4882a593Smuzhiyun outb(addr, fujitsu.io_base);
182*4882a593Smuzhiyun return inb(fujitsu.io_base + 4);
183*4882a593Smuzhiyun }
184*4882a593Smuzhiyun
fujitsu_send_state(void)185*4882a593Smuzhiyun static void fujitsu_send_state(void)
186*4882a593Smuzhiyun {
187*4882a593Smuzhiyun int state;
188*4882a593Smuzhiyun int dock, tablet_mode;
189*4882a593Smuzhiyun
190*4882a593Smuzhiyun state = fujitsu_read_register(0xdd);
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun dock = state & 0x02;
193*4882a593Smuzhiyun if (fujitsu.config.quirks & INVERT_DOCK_STATE_BIT)
194*4882a593Smuzhiyun dock = !dock;
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun if ((fujitsu.config.quirks & FORCE_TABLET_MODE_IF_UNDOCK) && (!dock)) {
197*4882a593Smuzhiyun tablet_mode = 1;
198*4882a593Smuzhiyun } else{
199*4882a593Smuzhiyun tablet_mode = state & 0x01;
200*4882a593Smuzhiyun if (fujitsu.config.quirks & INVERT_TABLET_MODE_BIT)
201*4882a593Smuzhiyun tablet_mode = !tablet_mode;
202*4882a593Smuzhiyun }
203*4882a593Smuzhiyun
204*4882a593Smuzhiyun input_report_switch(fujitsu.idev, SW_DOCK, dock);
205*4882a593Smuzhiyun input_report_switch(fujitsu.idev, SW_TABLET_MODE, tablet_mode);
206*4882a593Smuzhiyun input_sync(fujitsu.idev);
207*4882a593Smuzhiyun }
208*4882a593Smuzhiyun
fujitsu_reset(void)209*4882a593Smuzhiyun static void fujitsu_reset(void)
210*4882a593Smuzhiyun {
211*4882a593Smuzhiyun int timeout = 50;
212*4882a593Smuzhiyun
213*4882a593Smuzhiyun fujitsu_ack();
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun while ((fujitsu_status() & 0x02) && (--timeout))
216*4882a593Smuzhiyun msleep(20);
217*4882a593Smuzhiyun
218*4882a593Smuzhiyun fujitsu_send_state();
219*4882a593Smuzhiyun }
220*4882a593Smuzhiyun
input_fujitsu_setup(struct device * parent,const char * name,const char * phys)221*4882a593Smuzhiyun static int input_fujitsu_setup(struct device *parent, const char *name,
222*4882a593Smuzhiyun const char *phys)
223*4882a593Smuzhiyun {
224*4882a593Smuzhiyun struct input_dev *idev;
225*4882a593Smuzhiyun int error;
226*4882a593Smuzhiyun int i;
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun idev = input_allocate_device();
229*4882a593Smuzhiyun if (!idev)
230*4882a593Smuzhiyun return -ENOMEM;
231*4882a593Smuzhiyun
232*4882a593Smuzhiyun idev->dev.parent = parent;
233*4882a593Smuzhiyun idev->phys = phys;
234*4882a593Smuzhiyun idev->name = name;
235*4882a593Smuzhiyun idev->id.bustype = BUS_HOST;
236*4882a593Smuzhiyun idev->id.vendor = 0x1734; /* Fujitsu Siemens Computer GmbH */
237*4882a593Smuzhiyun idev->id.product = 0x0001;
238*4882a593Smuzhiyun idev->id.version = 0x0101;
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun idev->keycode = fujitsu.config.keymap;
241*4882a593Smuzhiyun idev->keycodesize = sizeof(fujitsu.config.keymap[0]);
242*4882a593Smuzhiyun idev->keycodemax = ARRAY_SIZE(fujitsu.config.keymap);
243*4882a593Smuzhiyun
244*4882a593Smuzhiyun __set_bit(EV_REP, idev->evbit);
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(fujitsu.config.keymap); i++)
247*4882a593Smuzhiyun if (fujitsu.config.keymap[i])
248*4882a593Smuzhiyun input_set_capability(idev, EV_KEY, fujitsu.config.keymap[i]);
249*4882a593Smuzhiyun
250*4882a593Smuzhiyun input_set_capability(idev, EV_MSC, MSC_SCAN);
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun input_set_capability(idev, EV_SW, SW_DOCK);
253*4882a593Smuzhiyun input_set_capability(idev, EV_SW, SW_TABLET_MODE);
254*4882a593Smuzhiyun
255*4882a593Smuzhiyun error = input_register_device(idev);
256*4882a593Smuzhiyun if (error) {
257*4882a593Smuzhiyun input_free_device(idev);
258*4882a593Smuzhiyun return error;
259*4882a593Smuzhiyun }
260*4882a593Smuzhiyun
261*4882a593Smuzhiyun fujitsu.idev = idev;
262*4882a593Smuzhiyun return 0;
263*4882a593Smuzhiyun }
264*4882a593Smuzhiyun
input_fujitsu_remove(void)265*4882a593Smuzhiyun static void input_fujitsu_remove(void)
266*4882a593Smuzhiyun {
267*4882a593Smuzhiyun input_unregister_device(fujitsu.idev);
268*4882a593Smuzhiyun }
269*4882a593Smuzhiyun
fujitsu_interrupt(int irq,void * dev_id)270*4882a593Smuzhiyun static irqreturn_t fujitsu_interrupt(int irq, void *dev_id)
271*4882a593Smuzhiyun {
272*4882a593Smuzhiyun unsigned long keymask, changed;
273*4882a593Smuzhiyun unsigned int keycode;
274*4882a593Smuzhiyun int pressed;
275*4882a593Smuzhiyun int i;
276*4882a593Smuzhiyun
277*4882a593Smuzhiyun if (unlikely(!(fujitsu_status() & 0x01)))
278*4882a593Smuzhiyun return IRQ_NONE;
279*4882a593Smuzhiyun
280*4882a593Smuzhiyun fujitsu_send_state();
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun keymask = fujitsu_read_register(0xde);
283*4882a593Smuzhiyun keymask |= fujitsu_read_register(0xdf) << 8;
284*4882a593Smuzhiyun keymask ^= 0xffff;
285*4882a593Smuzhiyun
286*4882a593Smuzhiyun changed = keymask ^ fujitsu.prev_keymask;
287*4882a593Smuzhiyun if (changed) {
288*4882a593Smuzhiyun fujitsu.prev_keymask = keymask;
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun for_each_set_bit(i, &changed, KEYMAP_LEN) {
291*4882a593Smuzhiyun keycode = fujitsu.config.keymap[i];
292*4882a593Smuzhiyun pressed = keymask & changed & BIT(i);
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun if (pressed)
295*4882a593Smuzhiyun input_event(fujitsu.idev, EV_MSC, MSC_SCAN, i);
296*4882a593Smuzhiyun
297*4882a593Smuzhiyun input_report_key(fujitsu.idev, keycode, pressed);
298*4882a593Smuzhiyun input_sync(fujitsu.idev);
299*4882a593Smuzhiyun }
300*4882a593Smuzhiyun }
301*4882a593Smuzhiyun
302*4882a593Smuzhiyun fujitsu_ack();
303*4882a593Smuzhiyun return IRQ_HANDLED;
304*4882a593Smuzhiyun }
305*4882a593Smuzhiyun
fujitsu_dmi_common(const struct dmi_system_id * dmi)306*4882a593Smuzhiyun static void __init fujitsu_dmi_common(const struct dmi_system_id *dmi)
307*4882a593Smuzhiyun {
308*4882a593Smuzhiyun pr_info("%s\n", dmi->ident);
309*4882a593Smuzhiyun memcpy(fujitsu.config.keymap, dmi->driver_data,
310*4882a593Smuzhiyun sizeof(fujitsu.config.keymap));
311*4882a593Smuzhiyun }
312*4882a593Smuzhiyun
fujitsu_dmi_lifebook(const struct dmi_system_id * dmi)313*4882a593Smuzhiyun static int __init fujitsu_dmi_lifebook(const struct dmi_system_id *dmi)
314*4882a593Smuzhiyun {
315*4882a593Smuzhiyun fujitsu_dmi_common(dmi);
316*4882a593Smuzhiyun fujitsu.config.quirks |= INVERT_TABLET_MODE_BIT;
317*4882a593Smuzhiyun return 1;
318*4882a593Smuzhiyun }
319*4882a593Smuzhiyun
fujitsu_dmi_stylistic(const struct dmi_system_id * dmi)320*4882a593Smuzhiyun static int __init fujitsu_dmi_stylistic(const struct dmi_system_id *dmi)
321*4882a593Smuzhiyun {
322*4882a593Smuzhiyun fujitsu_dmi_common(dmi);
323*4882a593Smuzhiyun fujitsu.config.quirks |= FORCE_TABLET_MODE_IF_UNDOCK;
324*4882a593Smuzhiyun fujitsu.config.quirks |= INVERT_DOCK_STATE_BIT;
325*4882a593Smuzhiyun return 1;
326*4882a593Smuzhiyun }
327*4882a593Smuzhiyun
328*4882a593Smuzhiyun static const struct dmi_system_id dmi_ids[] __initconst = {
329*4882a593Smuzhiyun {
330*4882a593Smuzhiyun .callback = fujitsu_dmi_lifebook,
331*4882a593Smuzhiyun .ident = "Fujitsu Lifebook T901",
332*4882a593Smuzhiyun .matches = {
333*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
334*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook T901")
335*4882a593Smuzhiyun },
336*4882a593Smuzhiyun .driver_data = keymap_Lifebook_T901
337*4882a593Smuzhiyun },
338*4882a593Smuzhiyun {
339*4882a593Smuzhiyun .callback = fujitsu_dmi_lifebook,
340*4882a593Smuzhiyun .ident = "Fujitsu Lifebook T901",
341*4882a593Smuzhiyun .matches = {
342*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
343*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T901")
344*4882a593Smuzhiyun },
345*4882a593Smuzhiyun .driver_data = keymap_Lifebook_T901
346*4882a593Smuzhiyun },
347*4882a593Smuzhiyun {
348*4882a593Smuzhiyun .callback = fujitsu_dmi_lifebook,
349*4882a593Smuzhiyun .ident = "Fujitsu Lifebook T902",
350*4882a593Smuzhiyun .matches = {
351*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
352*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T902")
353*4882a593Smuzhiyun },
354*4882a593Smuzhiyun .driver_data = keymap_Lifebook_T902
355*4882a593Smuzhiyun },
356*4882a593Smuzhiyun {
357*4882a593Smuzhiyun .callback = fujitsu_dmi_lifebook,
358*4882a593Smuzhiyun .ident = "Fujitsu Siemens P/T Series",
359*4882a593Smuzhiyun .matches = {
360*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
361*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK")
362*4882a593Smuzhiyun },
363*4882a593Smuzhiyun .driver_data = keymap_Lifebook_Tseries
364*4882a593Smuzhiyun },
365*4882a593Smuzhiyun {
366*4882a593Smuzhiyun .callback = fujitsu_dmi_lifebook,
367*4882a593Smuzhiyun .ident = "Fujitsu Lifebook T Series",
368*4882a593Smuzhiyun .matches = {
369*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
370*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook T")
371*4882a593Smuzhiyun },
372*4882a593Smuzhiyun .driver_data = keymap_Lifebook_Tseries
373*4882a593Smuzhiyun },
374*4882a593Smuzhiyun {
375*4882a593Smuzhiyun .callback = fujitsu_dmi_stylistic,
376*4882a593Smuzhiyun .ident = "Fujitsu Siemens Stylistic T Series",
377*4882a593Smuzhiyun .matches = {
378*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
379*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "Stylistic T")
380*4882a593Smuzhiyun },
381*4882a593Smuzhiyun .driver_data = keymap_Stylistic_Tseries
382*4882a593Smuzhiyun },
383*4882a593Smuzhiyun {
384*4882a593Smuzhiyun .callback = fujitsu_dmi_lifebook,
385*4882a593Smuzhiyun .ident = "Fujitsu LifeBook U810",
386*4882a593Smuzhiyun .matches = {
387*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
388*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook U810")
389*4882a593Smuzhiyun },
390*4882a593Smuzhiyun .driver_data = keymap_Lifebook_U810
391*4882a593Smuzhiyun },
392*4882a593Smuzhiyun {
393*4882a593Smuzhiyun .callback = fujitsu_dmi_stylistic,
394*4882a593Smuzhiyun .ident = "Fujitsu Siemens Stylistic ST5xxx Series",
395*4882a593Smuzhiyun .matches = {
396*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
397*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "STYLISTIC ST5")
398*4882a593Smuzhiyun },
399*4882a593Smuzhiyun .driver_data = keymap_Stylistic_ST5xxx
400*4882a593Smuzhiyun },
401*4882a593Smuzhiyun {
402*4882a593Smuzhiyun .callback = fujitsu_dmi_stylistic,
403*4882a593Smuzhiyun .ident = "Fujitsu Siemens Stylistic ST5xxx Series",
404*4882a593Smuzhiyun .matches = {
405*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
406*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "Stylistic ST5")
407*4882a593Smuzhiyun },
408*4882a593Smuzhiyun .driver_data = keymap_Stylistic_ST5xxx
409*4882a593Smuzhiyun },
410*4882a593Smuzhiyun {
411*4882a593Smuzhiyun .callback = fujitsu_dmi_lifebook,
412*4882a593Smuzhiyun .ident = "Unknown (using defaults)",
413*4882a593Smuzhiyun .matches = {
414*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, ""),
415*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "")
416*4882a593Smuzhiyun },
417*4882a593Smuzhiyun .driver_data = keymap_Lifebook_Tseries
418*4882a593Smuzhiyun },
419*4882a593Smuzhiyun { NULL }
420*4882a593Smuzhiyun };
421*4882a593Smuzhiyun
fujitsu_walk_resources(struct acpi_resource * res,void * data)422*4882a593Smuzhiyun static acpi_status fujitsu_walk_resources(struct acpi_resource *res, void *data)
423*4882a593Smuzhiyun {
424*4882a593Smuzhiyun switch (res->type) {
425*4882a593Smuzhiyun case ACPI_RESOURCE_TYPE_IRQ:
426*4882a593Smuzhiyun fujitsu.irq = res->data.irq.interrupts[0];
427*4882a593Smuzhiyun return AE_OK;
428*4882a593Smuzhiyun
429*4882a593Smuzhiyun case ACPI_RESOURCE_TYPE_IO:
430*4882a593Smuzhiyun fujitsu.io_base = res->data.io.minimum;
431*4882a593Smuzhiyun fujitsu.io_length = res->data.io.address_length;
432*4882a593Smuzhiyun return AE_OK;
433*4882a593Smuzhiyun
434*4882a593Smuzhiyun case ACPI_RESOURCE_TYPE_END_TAG:
435*4882a593Smuzhiyun if (fujitsu.irq && fujitsu.io_base)
436*4882a593Smuzhiyun return AE_OK;
437*4882a593Smuzhiyun else
438*4882a593Smuzhiyun return AE_NOT_FOUND;
439*4882a593Smuzhiyun
440*4882a593Smuzhiyun default:
441*4882a593Smuzhiyun return AE_ERROR;
442*4882a593Smuzhiyun }
443*4882a593Smuzhiyun }
444*4882a593Smuzhiyun
acpi_fujitsu_add(struct acpi_device * adev)445*4882a593Smuzhiyun static int acpi_fujitsu_add(struct acpi_device *adev)
446*4882a593Smuzhiyun {
447*4882a593Smuzhiyun acpi_status status;
448*4882a593Smuzhiyun int error;
449*4882a593Smuzhiyun
450*4882a593Smuzhiyun if (!adev)
451*4882a593Smuzhiyun return -EINVAL;
452*4882a593Smuzhiyun
453*4882a593Smuzhiyun status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS,
454*4882a593Smuzhiyun fujitsu_walk_resources, NULL);
455*4882a593Smuzhiyun if (ACPI_FAILURE(status) || !fujitsu.irq || !fujitsu.io_base)
456*4882a593Smuzhiyun return -ENODEV;
457*4882a593Smuzhiyun
458*4882a593Smuzhiyun sprintf(acpi_device_name(adev), "Fujitsu %s", acpi_device_hid(adev));
459*4882a593Smuzhiyun sprintf(acpi_device_class(adev), "%s", ACPI_FUJITSU_CLASS);
460*4882a593Smuzhiyun
461*4882a593Smuzhiyun snprintf(fujitsu.phys, sizeof(fujitsu.phys),
462*4882a593Smuzhiyun "%s/input0", acpi_device_hid(adev));
463*4882a593Smuzhiyun
464*4882a593Smuzhiyun error = input_fujitsu_setup(&adev->dev,
465*4882a593Smuzhiyun acpi_device_name(adev), fujitsu.phys);
466*4882a593Smuzhiyun if (error)
467*4882a593Smuzhiyun return error;
468*4882a593Smuzhiyun
469*4882a593Smuzhiyun if (!request_region(fujitsu.io_base, fujitsu.io_length, MODULENAME)) {
470*4882a593Smuzhiyun input_fujitsu_remove();
471*4882a593Smuzhiyun return -EBUSY;
472*4882a593Smuzhiyun }
473*4882a593Smuzhiyun
474*4882a593Smuzhiyun fujitsu_reset();
475*4882a593Smuzhiyun
476*4882a593Smuzhiyun error = request_irq(fujitsu.irq, fujitsu_interrupt,
477*4882a593Smuzhiyun IRQF_SHARED, MODULENAME, fujitsu_interrupt);
478*4882a593Smuzhiyun if (error) {
479*4882a593Smuzhiyun release_region(fujitsu.io_base, fujitsu.io_length);
480*4882a593Smuzhiyun input_fujitsu_remove();
481*4882a593Smuzhiyun return error;
482*4882a593Smuzhiyun }
483*4882a593Smuzhiyun
484*4882a593Smuzhiyun return 0;
485*4882a593Smuzhiyun }
486*4882a593Smuzhiyun
acpi_fujitsu_remove(struct acpi_device * adev)487*4882a593Smuzhiyun static int acpi_fujitsu_remove(struct acpi_device *adev)
488*4882a593Smuzhiyun {
489*4882a593Smuzhiyun free_irq(fujitsu.irq, fujitsu_interrupt);
490*4882a593Smuzhiyun release_region(fujitsu.io_base, fujitsu.io_length);
491*4882a593Smuzhiyun input_fujitsu_remove();
492*4882a593Smuzhiyun return 0;
493*4882a593Smuzhiyun }
494*4882a593Smuzhiyun
495*4882a593Smuzhiyun #ifdef CONFIG_PM_SLEEP
acpi_fujitsu_resume(struct device * dev)496*4882a593Smuzhiyun static int acpi_fujitsu_resume(struct device *dev)
497*4882a593Smuzhiyun {
498*4882a593Smuzhiyun fujitsu_reset();
499*4882a593Smuzhiyun return 0;
500*4882a593Smuzhiyun }
501*4882a593Smuzhiyun #endif
502*4882a593Smuzhiyun
503*4882a593Smuzhiyun static SIMPLE_DEV_PM_OPS(acpi_fujitsu_pm, NULL, acpi_fujitsu_resume);
504*4882a593Smuzhiyun
505*4882a593Smuzhiyun static struct acpi_driver acpi_fujitsu_driver = {
506*4882a593Smuzhiyun .name = MODULENAME,
507*4882a593Smuzhiyun .class = "hotkey",
508*4882a593Smuzhiyun .ids = fujitsu_ids,
509*4882a593Smuzhiyun .ops = {
510*4882a593Smuzhiyun .add = acpi_fujitsu_add,
511*4882a593Smuzhiyun .remove = acpi_fujitsu_remove,
512*4882a593Smuzhiyun },
513*4882a593Smuzhiyun .drv.pm = &acpi_fujitsu_pm,
514*4882a593Smuzhiyun };
515*4882a593Smuzhiyun
fujitsu_module_init(void)516*4882a593Smuzhiyun static int __init fujitsu_module_init(void)
517*4882a593Smuzhiyun {
518*4882a593Smuzhiyun int error;
519*4882a593Smuzhiyun
520*4882a593Smuzhiyun dmi_check_system(dmi_ids);
521*4882a593Smuzhiyun
522*4882a593Smuzhiyun error = acpi_bus_register_driver(&acpi_fujitsu_driver);
523*4882a593Smuzhiyun if (error)
524*4882a593Smuzhiyun return error;
525*4882a593Smuzhiyun
526*4882a593Smuzhiyun return 0;
527*4882a593Smuzhiyun }
528*4882a593Smuzhiyun
fujitsu_module_exit(void)529*4882a593Smuzhiyun static void __exit fujitsu_module_exit(void)
530*4882a593Smuzhiyun {
531*4882a593Smuzhiyun acpi_bus_unregister_driver(&acpi_fujitsu_driver);
532*4882a593Smuzhiyun }
533*4882a593Smuzhiyun
534*4882a593Smuzhiyun module_init(fujitsu_module_init);
535*4882a593Smuzhiyun module_exit(fujitsu_module_exit);
536*4882a593Smuzhiyun
537*4882a593Smuzhiyun MODULE_AUTHOR("Robert Gerlach <khnz@gmx.de>");
538*4882a593Smuzhiyun MODULE_DESCRIPTION("Fujitsu tablet pc extras driver");
539*4882a593Smuzhiyun MODULE_LICENSE("GPL");
540*4882a593Smuzhiyun MODULE_VERSION("2.5");
541*4882a593Smuzhiyun
542*4882a593Smuzhiyun MODULE_DEVICE_TABLE(acpi, fujitsu_ids);
543