diff options
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/Kconfig | 16 | ||||
-rw-r--r-- | drivers/misc/Makefile | 2 | ||||
-rw-r--r-- | drivers/misc/acer-wmi.c | 6 | ||||
-rw-r--r-- | drivers/misc/asus-laptop.c | 10 | ||||
-rw-r--r-- | drivers/misc/c2port/Kconfig | 35 | ||||
-rw-r--r-- | drivers/misc/c2port/Makefile | 3 | ||||
-rw-r--r-- | drivers/misc/c2port/c2port-duramar2150.c | 158 | ||||
-rw-r--r-- | drivers/misc/c2port/core.c | 1002 | ||||
-rw-r--r-- | drivers/misc/compal-laptop.c | 12 | ||||
-rw-r--r-- | drivers/misc/eeepc-laptop.c | 12 | ||||
-rw-r--r-- | drivers/misc/fujitsu-laptop.c | 43 | ||||
-rw-r--r-- | drivers/misc/ics932s401.c | 515 | ||||
-rw-r--r-- | drivers/misc/intel_menlow.c | 10 | ||||
-rw-r--r-- | drivers/misc/msi-laptop.c | 16 | ||||
-rw-r--r-- | drivers/misc/sony-laptop.c | 6 | ||||
-rw-r--r-- | drivers/misc/thinkpad_acpi.c | 29 |
16 files changed, 1831 insertions, 44 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 9494400e8fd0..fee7304102af 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig | |||
@@ -227,10 +227,20 @@ config HP_WMI | |||
227 | To compile this driver as a module, choose M here: the module will | 227 | To compile this driver as a module, choose M here: the module will |
228 | be called hp-wmi. | 228 | be called hp-wmi. |
229 | 229 | ||
230 | config ICS932S401 | ||
231 | tristate "Integrated Circuits ICS932S401" | ||
232 | depends on I2C && EXPERIMENTAL | ||
233 | help | ||
234 | If you say yes here you get support for the Integrated Circuits | ||
235 | ICS932S401 clock control chips. | ||
236 | |||
237 | This driver can also be built as a module. If so, the module | ||
238 | will be called ics932s401. | ||
239 | |||
230 | config MSI_LAPTOP | 240 | config MSI_LAPTOP |
231 | tristate "MSI Laptop Extras" | 241 | tristate "MSI Laptop Extras" |
232 | depends on X86 | 242 | depends on X86 |
233 | depends on ACPI_EC | 243 | depends on ACPI |
234 | depends on BACKLIGHT_CLASS_DEVICE | 244 | depends on BACKLIGHT_CLASS_DEVICE |
235 | ---help--- | 245 | ---help--- |
236 | This is a driver for laptops built by MSI (MICRO-STAR | 246 | This is a driver for laptops built by MSI (MICRO-STAR |
@@ -260,7 +270,7 @@ config PANASONIC_LAPTOP | |||
260 | config COMPAL_LAPTOP | 270 | config COMPAL_LAPTOP |
261 | tristate "Compal Laptop Extras" | 271 | tristate "Compal Laptop Extras" |
262 | depends on X86 | 272 | depends on X86 |
263 | depends on ACPI_EC | 273 | depends on ACPI |
264 | depends on BACKLIGHT_CLASS_DEVICE | 274 | depends on BACKLIGHT_CLASS_DEVICE |
265 | ---help--- | 275 | ---help--- |
266 | This is a driver for laptops built by Compal: | 276 | This is a driver for laptops built by Compal: |
@@ -488,4 +498,6 @@ config SGI_GRU_DEBUG | |||
488 | This option enables addition debugging code for the SGI GRU driver. If | 498 | This option enables addition debugging code for the SGI GRU driver. If |
489 | you are unsure, say N. | 499 | you are unsure, say N. |
490 | 500 | ||
501 | source "drivers/misc/c2port/Kconfig" | ||
502 | |||
491 | endif # MISC_DEVICES | 503 | endif # MISC_DEVICES |
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 909e2468cdc9..817f7f5ab3bd 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile | |||
@@ -14,6 +14,7 @@ obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o | |||
14 | obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o | 14 | obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o |
15 | obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o | 15 | obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o |
16 | obj-$(CONFIG_HP_WMI) += hp-wmi.o | 16 | obj-$(CONFIG_HP_WMI) += hp-wmi.o |
17 | obj-$(CONFIG_ICS932S401) += ics932s401.o | ||
17 | obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o | 18 | obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o |
18 | obj-$(CONFIG_LKDTM) += lkdtm.o | 19 | obj-$(CONFIG_LKDTM) += lkdtm.o |
19 | obj-$(CONFIG_TIFM_CORE) += tifm_core.o | 20 | obj-$(CONFIG_TIFM_CORE) += tifm_core.o |
@@ -31,3 +32,4 @@ obj-$(CONFIG_KGDB_TESTS) += kgdbts.o | |||
31 | obj-$(CONFIG_SGI_XP) += sgi-xp/ | 32 | obj-$(CONFIG_SGI_XP) += sgi-xp/ |
32 | obj-$(CONFIG_SGI_GRU) += sgi-gru/ | 33 | obj-$(CONFIG_SGI_GRU) += sgi-gru/ |
33 | obj-$(CONFIG_HP_ILO) += hpilo.o | 34 | obj-$(CONFIG_HP_ILO) += hpilo.o |
35 | obj-$(CONFIG_C2PORT) += c2port/ | ||
diff --git a/drivers/misc/acer-wmi.c b/drivers/misc/acer-wmi.c index 0532a2de2ce4..94c9f911824e 100644 --- a/drivers/misc/acer-wmi.c +++ b/drivers/misc/acer-wmi.c | |||
@@ -1297,6 +1297,12 @@ static int __init acer_wmi_init(void) | |||
1297 | 1297 | ||
1298 | set_quirks(); | 1298 | set_quirks(); |
1299 | 1299 | ||
1300 | if (!acpi_video_backlight_support() && has_cap(ACER_CAP_BRIGHTNESS)) { | ||
1301 | interface->capability &= ~ACER_CAP_BRIGHTNESS; | ||
1302 | printk(ACER_INFO "Brightness must be controlled by " | ||
1303 | "generic video driver\n"); | ||
1304 | } | ||
1305 | |||
1300 | if (platform_driver_register(&acer_platform_driver)) { | 1306 | if (platform_driver_register(&acer_platform_driver)) { |
1301 | printk(ACER_ERR "Unable to register platform driver.\n"); | 1307 | printk(ACER_ERR "Unable to register platform driver.\n"); |
1302 | goto error_platform_register; | 1308 | goto error_platform_register; |
diff --git a/drivers/misc/asus-laptop.c b/drivers/misc/asus-laptop.c index a9d5228724a6..8fb8b3591048 100644 --- a/drivers/misc/asus-laptop.c +++ b/drivers/misc/asus-laptop.c | |||
@@ -1208,9 +1208,13 @@ static int __init asus_laptop_init(void) | |||
1208 | 1208 | ||
1209 | dev = acpi_get_physical_device(hotk->device->handle); | 1209 | dev = acpi_get_physical_device(hotk->device->handle); |
1210 | 1210 | ||
1211 | result = asus_backlight_init(dev); | 1211 | if (!acpi_video_backlight_support()) { |
1212 | if (result) | 1212 | result = asus_backlight_init(dev); |
1213 | goto fail_backlight; | 1213 | if (result) |
1214 | goto fail_backlight; | ||
1215 | } else | ||
1216 | printk(ASUS_INFO "Brightness ignored, must be controlled by " | ||
1217 | "ACPI video driver\n"); | ||
1214 | 1218 | ||
1215 | result = asus_led_init(dev); | 1219 | result = asus_led_init(dev); |
1216 | if (result) | 1220 | if (result) |
diff --git a/drivers/misc/c2port/Kconfig b/drivers/misc/c2port/Kconfig new file mode 100644 index 000000000000..e46af9a5810d --- /dev/null +++ b/drivers/misc/c2port/Kconfig | |||
@@ -0,0 +1,35 @@ | |||
1 | # | ||
2 | # C2 port devices | ||
3 | # | ||
4 | |||
5 | menuconfig C2PORT | ||
6 | tristate "Silicon Labs C2 port support (EXPERIMENTAL)" | ||
7 | depends on EXPERIMENTAL | ||
8 | default no | ||
9 | help | ||
10 | This option enables support for Silicon Labs C2 port used to | ||
11 | program Silicon micro controller chips (and other 8051 compatible). | ||
12 | |||
13 | If your board have no such micro controllers you don't need this | ||
14 | interface at all. | ||
15 | |||
16 | To compile this driver as a module, choose M here: the module will | ||
17 | be called c2port_core. Note that you also need a client module | ||
18 | usually called c2port-*. | ||
19 | |||
20 | If you are not sure, say N here. | ||
21 | |||
22 | if C2PORT | ||
23 | |||
24 | config C2PORT_DURAMAR_2150 | ||
25 | tristate "C2 port support for Eurotech's Duramar 2150 (EXPERIMENTAL)" | ||
26 | depends on X86 && C2PORT | ||
27 | default no | ||
28 | help | ||
29 | This option enables C2 support for the Eurotech's Duramar 2150 | ||
30 | on board micro controller. | ||
31 | |||
32 | To compile this driver as a module, choose M here: the module will | ||
33 | be called c2port-duramar2150. | ||
34 | |||
35 | endif # C2PORT | ||
diff --git a/drivers/misc/c2port/Makefile b/drivers/misc/c2port/Makefile new file mode 100644 index 000000000000..3b2cf43d60f5 --- /dev/null +++ b/drivers/misc/c2port/Makefile | |||
@@ -0,0 +1,3 @@ | |||
1 | obj-$(CONFIG_C2PORT) += core.o | ||
2 | |||
3 | obj-$(CONFIG_C2PORT_DURAMAR_2150) += c2port-duramar2150.o | ||
diff --git a/drivers/misc/c2port/c2port-duramar2150.c b/drivers/misc/c2port/c2port-duramar2150.c new file mode 100644 index 000000000000..338dcc121507 --- /dev/null +++ b/drivers/misc/c2port/c2port-duramar2150.c | |||
@@ -0,0 +1,158 @@ | |||
1 | /* | ||
2 | * Silicon Labs C2 port Linux support for Eurotech Duramar 2150 | ||
3 | * | ||
4 | * Copyright (c) 2008 Rodolfo Giometti <giometti@linux.it> | ||
5 | * Copyright (c) 2008 Eurotech S.p.A. <info@eurotech.it> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License version 2 as published by | ||
9 | * the Free Software Foundation | ||
10 | */ | ||
11 | |||
12 | #include <linux/errno.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/delay.h> | ||
17 | #include <linux/io.h> | ||
18 | #include <linux/c2port.h> | ||
19 | |||
20 | #define DATA_PORT 0x325 | ||
21 | #define DIR_PORT 0x326 | ||
22 | #define C2D (1 << 0) | ||
23 | #define C2CK (1 << 1) | ||
24 | |||
25 | static DEFINE_MUTEX(update_lock); | ||
26 | |||
27 | /* | ||
28 | * C2 port operations | ||
29 | */ | ||
30 | |||
31 | static void duramar2150_c2port_access(struct c2port_device *dev, int status) | ||
32 | { | ||
33 | u8 v; | ||
34 | |||
35 | mutex_lock(&update_lock); | ||
36 | |||
37 | v = inb(DIR_PORT); | ||
38 | |||
39 | /* 0 = input, 1 = output */ | ||
40 | if (status) | ||
41 | outb(v | (C2D | C2CK), DIR_PORT); | ||
42 | else | ||
43 | /* When access is "off" is important that both lines are set | ||
44 | * as inputs or hi-impedence */ | ||
45 | outb(v & ~(C2D | C2CK), DIR_PORT); | ||
46 | |||
47 | mutex_unlock(&update_lock); | ||
48 | } | ||
49 | |||
50 | static void duramar2150_c2port_c2d_dir(struct c2port_device *dev, int dir) | ||
51 | { | ||
52 | u8 v; | ||
53 | |||
54 | mutex_lock(&update_lock); | ||
55 | |||
56 | v = inb(DIR_PORT); | ||
57 | |||
58 | if (dir) | ||
59 | outb(v & ~C2D, DIR_PORT); | ||
60 | else | ||
61 | outb(v | C2D, DIR_PORT); | ||
62 | |||
63 | mutex_unlock(&update_lock); | ||
64 | } | ||
65 | |||
66 | static int duramar2150_c2port_c2d_get(struct c2port_device *dev) | ||
67 | { | ||
68 | return inb(DATA_PORT) & C2D; | ||
69 | } | ||
70 | |||
71 | static void duramar2150_c2port_c2d_set(struct c2port_device *dev, int status) | ||
72 | { | ||
73 | u8 v; | ||
74 | |||
75 | mutex_lock(&update_lock); | ||
76 | |||
77 | v = inb(DATA_PORT); | ||
78 | |||
79 | if (status) | ||
80 | outb(v | C2D, DATA_PORT); | ||
81 | else | ||
82 | outb(v & ~C2D, DATA_PORT); | ||
83 | |||
84 | mutex_unlock(&update_lock); | ||
85 | } | ||
86 | |||
87 | static void duramar2150_c2port_c2ck_set(struct c2port_device *dev, int status) | ||
88 | { | ||
89 | u8 v; | ||
90 | |||
91 | mutex_lock(&update_lock); | ||
92 | |||
93 | v = inb(DATA_PORT); | ||
94 | |||
95 | if (status) | ||
96 | outb(v | C2CK, DATA_PORT); | ||
97 | else | ||
98 | outb(v & ~C2CK, DATA_PORT); | ||
99 | |||
100 | mutex_unlock(&update_lock); | ||
101 | } | ||
102 | |||
103 | static struct c2port_ops duramar2150_c2port_ops = { | ||
104 | .block_size = 512, /* bytes */ | ||
105 | .blocks_num = 30, /* total flash size: 15360 bytes */ | ||
106 | |||
107 | .access = duramar2150_c2port_access, | ||
108 | .c2d_dir = duramar2150_c2port_c2d_dir, | ||
109 | .c2d_get = duramar2150_c2port_c2d_get, | ||
110 | .c2d_set = duramar2150_c2port_c2d_set, | ||
111 | .c2ck_set = duramar2150_c2port_c2ck_set, | ||
112 | }; | ||
113 | |||
114 | static struct c2port_device *duramar2150_c2port_dev; | ||
115 | |||
116 | /* | ||
117 | * Module stuff | ||
118 | */ | ||
119 | |||
120 | static int __init duramar2150_c2port_init(void) | ||
121 | { | ||
122 | struct resource *res; | ||
123 | int ret = 0; | ||
124 | |||
125 | res = request_region(0x325, 2, "c2port"); | ||
126 | if (!res) | ||
127 | return -EBUSY; | ||
128 | |||
129 | duramar2150_c2port_dev = c2port_device_register("uc", | ||
130 | &duramar2150_c2port_ops, NULL); | ||
131 | if (!duramar2150_c2port_dev) { | ||
132 | ret = -ENODEV; | ||
133 | goto free_region; | ||
134 | } | ||
135 | |||
136 | return 0; | ||
137 | |||
138 | free_region: | ||
139 | release_region(0x325, 2); | ||
140 | return ret; | ||
141 | } | ||
142 | |||
143 | static void __exit duramar2150_c2port_exit(void) | ||
144 | { | ||
145 | /* Setup the GPIOs as input by default (access = 0) */ | ||
146 | duramar2150_c2port_access(duramar2150_c2port_dev, 0); | ||
147 | |||
148 | c2port_device_unregister(duramar2150_c2port_dev); | ||
149 | |||
150 | release_region(0x325, 2); | ||
151 | } | ||
152 | |||
153 | module_init(duramar2150_c2port_init); | ||
154 | module_exit(duramar2150_c2port_exit); | ||
155 | |||
156 | MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); | ||
157 | MODULE_DESCRIPTION("Silicon Labs C2 port Linux support for Duramar 2150"); | ||
158 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/misc/c2port/core.c b/drivers/misc/c2port/core.c new file mode 100644 index 000000000000..976b35d1d035 --- /dev/null +++ b/drivers/misc/c2port/core.c | |||
@@ -0,0 +1,1002 @@ | |||
1 | /* | ||
2 | * Silicon Labs C2 port core Linux support | ||
3 | * | ||
4 | * Copyright (c) 2007 Rodolfo Giometti <giometti@linux.it> | ||
5 | * Copyright (c) 2007 Eurotech S.p.A. <info@eurotech.it> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License version 2 as published by | ||
9 | * the Free Software Foundation | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/device.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <linux/err.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/ctype.h> | ||
19 | #include <linux/delay.h> | ||
20 | #include <linux/idr.h> | ||
21 | |||
22 | #include <linux/c2port.h> | ||
23 | |||
24 | #define DRIVER_NAME "c2port" | ||
25 | #define DRIVER_VERSION "0.51.0" | ||
26 | |||
27 | static DEFINE_SPINLOCK(c2port_idr_lock); | ||
28 | static DEFINE_IDR(c2port_idr); | ||
29 | |||
30 | /* | ||
31 | * Local variables | ||
32 | */ | ||
33 | |||
34 | static struct class *c2port_class; | ||
35 | |||
36 | /* | ||
37 | * C2 registers & commands defines | ||
38 | */ | ||
39 | |||
40 | /* C2 registers */ | ||
41 | #define C2PORT_DEVICEID 0x00 | ||
42 | #define C2PORT_REVID 0x01 | ||
43 | #define C2PORT_FPCTL 0x02 | ||
44 | #define C2PORT_FPDAT 0xB4 | ||
45 | |||
46 | /* C2 interface commands */ | ||
47 | #define C2PORT_GET_VERSION 0x01 | ||
48 | #define C2PORT_DEVICE_ERASE 0x03 | ||
49 | #define C2PORT_BLOCK_READ 0x06 | ||
50 | #define C2PORT_BLOCK_WRITE 0x07 | ||
51 | #define C2PORT_PAGE_ERASE 0x08 | ||
52 | |||
53 | /* C2 status return codes */ | ||
54 | #define C2PORT_INVALID_COMMAND 0x00 | ||
55 | #define C2PORT_COMMAND_FAILED 0x02 | ||
56 | #define C2PORT_COMMAND_OK 0x0d | ||
57 | |||
58 | /* | ||
59 | * C2 port low level signal managements | ||
60 | */ | ||
61 | |||
62 | static void c2port_reset(struct c2port_device *dev) | ||
63 | { | ||
64 | struct c2port_ops *ops = dev->ops; | ||
65 | |||
66 | /* To reset the device we have to keep clock line low for at least | ||
67 | * 20us. | ||
68 | */ | ||
69 | local_irq_disable(); | ||
70 | ops->c2ck_set(dev, 0); | ||
71 | udelay(25); | ||
72 | ops->c2ck_set(dev, 1); | ||
73 | local_irq_enable(); | ||
74 | |||
75 | udelay(1); | ||
76 | } | ||
77 | |||
78 | static void c2port_strobe_ck(struct c2port_device *dev) | ||
79 | { | ||
80 | struct c2port_ops *ops = dev->ops; | ||
81 | |||
82 | /* During hi-low-hi transition we disable local IRQs to avoid | ||
83 | * interructions since C2 port specification says that it must be | ||
84 | * shorter than 5us, otherwise the microcontroller may consider | ||
85 | * it as a reset signal! | ||
86 | */ | ||
87 | local_irq_disable(); | ||
88 | ops->c2ck_set(dev, 0); | ||
89 | udelay(1); | ||
90 | ops->c2ck_set(dev, 1); | ||
91 | local_irq_enable(); | ||
92 | |||
93 | udelay(1); | ||
94 | } | ||
95 | |||
96 | /* | ||
97 | * C2 port basic functions | ||
98 | */ | ||
99 | |||
100 | static void c2port_write_ar(struct c2port_device *dev, u8 addr) | ||
101 | { | ||
102 | struct c2port_ops *ops = dev->ops; | ||
103 | int i; | ||
104 | |||
105 | /* START field */ | ||
106 | c2port_strobe_ck(dev); | ||
107 | |||
108 | /* INS field (11b, LSB first) */ | ||
109 | ops->c2d_dir(dev, 0); | ||
110 | ops->c2d_set(dev, 1); | ||
111 | c2port_strobe_ck(dev); | ||
112 | ops->c2d_set(dev, 1); | ||
113 | c2port_strobe_ck(dev); | ||
114 | |||
115 | /* ADDRESS field */ | ||
116 | for (i = 0; i < 8; i++) { | ||
117 | ops->c2d_set(dev, addr & 0x01); | ||
118 | c2port_strobe_ck(dev); | ||
119 | |||
120 | addr >>= 1; | ||
121 | } | ||
122 | |||
123 | /* STOP field */ | ||
124 | ops->c2d_dir(dev, 1); | ||
125 | c2port_strobe_ck(dev); | ||
126 | } | ||
127 | |||
128 | static int c2port_read_ar(struct c2port_device *dev, u8 *addr) | ||
129 | { | ||
130 | struct c2port_ops *ops = dev->ops; | ||
131 | int i; | ||
132 | |||
133 | /* START field */ | ||
134 | c2port_strobe_ck(dev); | ||
135 | |||
136 | /* INS field (10b, LSB first) */ | ||
137 | ops->c2d_dir(dev, 0); | ||
138 | ops->c2d_set(dev, 0); | ||
139 | c2port_strobe_ck(dev); | ||
140 | ops->c2d_set(dev, 1); | ||
141 | c2port_strobe_ck(dev); | ||
142 | |||
143 | /* ADDRESS field */ | ||
144 | ops->c2d_dir(dev, 1); | ||
145 | *addr = 0; | ||
146 | for (i = 0; i < 8; i++) { | ||
147 | *addr >>= 1; /* shift in 8-bit ADDRESS field LSB first */ | ||
148 | |||
149 | c2port_strobe_ck(dev); | ||
150 | if (ops->c2d_get(dev)) | ||
151 | *addr |= 0x80; | ||
152 | } | ||
153 | |||
154 | /* STOP field */ | ||
155 | c2port_strobe_ck(dev); | ||
156 | |||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | static int c2port_write_dr(struct c2port_device *dev, u8 data) | ||
161 | { | ||
162 | struct c2port_ops *ops = dev->ops; | ||
163 | int timeout, i; | ||
164 | |||
165 | /* START field */ | ||
166 | c2port_strobe_ck(dev); | ||
167 | |||
168 | /* INS field (01b, LSB first) */ | ||
169 | ops->c2d_dir(dev, 0); | ||
170 | ops->c2d_set(dev, 1); | ||
171 | c2port_strobe_ck(dev); | ||
172 | ops->c2d_set(dev, 0); | ||
173 | c2port_strobe_ck(dev); | ||
174 | |||
175 | /* LENGTH field (00b, LSB first -> 1 byte) */ | ||
176 | ops->c2d_set(dev, 0); | ||
177 | c2port_strobe_ck(dev); | ||
178 | ops->c2d_set(dev, 0); | ||
179 | c2port_strobe_ck(dev); | ||
180 | |||
181 | /* DATA field */ | ||
182 | for (i = 0; i < 8; i++) { | ||
183 | ops->c2d_set(dev, data & 0x01); | ||
184 | c2port_strobe_ck(dev); | ||
185 | |||
186 | data >>= 1; | ||
187 | } | ||
188 | |||
189 | /* WAIT field */ | ||
190 | ops->c2d_dir(dev, 1); | ||
191 | timeout = 20; | ||
192 | do { | ||
193 | c2port_strobe_ck(dev); | ||
194 | if (ops->c2d_get(dev)) | ||
195 | break; | ||
196 | |||
197 | udelay(1); | ||
198 | } while (--timeout > 0); | ||
199 | if (timeout == 0) | ||
200 | return -EIO; | ||
201 | |||
202 | /* STOP field */ | ||
203 | c2port_strobe_ck(dev); | ||
204 | |||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | static int c2port_read_dr(struct c2port_device *dev, u8 *data) | ||
209 | { | ||
210 | struct c2port_ops *ops = dev->ops; | ||
211 | int timeout, i; | ||
212 | |||
213 | /* START field */ | ||
214 | c2port_strobe_ck(dev); | ||
215 | |||
216 | /* INS field (00b, LSB first) */ | ||
217 | ops->c2d_dir(dev, 0); | ||
218 | ops->c2d_set(dev, 0); | ||
219 | c2port_strobe_ck(dev); | ||
220 | ops->c2d_set(dev, 0); | ||
221 | c2port_strobe_ck(dev); | ||
222 | |||
223 | /* LENGTH field (00b, LSB first -> 1 byte) */ | ||
224 | ops->c2d_set(dev, 0); | ||
225 | c2port_strobe_ck(dev); | ||
226 | ops->c2d_set(dev, 0); | ||
227 | c2port_strobe_ck(dev); | ||
228 | |||
229 | /* WAIT field */ | ||
230 | ops->c2d_dir(dev, 1); | ||
231 | timeout = 20; | ||
232 | do { | ||
233 | c2port_strobe_ck(dev); | ||
234 | if (ops->c2d_get(dev)) | ||
235 | break; | ||
236 | |||
237 | udelay(1); | ||
238 | } while (--timeout > 0); | ||
239 | if (timeout == 0) | ||
240 | return -EIO; | ||
241 | |||
242 | /* DATA field */ | ||
243 | *data = 0; | ||
244 | for (i = 0; i < 8; i++) { | ||
245 | *data >>= 1; /* shift in 8-bit DATA field LSB first */ | ||
246 | |||
247 | c2port_strobe_ck(dev); | ||
248 | if (ops->c2d_get(dev)) | ||
249 | *data |= 0x80; | ||
250 | } | ||
251 | |||
252 | /* STOP field */ | ||
253 | c2port_strobe_ck(dev); | ||
254 | |||
255 | return 0; | ||
256 | } | ||
257 | |||
258 | static int c2port_poll_in_busy(struct c2port_device *dev) | ||
259 | { | ||
260 | u8 addr; | ||
261 | int ret, timeout = 20; | ||
262 | |||
263 | do { | ||
264 | ret = (c2port_read_ar(dev, &addr)); | ||
265 | if (ret < 0) | ||
266 | return -EIO; | ||
267 | |||
268 | if (!(addr & 0x02)) | ||
269 | break; | ||
270 | |||
271 | udelay(1); | ||
272 | } while (--timeout > 0); | ||
273 | if (timeout == 0) | ||
274 | return -EIO; | ||
275 | |||
276 | return 0; | ||
277 | } | ||
278 | |||
279 | static int c2port_poll_out_ready(struct c2port_device *dev) | ||
280 | { | ||
281 | u8 addr; | ||
282 | int ret, timeout = 10000; /* erase flash needs long time... */ | ||
283 | |||
284 | do { | ||
285 | ret = (c2port_read_ar(dev, &addr)); | ||
286 | if (ret < 0) | ||
287 | return -EIO; | ||
288 | |||
289 | if (addr & 0x01) | ||
290 | break; | ||
291 | |||
292 | udelay(1); | ||
293 | } while (--timeout > 0); | ||
294 | if (timeout == 0) | ||
295 | return -EIO; | ||
296 | |||
297 | return 0; | ||
298 | } | ||
299 | |||
300 | /* | ||
301 | * sysfs methods | ||
302 | */ | ||
303 | |||
304 | static ssize_t c2port_show_name(struct device *dev, | ||
305 | struct device_attribute *attr, char *buf) | ||
306 | { | ||
307 | struct c2port_device *c2dev = dev_get_drvdata(dev); | ||
308 | |||
309 | return sprintf(buf, "%s\n", c2dev->name); | ||
310 | } | ||
311 | |||
312 | static ssize_t c2port_show_flash_blocks_num(struct device *dev, | ||
313 | struct device_attribute *attr, char *buf) | ||
314 | { | ||
315 | struct c2port_device *c2dev = dev_get_drvdata(dev); | ||
316 | struct c2port_ops *ops = c2dev->ops; | ||
317 | |||
318 | return sprintf(buf, "%d\n", ops->blocks_num); | ||
319 | } | ||
320 | |||
321 | static ssize_t c2port_show_flash_block_size(struct device *dev, | ||
322 | struct device_attribute *attr, char *buf) | ||
323 | { | ||
324 | struct c2port_device *c2dev = dev_get_drvdata(dev); | ||
325 | struct c2port_ops *ops = c2dev->ops; | ||
326 | |||
327 | return sprintf(buf, "%d\n", ops->block_size); | ||
328 | } | ||
329 | |||
330 | static ssize_t c2port_show_flash_size(struct device *dev, | ||
331 | struct device_attribute *attr, char *buf) | ||
332 | { | ||
333 | struct c2port_device *c2dev = dev_get_drvdata(dev); | ||
334 | struct c2port_ops *ops = c2dev->ops; | ||
335 | |||
336 | return sprintf(buf, "%d\n", ops->blocks_num * ops->block_size); | ||
337 | } | ||
338 | |||
339 | static ssize_t c2port_show_access(struct device *dev, | ||
340 | struct device_attribute *attr, char *buf) | ||
341 | { | ||
342 | struct c2port_device *c2dev = dev_get_drvdata(dev); | ||
343 | |||
344 | return sprintf(buf, "%d\n", c2dev->access); | ||
345 | } | ||
346 | |||
347 | static ssize_t c2port_store_access(struct device *dev, | ||
348 | struct device_attribute *attr, | ||
349 | const char *buf, size_t count) | ||
350 | { | ||
351 | struct c2port_device *c2dev = dev_get_drvdata(dev); | ||
352 | struct c2port_ops *ops = c2dev->ops; | ||
353 | int status, ret; | ||
354 | |||
355 | ret = sscanf(buf, "%d", &status); | ||
356 | if (ret != 1) | ||
357 | return -EINVAL; | ||
358 | |||
359 | mutex_lock(&c2dev->mutex); | ||
360 | |||
361 | c2dev->access = !!status; | ||
362 | |||
363 | /* If access is "on" clock should be HIGH _before_ setting the line | ||
364 | * as output and data line should be set as INPUT anyway */ | ||
365 | if (c2dev->access) | ||
366 | ops->c2ck_set(c2dev, 1); | ||
367 | ops->access(c2dev, c2dev->access); | ||
368 | if (c2dev->access) | ||
369 | ops->c2d_dir(c2dev, 1); | ||
370 | |||
371 | mutex_unlock(&c2dev->mutex); | ||
372 | |||
373 | return count; | ||
374 | } | ||
375 | |||
376 | static ssize_t c2port_store_reset(struct device *dev, | ||
377 | struct device_attribute *attr, | ||
378 | const char *buf, size_t count) | ||
379 | { | ||
380 | struct c2port_device *c2dev = dev_get_drvdata(dev); | ||
381 | |||
382 | /* Check the device access status */ | ||
383 | if (!c2dev->access) | ||
384 | return -EBUSY; | ||
385 | |||
386 | mutex_lock(&c2dev->mutex); | ||
387 | |||
388 | c2port_reset(c2dev); | ||
389 | c2dev->flash_access = 0; | ||
390 | |||
391 | mutex_unlock(&c2dev->mutex); | ||
392 | |||
393 | return count; | ||
394 | } | ||
395 | |||
396 | static ssize_t __c2port_show_dev_id(struct c2port_device *dev, char *buf) | ||
397 | { | ||
398 | u8 data; | ||
399 | int ret; | ||
400 | |||
401 | /* Select DEVICEID register for C2 data register accesses */ | ||
402 | c2port_write_ar(dev, C2PORT_DEVICEID); | ||
403 | |||
404 | /* Read and return the device ID register */ | ||
405 | ret = c2port_read_dr(dev, &data); | ||
406 | if (ret < 0) | ||
407 | return ret; | ||
408 | |||
409 | return sprintf(buf, "%d\n", data); | ||
410 | } | ||
411 | |||
412 | static ssize_t c2port_show_dev_id(struct device *dev, | ||
413 | struct device_attribute *attr, char *buf) | ||
414 | { | ||
415 | struct c2port_device *c2dev = dev_get_drvdata(dev); | ||
416 | ssize_t ret; | ||
417 | |||
418 | /* Check the device access status */ | ||
419 | if (!c2dev->access) | ||
420 | return -EBUSY; | ||
421 | |||
422 | mutex_lock(&c2dev->mutex); | ||
423 | ret = __c2port_show_dev_id(c2dev, buf); | ||
424 | mutex_unlock(&c2dev->mutex); | ||
425 | |||
426 | if (ret < 0) | ||
427 | dev_err(dev, "cannot read from %s\n", c2dev->name); | ||
428 | |||
429 | return ret; | ||
430 | } | ||
431 | |||
432 | static ssize_t __c2port_show_rev_id(struct c2port_device *dev, char *buf) | ||
433 | { | ||
434 | u8 data; | ||
435 | int ret; | ||
436 | |||
437 | /* Select REVID register for C2 data register accesses */ | ||
438 | c2port_write_ar(dev, C2PORT_REVID); | ||
439 | |||
440 | /* Read and return the revision ID register */ | ||
441 | ret = c2port_read_dr(dev, &data); | ||
442 | if (ret < 0) | ||
443 | return ret; | ||
444 | |||
445 | return sprintf(buf, "%d\n", data); | ||
446 | } | ||
447 | |||
448 | static ssize_t c2port_show_rev_id(struct device *dev, | ||
449 | struct device_attribute *attr, char *buf) | ||
450 | { | ||
451 | struct c2port_device *c2dev = dev_get_drvdata(dev); | ||
452 | ssize_t ret; | ||
453 | |||
454 | /* Check the device access status */ | ||
455 | if (!c2dev->access) | ||
456 | return -EBUSY; | ||
457 | |||
458 | mutex_lock(&c2dev->mutex); | ||
459 | ret = __c2port_show_rev_id(c2dev, buf); | ||
460 | mutex_unlock(&c2dev->mutex); | ||
461 | |||
462 | if (ret < 0) | ||
463 | dev_err(c2dev->dev, "cannot read from %s\n", c2dev->name); | ||
464 | |||
465 | return ret; | ||
466 | } | ||
467 | |||
468 | static ssize_t c2port_show_flash_access(struct device *dev, | ||
469 | struct device_attribute *attr, char *buf) | ||
470 | { | ||
471 | struct c2port_device *c2dev = dev_get_drvdata(dev); | ||
472 | |||
473 | return sprintf(buf, "%d\n", c2dev->flash_access); | ||
474 | } | ||
475 | |||
476 | static ssize_t __c2port_store_flash_access(struct c2port_device *dev, | ||
477 | int status) | ||
478 | { | ||
479 | int ret; | ||
480 | |||
481 | /* Check the device access status */ | ||
482 | if (!dev->access) | ||
483 | return -EBUSY; | ||
484 | |||
485 | dev->flash_access = !!status; | ||
486 | |||
487 | /* If flash_access is off we have nothing to do... */ | ||
488 | if (dev->flash_access == 0) | ||
489 | return 0; | ||
490 | |||
491 | /* Target the C2 flash programming control register for C2 data | ||
492 | * register access */ | ||
493 | c2port_write_ar(dev, C2PORT_FPCTL); | ||
494 | |||
495 | /* Write the first keycode to enable C2 Flash programming */ | ||
496 | ret = c2port_write_dr(dev, 0x02); | ||
497 | if (ret < 0) | ||
498 | return ret; | ||
499 | |||
500 | /* Write the second keycode to enable C2 Flash programming */ | ||
501 | ret = c2port_write_dr(dev, 0x01); | ||
502 | if (ret < 0) | ||
503 | return ret; | ||
504 | |||
505 | /* Delay for at least 20ms to ensure the target is ready for | ||
506 | * C2 flash programming */ | ||
507 | mdelay(25); | ||
508 | |||
509 | return 0; | ||
510 | } | ||
511 | |||
512 | static ssize_t c2port_store_flash_access(struct device *dev, | ||
513 | struct device_attribute *attr, | ||
514 | const char *buf, size_t count) | ||
515 | { | ||
516 | struct c2port_device *c2dev = dev_get_drvdata(dev); | ||
517 | int status; | ||
518 | ssize_t ret; | ||
519 | |||
520 | ret = sscanf(buf, "%d", &status); | ||
521 | if (ret != 1) | ||
522 | return -EINVAL; | ||
523 | |||
524 | mutex_lock(&c2dev->mutex); | ||
525 | ret = __c2port_store_flash_access(c2dev, status); | ||
526 | mutex_unlock(&c2dev->mutex); | ||
527 | |||
528 | if (ret < 0) { | ||
529 | dev_err(c2dev->dev, "cannot enable %s flash programming\n", | ||
530 | c2dev->name); | ||
531 | return ret; | ||
532 | } | ||
533 | |||
534 | return count; | ||
535 | } | ||
536 | |||
537 | static ssize_t __c2port_write_flash_erase(struct c2port_device *dev) | ||
538 | { | ||
539 | u8 status; | ||
540 | int ret; | ||
541 | |||
542 | /* Target the C2 flash programming data register for C2 data register | ||
543 | * access. | ||
544 | */ | ||
545 | c2port_write_ar(dev, C2PORT_FPDAT); | ||
546 | |||
547 | /* Send device erase command */ | ||
548 | c2port_write_dr(dev, C2PORT_DEVICE_ERASE); | ||
549 | |||
550 | /* Wait for input acknowledge */ | ||
551 | ret = c2port_poll_in_busy(dev); | ||
552 | if (ret < 0) | ||
553 | return ret; | ||
554 | |||
555 | /* Should check status before starting FLASH access sequence */ | ||
556 | |||
557 | /* Wait for status information */ | ||
558 | ret = c2port_poll_out_ready(dev); | ||
559 | if (ret < 0) | ||
560 | return ret; | ||
561 | |||
562 | /* Read flash programming interface status */ | ||
563 | ret = c2port_read_dr(dev, &status); | ||
564 | if (ret < 0) | ||
565 | return ret; | ||
566 | if (status != C2PORT_COMMAND_OK) | ||
567 | return -EBUSY; | ||
568 | |||
569 | /* Send a three-byte arming sequence to enable the device erase. | ||
570 | * If the sequence is not received correctly, the command will be | ||
571 | * ignored. | ||
572 | * Sequence is: 0xde, 0xad, 0xa5. | ||
573 | */ | ||
574 | c2port_write_dr(dev, 0xde); | ||
575 | ret = c2port_poll_in_busy(dev); | ||
576 | if (ret < 0) | ||
577 | return ret; | ||
578 | c2port_write_dr(dev, 0xad); | ||
579 | ret = c2port_poll_in_busy(dev); | ||
580 | if (ret < 0) | ||
581 | return ret; | ||
582 | c2port_write_dr(dev, 0xa5); | ||
583 | ret = c2port_poll_in_busy(dev); | ||
584 | if (ret < 0) | ||
585 | return ret; | ||
586 | |||
587 | ret = c2port_poll_out_ready(dev); | ||
588 | if (ret < 0) | ||
589 | return ret; | ||
590 | |||
591 | return 0; | ||
592 | } | ||
593 | |||
594 | static ssize_t c2port_store_flash_erase(struct device *dev, | ||
595 | struct device_attribute *attr, | ||
596 | const char *buf, size_t count) | ||
597 | { | ||
598 | struct c2port_device *c2dev = dev_get_drvdata(dev); | ||
599 | int ret; | ||
600 | |||
601 | /* Check the device and flash access status */ | ||
602 | if (!c2dev->access || !c2dev->flash_access) | ||
603 | return -EBUSY; | ||
604 | |||
605 | mutex_lock(&c2dev->mutex); | ||
606 | ret = __c2port_write_flash_erase(c2dev); | ||
607 | mutex_unlock(&c2dev->mutex); | ||
608 | |||
609 | if (ret < 0) { | ||
610 | dev_err(c2dev->dev, "cannot erase %s flash\n", c2dev->name); | ||
611 | return ret; | ||
612 | } | ||
613 | |||
614 | return count; | ||
615 | } | ||
616 | |||
617 | static ssize_t __c2port_read_flash_data(struct c2port_device *dev, | ||
618 | char *buffer, loff_t offset, size_t count) | ||
619 | { | ||
620 | struct c2port_ops *ops = dev->ops; | ||
621 | u8 status, nread = 128; | ||
622 | int i, ret; | ||
623 | |||
624 | /* Check for flash end */ | ||
625 | if (offset >= ops->block_size * ops->blocks_num) | ||
626 | return 0; | ||
627 | |||
628 | if (ops->block_size * ops->blocks_num - offset < nread) | ||
629 | nread = ops->block_size * ops->blocks_num - offset; | ||
630 | if (count < nread) | ||
631 | nread = count; | ||
632 | if (nread == 0) | ||
633 | return nread; | ||
634 | |||
635 | /* Target the C2 flash programming data register for C2 data register | ||
636 | * access */ | ||
637 | c2port_write_ar(dev, C2PORT_FPDAT); | ||
638 | |||
639 | /* Send flash block read command */ | ||
640 | c2port_write_dr(dev, C2PORT_BLOCK_READ); | ||
641 | |||
642 | /* Wait for input acknowledge */ | ||
643 | ret = c2port_poll_in_busy(dev); | ||
644 | if (ret < 0) | ||
645 | return ret; | ||
646 | |||
647 | /* Should check status before starting FLASH access sequence */ | ||
648 | |||
649 | /* Wait for status information */ | ||
650 | ret = c2port_poll_out_ready(dev); | ||
651 | if (ret < 0) | ||
652 | return ret; | ||
653 | |||
654 | /* Read flash programming interface status */ | ||
655 | ret = c2port_read_dr(dev, &status); | ||
656 | if (ret < 0) | ||
657 | return ret; | ||
658 | if (status != C2PORT_COMMAND_OK) | ||
659 | return -EBUSY; | ||
660 | |||
661 | /* Send address high byte */ | ||
662 | c2port_write_dr(dev, offset >> 8); | ||
663 | ret = c2port_poll_in_busy(dev); | ||
664 | if (ret < 0) | ||
665 | return ret; | ||
666 | |||
667 | /* Send address low byte */ | ||
668 | c2port_write_dr(dev, offset & 0x00ff); | ||
669 | ret = c2port_poll_in_busy(dev); | ||
670 | if (ret < 0) | ||
671 | return ret; | ||
672 | |||
673 | /* Send address block size */ | ||
674 | c2port_write_dr(dev, nread); | ||
675 | ret = c2port_poll_in_busy(dev); | ||
676 | if (ret < 0) | ||
677 | return ret; | ||
678 | |||
679 | /* Should check status before reading FLASH block */ | ||
680 | |||
681 | /* Wait for status information */ | ||
682 | ret = c2port_poll_out_ready(dev); | ||
683 | if (ret < 0) | ||
684 | return ret; | ||
685 | |||
686 | /* Read flash programming interface status */ | ||
687 | ret = c2port_read_dr(dev, &status); | ||
688 | if (ret < 0) | ||
689 | return ret; | ||
690 | if (status != C2PORT_COMMAND_OK) | ||
691 | return -EBUSY; | ||
692 | |||
693 | /* Read flash block */ | ||
694 | for (i = 0; i < nread; i++) { | ||
695 | ret = c2port_poll_out_ready(dev); | ||
696 | if (ret < 0) | ||
697 | return ret; | ||
698 | |||
699 | ret = c2port_read_dr(dev, buffer+i); | ||
700 | if (ret < 0) | ||
701 | return ret; | ||
702 | } | ||
703 | |||
704 | return nread; | ||
705 | } | ||
706 | |||
707 | static ssize_t c2port_read_flash_data(struct kobject *kobj, | ||
708 | struct bin_attribute *attr, | ||
709 | char *buffer, loff_t offset, size_t count) | ||
710 | { | ||
711 | struct c2port_device *c2dev = | ||
712 | dev_get_drvdata(container_of(kobj, | ||
713 | struct device, kobj)); | ||
714 | ssize_t ret; | ||
715 | |||
716 | /* Check the device and flash access status */ | ||
717 | if (!c2dev->access || !c2dev->flash_access) | ||
718 | return -EBUSY; | ||
719 | |||
720 | mutex_lock(&c2dev->mutex); | ||
721 | ret = __c2port_read_flash_data(c2dev, buffer, offset, count); | ||
722 | mutex_unlock(&c2dev->mutex); | ||
723 | |||
724 | if (ret < 0) | ||
725 | dev_err(c2dev->dev, "cannot read %s flash\n", c2dev->name); | ||
726 | |||
727 | return ret; | ||
728 | } | ||
729 | |||
730 | static ssize_t __c2port_write_flash_data(struct c2port_device *dev, | ||
731 | char *buffer, loff_t offset, size_t count) | ||
732 | { | ||
733 | struct c2port_ops *ops = dev->ops; | ||
734 | u8 status, nwrite = 128; | ||
735 | int i, ret; | ||
736 | |||
737 | if (nwrite > count) | ||
738 | nwrite = count; | ||
739 | if (ops->block_size * ops->blocks_num - offset < nwrite) | ||
740 | nwrite = ops->block_size * ops->blocks_num - offset; | ||
741 | |||
742 | /* Check for flash end */ | ||
743 | if (offset >= ops->block_size * ops->blocks_num) | ||
744 | return -EINVAL; | ||
745 | |||
746 | /* Target the C2 flash programming data register for C2 data register | ||
747 | * access */ | ||
748 | c2port_write_ar(dev, C2PORT_FPDAT); | ||
749 | |||
750 | /* Send flash block write command */ | ||
751 | c2port_write_dr(dev, C2PORT_BLOCK_WRITE); | ||
752 | |||
753 | /* Wait for input acknowledge */ | ||
754 | ret = c2port_poll_in_busy(dev); | ||
755 | if (ret < 0) | ||
756 | return ret; | ||
757 | |||
758 | /* Should check status before starting FLASH access sequence */ | ||
759 | |||
760 | /* Wait for status information */ | ||
761 | ret = c2port_poll_out_ready(dev); | ||
762 | if (ret < 0) | ||
763 | return ret; | ||
764 | |||
765 | /* Read flash programming interface status */ | ||
766 | ret = c2port_read_dr(dev, &status); | ||
767 | if (ret < 0) | ||
768 | return ret; | ||
769 | if (status != C2PORT_COMMAND_OK) | ||
770 | return -EBUSY; | ||
771 | |||
772 | /* Send address high byte */ | ||
773 | c2port_write_dr(dev, offset >> 8); | ||
774 | ret = c2port_poll_in_busy(dev); | ||
775 | if (ret < 0) | ||
776 | return ret; | ||
777 | |||
778 | /* Send address low byte */ | ||
779 | c2port_write_dr(dev, offset & 0x00ff); | ||
780 | ret = c2port_poll_in_busy(dev); | ||
781 | if (ret < 0) | ||
782 | return ret; | ||
783 | |||
784 | /* Send address block size */ | ||
785 | c2port_write_dr(dev, nwrite); | ||
786 | ret = c2port_poll_in_busy(dev); | ||
787 | if (ret < 0) | ||
788 | return ret; | ||
789 | |||
790 | /* Should check status before writing FLASH block */ | ||
791 | |||
792 | /* Wait for status information */ | ||
793 | ret = c2port_poll_out_ready(dev); | ||
794 | if (ret < 0) | ||
795 | return ret; | ||
796 | |||
797 | /* Read flash programming interface status */ | ||
798 | ret = c2port_read_dr(dev, &status); | ||
799 | if (ret < 0) | ||
800 | return ret; | ||
801 | if (status != C2PORT_COMMAND_OK) | ||
802 | return -EBUSY; | ||
803 | |||
804 | /* Write flash block */ | ||
805 | for (i = 0; i < nwrite; i++) { | ||
806 | ret = c2port_write_dr(dev, *(buffer+i)); | ||
807 | if (ret < 0) | ||
808 | return ret; | ||
809 | |||
810 | ret = c2port_poll_in_busy(dev); | ||
811 | if (ret < 0) | ||
812 | return ret; | ||
813 | |||
814 | } | ||
815 | |||
816 | /* Wait for last flash write to complete */ | ||
817 | ret = c2port_poll_out_ready(dev); | ||
818 | if (ret < 0) | ||
819 | return ret; | ||
820 | |||
821 | return nwrite; | ||
822 | } | ||
823 | |||
824 | static ssize_t c2port_write_flash_data(struct kobject *kobj, | ||
825 | struct bin_attribute *attr, | ||
826 | char *buffer, loff_t offset, size_t count) | ||
827 | { | ||
828 | struct c2port_device *c2dev = | ||
829 | dev_get_drvdata(container_of(kobj, | ||
830 | struct device, kobj)); | ||
831 | int ret; | ||
832 | |||
833 | /* Check the device access status */ | ||
834 | if (!c2dev->access || !c2dev->flash_access) | ||
835 | return -EBUSY; | ||
836 | |||
837 | mutex_lock(&c2dev->mutex); | ||
838 | ret = __c2port_write_flash_data(c2dev, buffer, offset, count); | ||
839 | mutex_unlock(&c2dev->mutex); | ||
840 | |||
841 | if (ret < 0) | ||
842 | dev_err(c2dev->dev, "cannot write %s flash\n", c2dev->name); | ||
843 | |||
844 | return ret; | ||
845 | } | ||
846 | |||
847 | /* | ||
848 | * Class attributes | ||
849 | */ | ||
850 | |||
851 | static struct device_attribute c2port_attrs[] = { | ||
852 | __ATTR(name, 0444, c2port_show_name, NULL), | ||
853 | __ATTR(flash_blocks_num, 0444, c2port_show_flash_blocks_num, NULL), | ||
854 | __ATTR(flash_block_size, 0444, c2port_show_flash_block_size, NULL), | ||
855 | __ATTR(flash_size, 0444, c2port_show_flash_size, NULL), | ||
856 | __ATTR(access, 0644, c2port_show_access, c2port_store_access), | ||
857 | __ATTR(reset, 0200, NULL, c2port_store_reset), | ||
858 | __ATTR(dev_id, 0444, c2port_show_dev_id, NULL), | ||
859 | __ATTR(rev_id, 0444, c2port_show_rev_id, NULL), | ||
860 | |||
861 | __ATTR(flash_access, 0644, c2port_show_flash_access, | ||
862 | c2port_store_flash_access), | ||
863 | __ATTR(flash_erase, 0200, NULL, c2port_store_flash_erase), | ||
864 | __ATTR_NULL, | ||
865 | }; | ||
866 | |||
867 | static struct bin_attribute c2port_bin_attrs = { | ||
868 | .attr = { | ||
869 | .name = "flash_data", | ||
870 | .mode = 0644 | ||
871 | }, | ||
872 | .read = c2port_read_flash_data, | ||
873 | .write = c2port_write_flash_data, | ||
874 | /* .size is computed at run-time */ | ||
875 | }; | ||
876 | |||
877 | /* | ||
878 | * Exported functions | ||
879 | */ | ||
880 | |||
881 | struct c2port_device *c2port_device_register(char *name, | ||
882 | struct c2port_ops *ops, void *devdata) | ||
883 | { | ||
884 | struct c2port_device *c2dev; | ||
885 | int id, ret; | ||
886 | |||
887 | if (unlikely(!ops) || unlikely(!ops->access) || \ | ||
888 | unlikely(!ops->c2d_dir) || unlikely(!ops->c2ck_set) || \ | ||
889 | unlikely(!ops->c2d_get) || unlikely(!ops->c2d_set)) | ||
890 | return ERR_PTR(-EINVAL); | ||
891 | |||
892 | c2dev = kmalloc(sizeof(struct c2port_device), GFP_KERNEL); | ||
893 | if (unlikely(!c2dev)) | ||
894 | return ERR_PTR(-ENOMEM); | ||
895 | |||
896 | ret = idr_pre_get(&c2port_idr, GFP_KERNEL); | ||
897 | if (!ret) { | ||
898 | ret = -ENOMEM; | ||
899 | goto error_idr_get_new; | ||
900 | } | ||
901 | |||
902 | spin_lock_irq(&c2port_idr_lock); | ||
903 | ret = idr_get_new(&c2port_idr, c2dev, &id); | ||
904 | spin_unlock_irq(&c2port_idr_lock); | ||
905 | |||
906 | if (ret < 0) | ||
907 | goto error_idr_get_new; | ||
908 | c2dev->id = id; | ||
909 | |||
910 | c2dev->dev = device_create(c2port_class, NULL, 0, c2dev, | ||
911 | "c2port%d", id); | ||
912 | if (unlikely(!c2dev->dev)) { | ||
913 | ret = -ENOMEM; | ||
914 | goto error_device_create; | ||
915 | } | ||
916 | dev_set_drvdata(c2dev->dev, c2dev); | ||
917 | |||
918 | strncpy(c2dev->name, name, C2PORT_NAME_LEN); | ||
919 | c2dev->ops = ops; | ||
920 | mutex_init(&c2dev->mutex); | ||
921 | |||
922 | /* Create binary file */ | ||
923 | c2port_bin_attrs.size = ops->blocks_num * ops->block_size; | ||
924 | ret = device_create_bin_file(c2dev->dev, &c2port_bin_attrs); | ||
925 | if (unlikely(ret)) | ||
926 | goto error_device_create_bin_file; | ||
927 | |||
928 | /* By default C2 port access is off */ | ||
929 | c2dev->access = c2dev->flash_access = 0; | ||
930 | ops->access(c2dev, 0); | ||
931 | |||
932 | dev_info(c2dev->dev, "C2 port %s added\n", name); | ||
933 | dev_info(c2dev->dev, "%s flash has %d blocks x %d bytes " | ||
934 | "(%d bytes total)\n", | ||
935 | name, ops->blocks_num, ops->block_size, | ||
936 | ops->blocks_num * ops->block_size); | ||
937 | |||
938 | return c2dev; | ||
939 | |||
940 | error_device_create_bin_file: | ||
941 | device_destroy(c2port_class, 0); | ||
942 | |||
943 | error_device_create: | ||
944 | spin_lock_irq(&c2port_idr_lock); | ||
945 | idr_remove(&c2port_idr, id); | ||
946 | spin_unlock_irq(&c2port_idr_lock); | ||
947 | |||
948 | error_idr_get_new: | ||
949 | kfree(c2dev); | ||
950 | |||
951 | return ERR_PTR(ret); | ||
952 | } | ||
953 | EXPORT_SYMBOL(c2port_device_register); | ||
954 | |||
955 | void c2port_device_unregister(struct c2port_device *c2dev) | ||
956 | { | ||
957 | if (!c2dev) | ||
958 | return; | ||
959 | |||
960 | dev_info(c2dev->dev, "C2 port %s removed\n", c2dev->name); | ||
961 | |||
962 | device_remove_bin_file(c2dev->dev, &c2port_bin_attrs); | ||
963 | spin_lock_irq(&c2port_idr_lock); | ||
964 | idr_remove(&c2port_idr, c2dev->id); | ||
965 | spin_unlock_irq(&c2port_idr_lock); | ||
966 | |||
967 | device_destroy(c2port_class, c2dev->id); | ||
968 | |||
969 | kfree(c2dev); | ||
970 | } | ||
971 | EXPORT_SYMBOL(c2port_device_unregister); | ||
972 | |||
973 | /* | ||
974 | * Module stuff | ||
975 | */ | ||
976 | |||
977 | static int __init c2port_init(void) | ||
978 | { | ||
979 | printk(KERN_INFO "Silicon Labs C2 port support v. " DRIVER_VERSION | ||
980 | " - (C) 2007 Rodolfo Giometti\n"); | ||
981 | |||
982 | c2port_class = class_create(THIS_MODULE, "c2port"); | ||
983 | if (!c2port_class) { | ||
984 | printk(KERN_ERR "c2port: failed to allocate class\n"); | ||
985 | return -ENOMEM; | ||
986 | } | ||
987 | c2port_class->dev_attrs = c2port_attrs; | ||
988 | |||
989 | return 0; | ||
990 | } | ||
991 | |||
992 | static void __exit c2port_exit(void) | ||
993 | { | ||
994 | class_destroy(c2port_class); | ||
995 | } | ||
996 | |||
997 | module_init(c2port_init); | ||
998 | module_exit(c2port_exit); | ||
999 | |||
1000 | MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); | ||
1001 | MODULE_DESCRIPTION("Silicon Labs C2 port support v. " DRIVER_VERSION); | ||
1002 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/misc/compal-laptop.c b/drivers/misc/compal-laptop.c index 344b790a6253..11003bba10d3 100644 --- a/drivers/misc/compal-laptop.c +++ b/drivers/misc/compal-laptop.c | |||
@@ -326,12 +326,14 @@ static int __init compal_init(void) | |||
326 | 326 | ||
327 | /* Register backlight stuff */ | 327 | /* Register backlight stuff */ |
328 | 328 | ||
329 | compalbl_device = backlight_device_register("compal-laptop", NULL, NULL, | 329 | if (!acpi_video_backlight_support()) { |
330 | &compalbl_ops); | 330 | compalbl_device = backlight_device_register("compal-laptop", NULL, NULL, |
331 | if (IS_ERR(compalbl_device)) | 331 | &compalbl_ops); |
332 | return PTR_ERR(compalbl_device); | 332 | if (IS_ERR(compalbl_device)) |
333 | return PTR_ERR(compalbl_device); | ||
333 | 334 | ||
334 | compalbl_device->props.max_brightness = COMPAL_LCD_LEVEL_MAX-1; | 335 | compalbl_device->props.max_brightness = COMPAL_LCD_LEVEL_MAX-1; |
336 | } | ||
335 | 337 | ||
336 | ret = platform_driver_register(&compal_driver); | 338 | ret = platform_driver_register(&compal_driver); |
337 | if (ret) | 339 | if (ret) |
diff --git a/drivers/misc/eeepc-laptop.c b/drivers/misc/eeepc-laptop.c index 9ef98b2d5039..02fe2b8b8939 100644 --- a/drivers/misc/eeepc-laptop.c +++ b/drivers/misc/eeepc-laptop.c | |||
@@ -825,9 +825,15 @@ static int __init eeepc_laptop_init(void) | |||
825 | return -ENODEV; | 825 | return -ENODEV; |
826 | } | 826 | } |
827 | dev = acpi_get_physical_device(ehotk->device->handle); | 827 | dev = acpi_get_physical_device(ehotk->device->handle); |
828 | result = eeepc_backlight_init(dev); | 828 | |
829 | if (result) | 829 | if (!acpi_video_backlight_support()) { |
830 | goto fail_backlight; | 830 | result = eeepc_backlight_init(dev); |
831 | if (result) | ||
832 | goto fail_backlight; | ||
833 | } else | ||
834 | printk(EEEPC_INFO "Backlight controlled by ACPI video " | ||
835 | "driver\n"); | ||
836 | |||
831 | result = eeepc_hwmon_init(dev); | 837 | result = eeepc_hwmon_init(dev); |
832 | if (result) | 838 | if (result) |
833 | goto fail_hwmon; | 839 | goto fail_hwmon; |
diff --git a/drivers/misc/fujitsu-laptop.c b/drivers/misc/fujitsu-laptop.c index d2cf0bfe3163..a7dd3e9fb79d 100644 --- a/drivers/misc/fujitsu-laptop.c +++ b/drivers/misc/fujitsu-laptop.c | |||
@@ -464,6 +464,14 @@ static int dmi_check_cb_s6410(const struct dmi_system_id *id) | |||
464 | return 0; | 464 | return 0; |
465 | } | 465 | } |
466 | 466 | ||
467 | static int dmi_check_cb_s6420(const struct dmi_system_id *id) | ||
468 | { | ||
469 | dmi_check_cb_common(id); | ||
470 | fujitsu->keycode1 = KEY_SCREENLOCK; /* "Lock" */ | ||
471 | fujitsu->keycode2 = KEY_HELP; /* "Mobility Center" */ | ||
472 | return 0; | ||
473 | } | ||
474 | |||
467 | static int dmi_check_cb_p8010(const struct dmi_system_id *id) | 475 | static int dmi_check_cb_p8010(const struct dmi_system_id *id) |
468 | { | 476 | { |
469 | dmi_check_cb_common(id); | 477 | dmi_check_cb_common(id); |
@@ -473,7 +481,7 @@ static int dmi_check_cb_p8010(const struct dmi_system_id *id) | |||
473 | return 0; | 481 | return 0; |
474 | } | 482 | } |
475 | 483 | ||
476 | static struct dmi_system_id __initdata fujitsu_dmi_table[] = { | 484 | static struct dmi_system_id fujitsu_dmi_table[] = { |
477 | { | 485 | { |
478 | .ident = "Fujitsu Siemens S6410", | 486 | .ident = "Fujitsu Siemens S6410", |
479 | .matches = { | 487 | .matches = { |
@@ -482,6 +490,13 @@ static struct dmi_system_id __initdata fujitsu_dmi_table[] = { | |||
482 | }, | 490 | }, |
483 | .callback = dmi_check_cb_s6410}, | 491 | .callback = dmi_check_cb_s6410}, |
484 | { | 492 | { |
493 | .ident = "Fujitsu Siemens S6420", | ||
494 | .matches = { | ||
495 | DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), | ||
496 | DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6420"), | ||
497 | }, | ||
498 | .callback = dmi_check_cb_s6420}, | ||
499 | { | ||
485 | .ident = "Fujitsu LifeBook P8010", | 500 | .ident = "Fujitsu LifeBook P8010", |
486 | .matches = { | 501 | .matches = { |
487 | DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), | 502 | DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), |
@@ -990,16 +1005,16 @@ static int __init fujitsu_init(void) | |||
990 | 1005 | ||
991 | /* Register backlight stuff */ | 1006 | /* Register backlight stuff */ |
992 | 1007 | ||
993 | fujitsu->bl_device = | 1008 | if (!acpi_video_backlight_support()) { |
994 | backlight_device_register("fujitsu-laptop", NULL, NULL, | 1009 | fujitsu->bl_device = |
995 | &fujitsubl_ops); | 1010 | backlight_device_register("fujitsu-laptop", NULL, NULL, |
996 | if (IS_ERR(fujitsu->bl_device)) | 1011 | &fujitsubl_ops); |
997 | return PTR_ERR(fujitsu->bl_device); | 1012 | if (IS_ERR(fujitsu->bl_device)) |
998 | 1013 | return PTR_ERR(fujitsu->bl_device); | |
999 | max_brightness = fujitsu->max_brightness; | 1014 | max_brightness = fujitsu->max_brightness; |
1000 | 1015 | fujitsu->bl_device->props.max_brightness = max_brightness - 1; | |
1001 | fujitsu->bl_device->props.max_brightness = max_brightness - 1; | 1016 | fujitsu->bl_device->props.brightness = fujitsu->brightness_level; |
1002 | fujitsu->bl_device->props.brightness = fujitsu->brightness_level; | 1017 | } |
1003 | 1018 | ||
1004 | ret = platform_driver_register(&fujitsupf_driver); | 1019 | ret = platform_driver_register(&fujitsupf_driver); |
1005 | if (ret) | 1020 | if (ret) |
@@ -1035,7 +1050,8 @@ fail_hotkey: | |||
1035 | 1050 | ||
1036 | fail_backlight: | 1051 | fail_backlight: |
1037 | 1052 | ||
1038 | backlight_device_unregister(fujitsu->bl_device); | 1053 | if (fujitsu->bl_device) |
1054 | backlight_device_unregister(fujitsu->bl_device); | ||
1039 | 1055 | ||
1040 | fail_platform_device2: | 1056 | fail_platform_device2: |
1041 | 1057 | ||
@@ -1062,7 +1078,8 @@ static void __exit fujitsu_cleanup(void) | |||
1062 | &fujitsupf_attribute_group); | 1078 | &fujitsupf_attribute_group); |
1063 | platform_device_unregister(fujitsu->pf_device); | 1079 | platform_device_unregister(fujitsu->pf_device); |
1064 | platform_driver_unregister(&fujitsupf_driver); | 1080 | platform_driver_unregister(&fujitsupf_driver); |
1065 | backlight_device_unregister(fujitsu->bl_device); | 1081 | if (fujitsu->bl_device) |
1082 | backlight_device_unregister(fujitsu->bl_device); | ||
1066 | 1083 | ||
1067 | acpi_bus_unregister_driver(&acpi_fujitsu_driver); | 1084 | acpi_bus_unregister_driver(&acpi_fujitsu_driver); |
1068 | 1085 | ||
diff --git a/drivers/misc/ics932s401.c b/drivers/misc/ics932s401.c new file mode 100644 index 000000000000..6e43ab4231ae --- /dev/null +++ b/drivers/misc/ics932s401.c | |||
@@ -0,0 +1,515 @@ | |||
1 | /* | ||
2 | * A driver for the Integrated Circuits ICS932S401 | ||
3 | * Copyright (C) 2008 IBM | ||
4 | * | ||
5 | * Author: Darrick J. Wong <djwong@us.ibm.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | */ | ||
21 | |||
22 | #include <linux/module.h> | ||
23 | #include <linux/jiffies.h> | ||
24 | #include <linux/i2c.h> | ||
25 | #include <linux/err.h> | ||
26 | #include <linux/mutex.h> | ||
27 | #include <linux/delay.h> | ||
28 | #include <linux/log2.h> | ||
29 | |||
30 | /* Addresses to scan */ | ||
31 | static const unsigned short normal_i2c[] = { 0x69, I2C_CLIENT_END }; | ||
32 | |||
33 | /* Insmod parameters */ | ||
34 | I2C_CLIENT_INSMOD_1(ics932s401); | ||
35 | |||
36 | /* ICS932S401 registers */ | ||
37 | #define ICS932S401_REG_CFG2 0x01 | ||
38 | #define ICS932S401_CFG1_SPREAD 0x01 | ||
39 | #define ICS932S401_REG_CFG7 0x06 | ||
40 | #define ICS932S401_FS_MASK 0x07 | ||
41 | #define ICS932S401_REG_VENDOR_REV 0x07 | ||
42 | #define ICS932S401_VENDOR 1 | ||
43 | #define ICS932S401_VENDOR_MASK 0x0F | ||
44 | #define ICS932S401_REV 4 | ||
45 | #define ICS932S401_REV_SHIFT 4 | ||
46 | #define ICS932S401_REG_DEVICE 0x09 | ||
47 | #define ICS932S401_DEVICE 11 | ||
48 | #define ICS932S401_REG_CTRL 0x0A | ||
49 | #define ICS932S401_MN_ENABLED 0x80 | ||
50 | #define ICS932S401_CPU_ALT 0x04 | ||
51 | #define ICS932S401_SRC_ALT 0x08 | ||
52 | #define ICS932S401_REG_CPU_M_CTRL 0x0B | ||
53 | #define ICS932S401_M_MASK 0x3F | ||
54 | #define ICS932S401_REG_CPU_N_CTRL 0x0C | ||
55 | #define ICS932S401_REG_CPU_SPREAD1 0x0D | ||
56 | #define ICS932S401_REG_CPU_SPREAD2 0x0E | ||
57 | #define ICS932S401_SPREAD_MASK 0x7FFF | ||
58 | #define ICS932S401_REG_SRC_M_CTRL 0x0F | ||
59 | #define ICS932S401_REG_SRC_N_CTRL 0x10 | ||
60 | #define ICS932S401_REG_SRC_SPREAD1 0x11 | ||
61 | #define ICS932S401_REG_SRC_SPREAD2 0x12 | ||
62 | #define ICS932S401_REG_CPU_DIVISOR 0x13 | ||
63 | #define ICS932S401_CPU_DIVISOR_SHIFT 4 | ||
64 | #define ICS932S401_REG_PCISRC_DIVISOR 0x14 | ||
65 | #define ICS932S401_SRC_DIVISOR_MASK 0x0F | ||
66 | #define ICS932S401_PCI_DIVISOR_SHIFT 4 | ||
67 | |||
68 | /* Base clock is 14.318MHz */ | ||
69 | #define BASE_CLOCK 14318 | ||
70 | |||
71 | #define NUM_REGS 21 | ||
72 | #define NUM_MIRRORED_REGS 15 | ||
73 | |||
74 | static int regs_to_copy[NUM_MIRRORED_REGS] = { | ||
75 | ICS932S401_REG_CFG2, | ||
76 | ICS932S401_REG_CFG7, | ||
77 | ICS932S401_REG_VENDOR_REV, | ||
78 | ICS932S401_REG_DEVICE, | ||
79 | ICS932S401_REG_CTRL, | ||
80 | ICS932S401_REG_CPU_M_CTRL, | ||
81 | ICS932S401_REG_CPU_N_CTRL, | ||
82 | ICS932S401_REG_CPU_SPREAD1, | ||
83 | ICS932S401_REG_CPU_SPREAD2, | ||
84 | ICS932S401_REG_SRC_M_CTRL, | ||
85 | ICS932S401_REG_SRC_N_CTRL, | ||
86 | ICS932S401_REG_SRC_SPREAD1, | ||
87 | ICS932S401_REG_SRC_SPREAD2, | ||
88 | ICS932S401_REG_CPU_DIVISOR, | ||
89 | ICS932S401_REG_PCISRC_DIVISOR, | ||
90 | }; | ||
91 | |||
92 | /* How often do we reread sensors values? (In jiffies) */ | ||
93 | #define SENSOR_REFRESH_INTERVAL (2 * HZ) | ||
94 | |||
95 | /* How often do we reread sensor limit values? (In jiffies) */ | ||
96 | #define LIMIT_REFRESH_INTERVAL (60 * HZ) | ||
97 | |||
98 | struct ics932s401_data { | ||
99 | struct attribute_group attrs; | ||
100 | struct mutex lock; | ||
101 | char sensors_valid; | ||
102 | unsigned long sensors_last_updated; /* In jiffies */ | ||
103 | |||
104 | u8 regs[NUM_REGS]; | ||
105 | }; | ||
106 | |||
107 | static int ics932s401_probe(struct i2c_client *client, | ||
108 | const struct i2c_device_id *id); | ||
109 | static int ics932s401_detect(struct i2c_client *client, int kind, | ||
110 | struct i2c_board_info *info); | ||
111 | static int ics932s401_remove(struct i2c_client *client); | ||
112 | |||
113 | static const struct i2c_device_id ics932s401_id[] = { | ||
114 | { "ics932s401", ics932s401 }, | ||
115 | { } | ||
116 | }; | ||
117 | MODULE_DEVICE_TABLE(i2c, ics932s401_id); | ||
118 | |||
119 | static struct i2c_driver ics932s401_driver = { | ||
120 | .class = I2C_CLASS_HWMON, | ||
121 | .driver = { | ||
122 | .name = "ics932s401", | ||
123 | }, | ||
124 | .probe = ics932s401_probe, | ||
125 | .remove = ics932s401_remove, | ||
126 | .id_table = ics932s401_id, | ||
127 | .detect = ics932s401_detect, | ||
128 | .address_data = &addr_data, | ||
129 | }; | ||
130 | |||
131 | static struct ics932s401_data *ics932s401_update_device(struct device *dev) | ||
132 | { | ||
133 | struct i2c_client *client = to_i2c_client(dev); | ||
134 | struct ics932s401_data *data = i2c_get_clientdata(client); | ||
135 | unsigned long local_jiffies = jiffies; | ||
136 | int i, temp; | ||
137 | |||
138 | mutex_lock(&data->lock); | ||
139 | if (time_before(local_jiffies, data->sensors_last_updated + | ||
140 | SENSOR_REFRESH_INTERVAL) | ||
141 | && data->sensors_valid) | ||
142 | goto out; | ||
143 | |||
144 | /* | ||
145 | * Each register must be read as a word and then right shifted 8 bits. | ||
146 | * Not really sure why this is; setting the "byte count programming" | ||
147 | * register to 1 does not fix this problem. | ||
148 | */ | ||
149 | for (i = 0; i < NUM_MIRRORED_REGS; i++) { | ||
150 | temp = i2c_smbus_read_word_data(client, regs_to_copy[i]); | ||
151 | data->regs[regs_to_copy[i]] = temp >> 8; | ||
152 | } | ||
153 | |||
154 | data->sensors_last_updated = local_jiffies; | ||
155 | data->sensors_valid = 1; | ||
156 | |||
157 | out: | ||
158 | mutex_unlock(&data->lock); | ||
159 | return data; | ||
160 | } | ||
161 | |||
162 | static ssize_t show_spread_enabled(struct device *dev, | ||
163 | struct device_attribute *devattr, | ||
164 | char *buf) | ||
165 | { | ||
166 | struct ics932s401_data *data = ics932s401_update_device(dev); | ||
167 | |||
168 | if (data->regs[ICS932S401_REG_CFG2] & ICS932S401_CFG1_SPREAD) | ||
169 | return sprintf(buf, "1\n"); | ||
170 | |||
171 | return sprintf(buf, "0\n"); | ||
172 | } | ||
173 | |||
174 | /* bit to cpu khz map */ | ||
175 | static const int fs_speeds[] = { | ||
176 | 266666, | ||
177 | 133333, | ||
178 | 200000, | ||
179 | 166666, | ||
180 | 333333, | ||
181 | 100000, | ||
182 | 400000, | ||
183 | 0, | ||
184 | }; | ||
185 | |||
186 | /* clock divisor map */ | ||
187 | static const int divisors[] = {2, 3, 5, 15, 4, 6, 10, 30, 8, 12, 20, 60, 16, | ||
188 | 24, 40, 120}; | ||
189 | |||
190 | /* Calculate CPU frequency from the M/N registers. */ | ||
191 | static int calculate_cpu_freq(struct ics932s401_data *data) | ||
192 | { | ||
193 | int m, n, freq; | ||
194 | |||
195 | m = data->regs[ICS932S401_REG_CPU_M_CTRL] & ICS932S401_M_MASK; | ||
196 | n = data->regs[ICS932S401_REG_CPU_N_CTRL]; | ||
197 | |||
198 | /* Pull in bits 8 & 9 from the M register */ | ||
199 | n |= ((int)data->regs[ICS932S401_REG_CPU_M_CTRL] & 0x80) << 1; | ||
200 | n |= ((int)data->regs[ICS932S401_REG_CPU_M_CTRL] & 0x40) << 3; | ||
201 | |||
202 | freq = BASE_CLOCK * (n + 8) / (m + 2); | ||
203 | freq /= divisors[data->regs[ICS932S401_REG_CPU_DIVISOR] >> | ||
204 | ICS932S401_CPU_DIVISOR_SHIFT]; | ||
205 | |||
206 | return freq; | ||
207 | } | ||
208 | |||
209 | static ssize_t show_cpu_clock(struct device *dev, | ||
210 | struct device_attribute *devattr, | ||
211 | char *buf) | ||
212 | { | ||
213 | struct ics932s401_data *data = ics932s401_update_device(dev); | ||
214 | |||
215 | return sprintf(buf, "%d\n", calculate_cpu_freq(data)); | ||
216 | } | ||
217 | |||
218 | static ssize_t show_cpu_clock_sel(struct device *dev, | ||
219 | struct device_attribute *devattr, | ||
220 | char *buf) | ||
221 | { | ||
222 | struct ics932s401_data *data = ics932s401_update_device(dev); | ||
223 | int freq; | ||
224 | |||
225 | if (data->regs[ICS932S401_REG_CTRL] & ICS932S401_MN_ENABLED) | ||
226 | freq = calculate_cpu_freq(data); | ||
227 | else { | ||
228 | /* Freq is neatly wrapped up for us */ | ||
229 | int fid = data->regs[ICS932S401_REG_CFG7] & ICS932S401_FS_MASK; | ||
230 | freq = fs_speeds[fid]; | ||
231 | if (data->regs[ICS932S401_REG_CTRL] & ICS932S401_CPU_ALT) { | ||
232 | switch (freq) { | ||
233 | case 166666: | ||
234 | freq = 160000; | ||
235 | break; | ||
236 | case 333333: | ||
237 | freq = 320000; | ||
238 | break; | ||
239 | } | ||
240 | } | ||
241 | } | ||
242 | |||
243 | return sprintf(buf, "%d\n", freq); | ||
244 | } | ||
245 | |||
246 | /* Calculate SRC frequency from the M/N registers. */ | ||
247 | static int calculate_src_freq(struct ics932s401_data *data) | ||
248 | { | ||
249 | int m, n, freq; | ||
250 | |||
251 | m = data->regs[ICS932S401_REG_SRC_M_CTRL] & ICS932S401_M_MASK; | ||
252 | n = data->regs[ICS932S401_REG_SRC_N_CTRL]; | ||
253 | |||
254 | /* Pull in bits 8 & 9 from the M register */ | ||
255 | n |= ((int)data->regs[ICS932S401_REG_SRC_M_CTRL] & 0x80) << 1; | ||
256 | n |= ((int)data->regs[ICS932S401_REG_SRC_M_CTRL] & 0x40) << 3; | ||
257 | |||
258 | freq = BASE_CLOCK * (n + 8) / (m + 2); | ||
259 | freq /= divisors[data->regs[ICS932S401_REG_PCISRC_DIVISOR] & | ||
260 | ICS932S401_SRC_DIVISOR_MASK]; | ||
261 | |||
262 | return freq; | ||
263 | } | ||
264 | |||
265 | static ssize_t show_src_clock(struct device *dev, | ||
266 | struct device_attribute *devattr, | ||
267 | char *buf) | ||
268 | { | ||
269 | struct ics932s401_data *data = ics932s401_update_device(dev); | ||
270 | |||
271 | return sprintf(buf, "%d\n", calculate_src_freq(data)); | ||
272 | } | ||
273 | |||
274 | static ssize_t show_src_clock_sel(struct device *dev, | ||
275 | struct device_attribute *devattr, | ||
276 | char *buf) | ||
277 | { | ||
278 | struct ics932s401_data *data = ics932s401_update_device(dev); | ||
279 | int freq; | ||
280 | |||
281 | if (data->regs[ICS932S401_REG_CTRL] & ICS932S401_MN_ENABLED) | ||
282 | freq = calculate_src_freq(data); | ||
283 | else | ||
284 | /* Freq is neatly wrapped up for us */ | ||
285 | if (data->regs[ICS932S401_REG_CTRL] & ICS932S401_CPU_ALT && | ||
286 | data->regs[ICS932S401_REG_CTRL] & ICS932S401_SRC_ALT) | ||
287 | freq = 96000; | ||
288 | else | ||
289 | freq = 100000; | ||
290 | |||
291 | return sprintf(buf, "%d\n", freq); | ||
292 | } | ||
293 | |||
294 | /* Calculate PCI frequency from the SRC M/N registers. */ | ||
295 | static int calculate_pci_freq(struct ics932s401_data *data) | ||
296 | { | ||
297 | int m, n, freq; | ||
298 | |||
299 | m = data->regs[ICS932S401_REG_SRC_M_CTRL] & ICS932S401_M_MASK; | ||
300 | n = data->regs[ICS932S401_REG_SRC_N_CTRL]; | ||
301 | |||
302 | /* Pull in bits 8 & 9 from the M register */ | ||
303 | n |= ((int)data->regs[ICS932S401_REG_SRC_M_CTRL] & 0x80) << 1; | ||
304 | n |= ((int)data->regs[ICS932S401_REG_SRC_M_CTRL] & 0x40) << 3; | ||
305 | |||
306 | freq = BASE_CLOCK * (n + 8) / (m + 2); | ||
307 | freq /= divisors[data->regs[ICS932S401_REG_PCISRC_DIVISOR] >> | ||
308 | ICS932S401_PCI_DIVISOR_SHIFT]; | ||
309 | |||
310 | return freq; | ||
311 | } | ||
312 | |||
313 | static ssize_t show_pci_clock(struct device *dev, | ||
314 | struct device_attribute *devattr, | ||
315 | char *buf) | ||
316 | { | ||
317 | struct ics932s401_data *data = ics932s401_update_device(dev); | ||
318 | |||
319 | return sprintf(buf, "%d\n", calculate_pci_freq(data)); | ||
320 | } | ||
321 | |||
322 | static ssize_t show_pci_clock_sel(struct device *dev, | ||
323 | struct device_attribute *devattr, | ||
324 | char *buf) | ||
325 | { | ||
326 | struct ics932s401_data *data = ics932s401_update_device(dev); | ||
327 | int freq; | ||
328 | |||
329 | if (data->regs[ICS932S401_REG_CTRL] & ICS932S401_MN_ENABLED) | ||
330 | freq = calculate_pci_freq(data); | ||
331 | else | ||
332 | freq = 33333; | ||
333 | |||
334 | return sprintf(buf, "%d\n", freq); | ||
335 | } | ||
336 | |||
337 | static ssize_t show_value(struct device *dev, | ||
338 | struct device_attribute *devattr, | ||
339 | char *buf); | ||
340 | |||
341 | static ssize_t show_spread(struct device *dev, | ||
342 | struct device_attribute *devattr, | ||
343 | char *buf); | ||
344 | |||
345 | static DEVICE_ATTR(spread_enabled, S_IRUGO, show_spread_enabled, NULL); | ||
346 | static DEVICE_ATTR(cpu_clock_selection, S_IRUGO, show_cpu_clock_sel, NULL); | ||
347 | static DEVICE_ATTR(cpu_clock, S_IRUGO, show_cpu_clock, NULL); | ||
348 | static DEVICE_ATTR(src_clock_selection, S_IRUGO, show_src_clock_sel, NULL); | ||
349 | static DEVICE_ATTR(src_clock, S_IRUGO, show_src_clock, NULL); | ||
350 | static DEVICE_ATTR(pci_clock_selection, S_IRUGO, show_pci_clock_sel, NULL); | ||
351 | static DEVICE_ATTR(pci_clock, S_IRUGO, show_pci_clock, NULL); | ||
352 | static DEVICE_ATTR(usb_clock, S_IRUGO, show_value, NULL); | ||
353 | static DEVICE_ATTR(ref_clock, S_IRUGO, show_value, NULL); | ||
354 | static DEVICE_ATTR(cpu_spread, S_IRUGO, show_spread, NULL); | ||
355 | static DEVICE_ATTR(src_spread, S_IRUGO, show_spread, NULL); | ||
356 | |||
357 | static struct attribute *ics932s401_attr[] = | ||
358 | { | ||
359 | &dev_attr_spread_enabled.attr, | ||
360 | &dev_attr_cpu_clock_selection.attr, | ||
361 | &dev_attr_cpu_clock.attr, | ||
362 | &dev_attr_src_clock_selection.attr, | ||
363 | &dev_attr_src_clock.attr, | ||
364 | &dev_attr_pci_clock_selection.attr, | ||
365 | &dev_attr_pci_clock.attr, | ||
366 | &dev_attr_usb_clock.attr, | ||
367 | &dev_attr_ref_clock.attr, | ||
368 | &dev_attr_cpu_spread.attr, | ||
369 | &dev_attr_src_spread.attr, | ||
370 | NULL | ||
371 | }; | ||
372 | |||
373 | static ssize_t show_value(struct device *dev, | ||
374 | struct device_attribute *devattr, | ||
375 | char *buf) | ||
376 | { | ||
377 | int x; | ||
378 | |||
379 | if (devattr == &dev_attr_usb_clock) | ||
380 | x = 48000; | ||
381 | else if (devattr == &dev_attr_ref_clock) | ||
382 | x = BASE_CLOCK; | ||
383 | else | ||
384 | BUG(); | ||
385 | |||
386 | return sprintf(buf, "%d\n", x); | ||
387 | } | ||
388 | |||
389 | static ssize_t show_spread(struct device *dev, | ||
390 | struct device_attribute *devattr, | ||
391 | char *buf) | ||
392 | { | ||
393 | struct ics932s401_data *data = ics932s401_update_device(dev); | ||
394 | int reg; | ||
395 | unsigned long val; | ||
396 | |||
397 | if (!(data->regs[ICS932S401_REG_CFG2] & ICS932S401_CFG1_SPREAD)) | ||
398 | return sprintf(buf, "0%%\n"); | ||
399 | |||
400 | if (devattr == &dev_attr_src_spread) | ||
401 | reg = ICS932S401_REG_SRC_SPREAD1; | ||
402 | else if (devattr == &dev_attr_cpu_spread) | ||
403 | reg = ICS932S401_REG_CPU_SPREAD1; | ||
404 | else | ||
405 | BUG(); | ||
406 | |||
407 | val = data->regs[reg] | (data->regs[reg + 1] << 8); | ||
408 | val &= ICS932S401_SPREAD_MASK; | ||
409 | |||
410 | /* Scale 0..2^14 to -0.5. */ | ||
411 | val = 500000 * val / 16384; | ||
412 | return sprintf(buf, "-0.%lu%%\n", val); | ||
413 | } | ||
414 | |||
415 | /* Return 0 if detection is successful, -ENODEV otherwise */ | ||
416 | static int ics932s401_detect(struct i2c_client *client, int kind, | ||
417 | struct i2c_board_info *info) | ||
418 | { | ||
419 | struct i2c_adapter *adapter = client->adapter; | ||
420 | |||
421 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) | ||
422 | return -ENODEV; | ||
423 | |||
424 | if (kind <= 0) { | ||
425 | int vendor, device, revision; | ||
426 | |||
427 | vendor = i2c_smbus_read_word_data(client, | ||
428 | ICS932S401_REG_VENDOR_REV); | ||
429 | vendor >>= 8; | ||
430 | revision = vendor >> ICS932S401_REV_SHIFT; | ||
431 | vendor &= ICS932S401_VENDOR_MASK; | ||
432 | if (vendor != ICS932S401_VENDOR) | ||
433 | return -ENODEV; | ||
434 | |||
435 | device = i2c_smbus_read_word_data(client, | ||
436 | ICS932S401_REG_DEVICE); | ||
437 | device >>= 8; | ||
438 | if (device != ICS932S401_DEVICE) | ||
439 | return -ENODEV; | ||
440 | |||
441 | if (revision != ICS932S401_REV) | ||
442 | dev_info(&adapter->dev, "Unknown revision %d\n", | ||
443 | revision); | ||
444 | } else | ||
445 | dev_dbg(&adapter->dev, "detection forced\n"); | ||
446 | |||
447 | strlcpy(info->type, "ics932s401", I2C_NAME_SIZE); | ||
448 | |||
449 | return 0; | ||
450 | } | ||
451 | |||
452 | static int ics932s401_probe(struct i2c_client *client, | ||
453 | const struct i2c_device_id *id) | ||
454 | { | ||
455 | struct ics932s401_data *data; | ||
456 | int err; | ||
457 | |||
458 | data = kzalloc(sizeof(struct ics932s401_data), GFP_KERNEL); | ||
459 | if (!data) { | ||
460 | err = -ENOMEM; | ||
461 | goto exit; | ||
462 | } | ||
463 | |||
464 | i2c_set_clientdata(client, data); | ||
465 | mutex_init(&data->lock); | ||
466 | |||
467 | dev_info(&client->dev, "%s chip found\n", client->name); | ||
468 | |||
469 | /* Register sysfs hooks */ | ||
470 | data->attrs.attrs = ics932s401_attr; | ||
471 | err = sysfs_create_group(&client->dev.kobj, &data->attrs); | ||
472 | if (err) | ||
473 | goto exit_free; | ||
474 | |||
475 | return 0; | ||
476 | |||
477 | exit_free: | ||
478 | kfree(data); | ||
479 | exit: | ||
480 | return err; | ||
481 | } | ||
482 | |||
483 | static int ics932s401_remove(struct i2c_client *client) | ||
484 | { | ||
485 | struct ics932s401_data *data = i2c_get_clientdata(client); | ||
486 | |||
487 | sysfs_remove_group(&client->dev.kobj, &data->attrs); | ||
488 | kfree(data); | ||
489 | return 0; | ||
490 | } | ||
491 | |||
492 | static int __init ics932s401_init(void) | ||
493 | { | ||
494 | return i2c_add_driver(&ics932s401_driver); | ||
495 | } | ||
496 | |||
497 | static void __exit ics932s401_exit(void) | ||
498 | { | ||
499 | i2c_del_driver(&ics932s401_driver); | ||
500 | } | ||
501 | |||
502 | MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>"); | ||
503 | MODULE_DESCRIPTION("ICS932S401 driver"); | ||
504 | MODULE_LICENSE("GPL"); | ||
505 | |||
506 | module_init(ics932s401_init); | ||
507 | module_exit(ics932s401_exit); | ||
508 | |||
509 | /* IBM IntelliStation Z30 */ | ||
510 | MODULE_ALIAS("dmi:bvnIBM:*:rn9228:*"); | ||
511 | MODULE_ALIAS("dmi:bvnIBM:*:rn9232:*"); | ||
512 | |||
513 | /* IBM x3650/x3550 */ | ||
514 | MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3650*"); | ||
515 | MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3550*"); | ||
diff --git a/drivers/misc/intel_menlow.c b/drivers/misc/intel_menlow.c index e00a2756e97e..27b7662955bb 100644 --- a/drivers/misc/intel_menlow.c +++ b/drivers/misc/intel_menlow.c | |||
@@ -52,6 +52,11 @@ MODULE_LICENSE("GPL"); | |||
52 | #define MEMORY_ARG_CUR_BANDWIDTH 1 | 52 | #define MEMORY_ARG_CUR_BANDWIDTH 1 |
53 | #define MEMORY_ARG_MAX_BANDWIDTH 0 | 53 | #define MEMORY_ARG_MAX_BANDWIDTH 0 |
54 | 54 | ||
55 | /* | ||
56 | * GTHS returning 'n' would mean that [0,n-1] states are supported | ||
57 | * In that case max_cstate would be n-1 | ||
58 | * GTHS returning '0' would mean that no bandwidth control states are supported | ||
59 | */ | ||
55 | static int memory_get_int_max_bandwidth(struct thermal_cooling_device *cdev, | 60 | static int memory_get_int_max_bandwidth(struct thermal_cooling_device *cdev, |
56 | unsigned long *max_state) | 61 | unsigned long *max_state) |
57 | { | 62 | { |
@@ -71,6 +76,9 @@ static int memory_get_int_max_bandwidth(struct thermal_cooling_device *cdev, | |||
71 | if (ACPI_FAILURE(status)) | 76 | if (ACPI_FAILURE(status)) |
72 | return -EFAULT; | 77 | return -EFAULT; |
73 | 78 | ||
79 | if (!value) | ||
80 | return -EINVAL; | ||
81 | |||
74 | *max_state = value - 1; | 82 | *max_state = value - 1; |
75 | return 0; | 83 | return 0; |
76 | } | 84 | } |
@@ -121,7 +129,7 @@ static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev, | |||
121 | if (memory_get_int_max_bandwidth(cdev, &max_state)) | 129 | if (memory_get_int_max_bandwidth(cdev, &max_state)) |
122 | return -EFAULT; | 130 | return -EFAULT; |
123 | 131 | ||
124 | if (max_state < 0 || state > max_state) | 132 | if (state > max_state) |
125 | return -EINVAL; | 133 | return -EINVAL; |
126 | 134 | ||
127 | arg_list.count = 1; | 135 | arg_list.count = 1; |
diff --git a/drivers/misc/msi-laptop.c b/drivers/misc/msi-laptop.c index de898c6938f3..759763d18e4c 100644 --- a/drivers/misc/msi-laptop.c +++ b/drivers/misc/msi-laptop.c | |||
@@ -347,12 +347,16 @@ static int __init msi_init(void) | |||
347 | 347 | ||
348 | /* Register backlight stuff */ | 348 | /* Register backlight stuff */ |
349 | 349 | ||
350 | msibl_device = backlight_device_register("msi-laptop-bl", NULL, NULL, | 350 | if (acpi_video_backlight_support()) { |
351 | &msibl_ops); | 351 | printk(KERN_INFO "MSI: Brightness ignored, must be controlled " |
352 | if (IS_ERR(msibl_device)) | 352 | "by ACPI video driver\n"); |
353 | return PTR_ERR(msibl_device); | 353 | } else { |
354 | 354 | msibl_device = backlight_device_register("msi-laptop-bl", NULL, | |
355 | msibl_device->props.max_brightness = MSI_LCD_LEVEL_MAX-1; | 355 | NULL, &msibl_ops); |
356 | if (IS_ERR(msibl_device)) | ||
357 | return PTR_ERR(msibl_device); | ||
358 | msibl_device->props.max_brightness = MSI_LCD_LEVEL_MAX-1; | ||
359 | } | ||
356 | 360 | ||
357 | ret = platform_driver_register(&msipf_driver); | 361 | ret = platform_driver_register(&msipf_driver); |
358 | if (ret) | 362 | if (ret) |
diff --git a/drivers/misc/sony-laptop.c b/drivers/misc/sony-laptop.c index 06f07e19dc70..7bcb81002dcf 100644 --- a/drivers/misc/sony-laptop.c +++ b/drivers/misc/sony-laptop.c | |||
@@ -1038,7 +1038,11 @@ static int sony_nc_add(struct acpi_device *device) | |||
1038 | goto outinput; | 1038 | goto outinput; |
1039 | } | 1039 | } |
1040 | 1040 | ||
1041 | if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", &handle))) { | 1041 | if (!acpi_video_backlight_support()) { |
1042 | printk(KERN_INFO DRV_PFX "Sony: Brightness ignored, must be " | ||
1043 | "controlled by ACPI video driver\n"); | ||
1044 | } else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", | ||
1045 | &handle))) { | ||
1042 | sony_backlight_device = backlight_device_register("sony", NULL, | 1046 | sony_backlight_device = backlight_device_register("sony", NULL, |
1043 | NULL, | 1047 | NULL, |
1044 | &sony_backlight_ops); | 1048 | &sony_backlight_ops); |
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 4db1cf9078d9..7a4a26b0edd2 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c | |||
@@ -4932,16 +4932,25 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
4932 | */ | 4932 | */ |
4933 | b = tpacpi_check_std_acpi_brightness_support(); | 4933 | b = tpacpi_check_std_acpi_brightness_support(); |
4934 | if (b > 0) { | 4934 | if (b > 0) { |
4935 | if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) { | 4935 | |
4936 | printk(TPACPI_NOTICE | 4936 | if (acpi_video_backlight_support()) { |
4937 | "Lenovo BIOS switched to ACPI backlight " | 4937 | if (brightness_enable > 1) { |
4938 | "control mode\n"); | 4938 | printk(TPACPI_NOTICE |
4939 | } | 4939 | "Standard ACPI backlight interface " |
4940 | if (brightness_enable > 1) { | 4940 | "available, not loading native one.\n"); |
4941 | printk(TPACPI_NOTICE | 4941 | return 1; |
4942 | "standard ACPI backlight interface " | 4942 | } else if (brightness_enable == 1) { |
4943 | "available, not loading native one...\n"); | 4943 | printk(TPACPI_NOTICE |
4944 | return 1; | 4944 | "Backlight control force enabled, even if standard " |
4945 | "ACPI backlight interface is available\n"); | ||
4946 | } | ||
4947 | } else { | ||
4948 | if (brightness_enable > 1) { | ||
4949 | printk(TPACPI_NOTICE | ||
4950 | "Standard ACPI backlight interface not " | ||
4951 | "available, thinkpad_acpi native " | ||
4952 | "brightness control enabled\n"); | ||
4953 | } | ||
4945 | } | 4954 | } |
4946 | } | 4955 | } |
4947 | 4956 | ||