diff options
author | Yin Kangkai <kangkai.yin@linux.intel.com> | 2011-04-06 10:05:24 -0400 |
---|---|---|
committer | Matthew Garrett <mjg@redhat.com> | 2011-05-27 12:36:43 -0400 |
commit | bb3ce2020451bdebe5ceac770504f427724008a9 (patch) | |
tree | 1f2e3fa0a8b6f8262f2cc33164efc934cb45008a /drivers/platform | |
parent | 0978e012cfbaca8bd312933e98cdea2d11778e11 (diff) |
platform/oaktrail: ACPI EC Extra driver for Oaktrail
This driver implements an Extra ACPI EC driver for products based on Intel
Oaktrail platform.
This driver does below things:
1. registers itself in the Linux backlight control in
/sys/class/backlight/intel_oaktrail/
2. registers in the rfkill subsystem here: /sys/class/rfkill/rfkillX/
for these components: wifi, bluetooth, wwan (3g), gps
Signed-off-by: Yin Kangkai <kangkai.yin@linux.intel.com>
[Extracted from a bigger patch by Yin Kangkai, this version leaves out some
sysfs bits that probably want to be driver managed, and ACPI i2c enumeration]
Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Matthew Garrett <mjg@redhat.com>
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/x86/Kconfig | 9 | ||||
-rw-r--r-- | drivers/platform/x86/Makefile | 1 | ||||
-rw-r--r-- | drivers/platform/x86/intel_oaktrail.c | 396 |
3 files changed, 406 insertions, 0 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 5cb999b50f95..83c2411acb5b 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig | |||
@@ -760,4 +760,13 @@ config MXM_WMI | |||
760 | MXM is a standard for laptop graphics cards, the WMI interface | 760 | MXM is a standard for laptop graphics cards, the WMI interface |
761 | is required for switchable nvidia graphics machines | 761 | is required for switchable nvidia graphics machines |
762 | 762 | ||
763 | config INTEL_OAKTRAIL | ||
764 | tristate "Intel Oaktrail Platform Extras" | ||
765 | depends on ACPI | ||
766 | depends on RFKILL && BACKLIGHT_CLASS_DEVICE && ACPI | ||
767 | ---help--- | ||
768 | Intel Oaktrail platform need this driver to provide interfaces to | ||
769 | enable/disable the Camera, WiFi, BT etc. devices. If in doubt, say Y | ||
770 | here; it will only load on supported platforms. | ||
771 | |||
763 | endif # X86_PLATFORM_DEVICES | 772 | endif # X86_PLATFORM_DEVICES |
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index a7ab3bc7b3a1..af683fb5a807 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile | |||
@@ -43,3 +43,4 @@ obj-$(CONFIG_IBM_RTL) += ibm_rtl.o | |||
43 | obj-$(CONFIG_SAMSUNG_LAPTOP) += samsung-laptop.o | 43 | obj-$(CONFIG_SAMSUNG_LAPTOP) += samsung-laptop.o |
44 | obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o | 44 | obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o |
45 | obj-$(CONFIG_MXM_WMI) += mxm-wmi.o | 45 | obj-$(CONFIG_MXM_WMI) += mxm-wmi.o |
46 | obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o | ||
diff --git a/drivers/platform/x86/intel_oaktrail.c b/drivers/platform/x86/intel_oaktrail.c new file mode 100644 index 000000000000..e936364a609d --- /dev/null +++ b/drivers/platform/x86/intel_oaktrail.c | |||
@@ -0,0 +1,396 @@ | |||
1 | /* | ||
2 | * intel_oaktrail.c - Intel OakTrail Platform support. | ||
3 | * | ||
4 | * Copyright (C) 2010-2011 Intel Corporation | ||
5 | * Author: Yin Kangkai (kangkai.yin@intel.com) | ||
6 | * | ||
7 | * based on Compal driver, Copyright (C) 2008 Cezary Jackiewicz | ||
8 | * <cezary.jackiewicz (at) gmail.com>, based on MSI driver | ||
9 | * Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
19 | * General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
24 | * 02110-1301, USA. | ||
25 | * | ||
26 | * This driver does below things: | ||
27 | * 1. registers itself in the Linux backlight control in | ||
28 | * /sys/class/backlight/intel_oaktrail/ | ||
29 | * | ||
30 | * 2. registers in the rfkill subsystem here: /sys/class/rfkill/rfkillX/ | ||
31 | * for these components: wifi, bluetooth, wwan (3g), gps | ||
32 | * | ||
33 | * This driver might work on other products based on Oaktrail. If you | ||
34 | * want to try it you can pass force=1 as argument to the module which | ||
35 | * will force it to load even when the DMI data doesn't identify the | ||
36 | * product as compatible. | ||
37 | */ | ||
38 | |||
39 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
40 | |||
41 | #include <linux/module.h> | ||
42 | #include <linux/kernel.h> | ||
43 | #include <linux/init.h> | ||
44 | #include <linux/acpi.h> | ||
45 | #include <linux/fb.h> | ||
46 | #include <linux/mutex.h> | ||
47 | #include <linux/err.h> | ||
48 | #include <linux/i2c.h> | ||
49 | #include <linux/backlight.h> | ||
50 | #include <linux/platform_device.h> | ||
51 | #include <linux/dmi.h> | ||
52 | #include <linux/rfkill.h> | ||
53 | #include <acpi/acpi_bus.h> | ||
54 | #include <acpi/acpi_drivers.h> | ||
55 | |||
56 | |||
57 | #define DRIVER_NAME "intel_oaktrail" | ||
58 | #define DRIVER_VERSION "0.4ac1" | ||
59 | |||
60 | /* | ||
61 | * This is the devices status address in EC space, and the control bits | ||
62 | * definition: | ||
63 | * | ||
64 | * (1 << 0): Camera enable/disable, RW. | ||
65 | * (1 << 1): Bluetooth enable/disable, RW. | ||
66 | * (1 << 2): GPS enable/disable, RW. | ||
67 | * (1 << 3): WiFi enable/disable, RW. | ||
68 | * (1 << 4): WWAN (3G) enable/disalbe, RW. | ||
69 | * (1 << 5): Touchscreen enable/disable, Read Only. | ||
70 | */ | ||
71 | #define OT_EC_DEVICE_STATE_ADDRESS 0xD6 | ||
72 | |||
73 | #define OT_EC_CAMERA_MASK (1 << 0) | ||
74 | #define OT_EC_BT_MASK (1 << 1) | ||
75 | #define OT_EC_GPS_MASK (1 << 2) | ||
76 | #define OT_EC_WIFI_MASK (1 << 3) | ||
77 | #define OT_EC_WWAN_MASK (1 << 4) | ||
78 | #define OT_EC_TS_MASK (1 << 5) | ||
79 | |||
80 | /* | ||
81 | * This is the address in EC space and commands used to control LCD backlight: | ||
82 | * | ||
83 | * Two steps needed to change the LCD backlight: | ||
84 | * 1. write the backlight percentage into OT_EC_BL_BRIGHTNESS_ADDRESS; | ||
85 | * 2. write OT_EC_BL_CONTROL_ON_DATA into OT_EC_BL_CONTROL_ADDRESS. | ||
86 | * | ||
87 | * To read the LCD back light, just read out the value from | ||
88 | * OT_EC_BL_BRIGHTNESS_ADDRESS. | ||
89 | * | ||
90 | * LCD backlight brightness range: 0 - 100 (OT_EC_BL_BRIGHTNESS_MAX) | ||
91 | */ | ||
92 | #define OT_EC_BL_BRIGHTNESS_ADDRESS 0x44 | ||
93 | #define OT_EC_BL_BRIGHTNESS_MAX 100 | ||
94 | #define OT_EC_BL_CONTROL_ADDRESS 0x3A | ||
95 | #define OT_EC_BL_CONTROL_ON_DATA 0x1A | ||
96 | |||
97 | |||
98 | static int force; | ||
99 | module_param(force, bool, 0); | ||
100 | MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); | ||
101 | |||
102 | static struct platform_device *oaktrail_device; | ||
103 | static struct backlight_device *oaktrail_bl_device; | ||
104 | static struct rfkill *bt_rfkill; | ||
105 | static struct rfkill *gps_rfkill; | ||
106 | static struct rfkill *wifi_rfkill; | ||
107 | static struct rfkill *wwan_rfkill; | ||
108 | |||
109 | |||
110 | /* rfkill */ | ||
111 | static int oaktrail_rfkill_set(void *data, bool blocked) | ||
112 | { | ||
113 | u8 value; | ||
114 | u8 result; | ||
115 | unsigned long radio = (unsigned long) data; | ||
116 | |||
117 | ec_read(OT_EC_DEVICE_STATE_ADDRESS, &result); | ||
118 | |||
119 | if (!blocked) | ||
120 | value = (u8) (result | radio); | ||
121 | else | ||
122 | value = (u8) (result & ~radio); | ||
123 | |||
124 | ec_write(OT_EC_DEVICE_STATE_ADDRESS, value); | ||
125 | |||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | static const struct rfkill_ops oaktrail_rfkill_ops = { | ||
130 | .set_block = oaktrail_rfkill_set, | ||
131 | }; | ||
132 | |||
133 | static struct rfkill *oaktrail_rfkill_new(char *name, enum rfkill_type type, | ||
134 | unsigned long mask) | ||
135 | { | ||
136 | struct rfkill *rfkill_dev; | ||
137 | u8 value; | ||
138 | int err; | ||
139 | |||
140 | rfkill_dev = rfkill_alloc(name, &oaktrail_device->dev, type, | ||
141 | &oaktrail_rfkill_ops, (void *)mask); | ||
142 | if (!rfkill_dev) | ||
143 | return ERR_PTR(-ENOMEM); | ||
144 | |||
145 | ec_read(OT_EC_DEVICE_STATE_ADDRESS, &value); | ||
146 | rfkill_init_sw_state(rfkill_dev, (value & mask) != 1); | ||
147 | |||
148 | err = rfkill_register(rfkill_dev); | ||
149 | if (err) { | ||
150 | rfkill_destroy(rfkill_dev); | ||
151 | return ERR_PTR(err); | ||
152 | } | ||
153 | |||
154 | return rfkill_dev; | ||
155 | } | ||
156 | |||
157 | static inline void __oaktrail_rfkill_cleanup(struct rfkill *rf) | ||
158 | { | ||
159 | if (rf) { | ||
160 | rfkill_unregister(rf); | ||
161 | rfkill_destroy(rf); | ||
162 | } | ||
163 | } | ||
164 | |||
165 | static void oaktrail_rfkill_cleanup(void) | ||
166 | { | ||
167 | __oaktrail_rfkill_cleanup(wifi_rfkill); | ||
168 | __oaktrail_rfkill_cleanup(bt_rfkill); | ||
169 | __oaktrail_rfkill_cleanup(gps_rfkill); | ||
170 | __oaktrail_rfkill_cleanup(wwan_rfkill); | ||
171 | } | ||
172 | |||
173 | static int oaktrail_rfkill_init(void) | ||
174 | { | ||
175 | int ret; | ||
176 | |||
177 | wifi_rfkill = oaktrail_rfkill_new("oaktrail-wifi", | ||
178 | RFKILL_TYPE_WLAN, | ||
179 | OT_EC_WIFI_MASK); | ||
180 | if (IS_ERR(wifi_rfkill)) { | ||
181 | ret = PTR_ERR(wifi_rfkill); | ||
182 | wifi_rfkill = NULL; | ||
183 | goto cleanup; | ||
184 | } | ||
185 | |||
186 | bt_rfkill = oaktrail_rfkill_new("oaktrail-bluetooth", | ||
187 | RFKILL_TYPE_BLUETOOTH, | ||
188 | OT_EC_BT_MASK); | ||
189 | if (IS_ERR(bt_rfkill)) { | ||
190 | ret = PTR_ERR(bt_rfkill); | ||
191 | bt_rfkill = NULL; | ||
192 | goto cleanup; | ||
193 | } | ||
194 | |||
195 | gps_rfkill = oaktrail_rfkill_new("oaktrail-gps", | ||
196 | RFKILL_TYPE_GPS, | ||
197 | OT_EC_GPS_MASK); | ||
198 | if (IS_ERR(gps_rfkill)) { | ||
199 | ret = PTR_ERR(gps_rfkill); | ||
200 | gps_rfkill = NULL; | ||
201 | goto cleanup; | ||
202 | } | ||
203 | |||
204 | wwan_rfkill = oaktrail_rfkill_new("oaktrail-wwan", | ||
205 | RFKILL_TYPE_WWAN, | ||
206 | OT_EC_WWAN_MASK); | ||
207 | if (IS_ERR(wwan_rfkill)) { | ||
208 | ret = PTR_ERR(wwan_rfkill); | ||
209 | wwan_rfkill = NULL; | ||
210 | goto cleanup; | ||
211 | } | ||
212 | |||
213 | return 0; | ||
214 | |||
215 | cleanup: | ||
216 | oaktrail_rfkill_cleanup(); | ||
217 | return ret; | ||
218 | } | ||
219 | |||
220 | |||
221 | /* backlight */ | ||
222 | static int get_backlight_brightness(struct backlight_device *b) | ||
223 | { | ||
224 | u8 value; | ||
225 | ec_read(OT_EC_BL_BRIGHTNESS_ADDRESS, &value); | ||
226 | |||
227 | return value; | ||
228 | } | ||
229 | |||
230 | static int set_backlight_brightness(struct backlight_device *b) | ||
231 | { | ||
232 | u8 percent = (u8) b->props.brightness; | ||
233 | if (percent < 0 || percent > OT_EC_BL_BRIGHTNESS_MAX) | ||
234 | return -EINVAL; | ||
235 | |||
236 | ec_write(OT_EC_BL_BRIGHTNESS_ADDRESS, percent); | ||
237 | ec_write(OT_EC_BL_CONTROL_ADDRESS, OT_EC_BL_CONTROL_ON_DATA); | ||
238 | |||
239 | return 0; | ||
240 | } | ||
241 | |||
242 | static const struct backlight_ops oaktrail_bl_ops = { | ||
243 | .get_brightness = get_backlight_brightness, | ||
244 | .update_status = set_backlight_brightness, | ||
245 | }; | ||
246 | |||
247 | static int oaktrail_backlight_init(void) | ||
248 | { | ||
249 | struct backlight_device *bd; | ||
250 | struct backlight_properties props; | ||
251 | |||
252 | memset(&props, 0, sizeof(struct backlight_properties)); | ||
253 | props.max_brightness = OT_EC_BL_BRIGHTNESS_MAX; | ||
254 | bd = backlight_device_register(DRIVER_NAME, | ||
255 | &oaktrail_device->dev, NULL, | ||
256 | &oaktrail_bl_ops, | ||
257 | &props); | ||
258 | |||
259 | if (IS_ERR(bd)) { | ||
260 | oaktrail_bl_device = NULL; | ||
261 | pr_warning("Unable to register backlight device\n"); | ||
262 | return PTR_ERR(bd); | ||
263 | } | ||
264 | |||
265 | oaktrail_bl_device = bd; | ||
266 | |||
267 | bd->props.brightness = get_backlight_brightness(bd); | ||
268 | bd->props.power = FB_BLANK_UNBLANK; | ||
269 | backlight_update_status(bd); | ||
270 | |||
271 | return 0; | ||
272 | } | ||
273 | |||
274 | static void oaktrail_backlight_exit(void) | ||
275 | { | ||
276 | if (oaktrail_bl_device) | ||
277 | backlight_device_unregister(oaktrail_bl_device); | ||
278 | } | ||
279 | |||
280 | static int __devinit oaktrail_probe(struct platform_device *pdev) | ||
281 | { | ||
282 | return 0; | ||
283 | } | ||
284 | |||
285 | static int __devexit oaktrail_remove(struct platform_device *pdev) | ||
286 | { | ||
287 | return 0; | ||
288 | } | ||
289 | |||
290 | static struct platform_driver oaktrail_driver = { | ||
291 | .driver = { | ||
292 | .name = DRIVER_NAME, | ||
293 | .owner = THIS_MODULE, | ||
294 | }, | ||
295 | .probe = oaktrail_probe, | ||
296 | .remove = __devexit_p(oaktrail_remove) | ||
297 | }; | ||
298 | |||
299 | static int dmi_check_cb(const struct dmi_system_id *id) | ||
300 | { | ||
301 | pr_info("Identified model '%s'\n", id->ident); | ||
302 | return 0; | ||
303 | } | ||
304 | |||
305 | static struct dmi_system_id __initdata oaktrail_dmi_table[] = { | ||
306 | { | ||
307 | .ident = "OakTrail platform", | ||
308 | .matches = { | ||
309 | DMI_MATCH(DMI_PRODUCT_NAME, "OakTrail platform"), | ||
310 | }, | ||
311 | .callback = dmi_check_cb | ||
312 | }, | ||
313 | { } | ||
314 | }; | ||
315 | |||
316 | static int __init oaktrail_init(void) | ||
317 | { | ||
318 | int ret; | ||
319 | |||
320 | if (acpi_disabled) { | ||
321 | pr_err("ACPI needs to be enabled for this driver to work!\n"); | ||
322 | return -ENODEV; | ||
323 | } | ||
324 | |||
325 | if (!force && !dmi_check_system(oaktrail_dmi_table)) { | ||
326 | pr_err("Platform not recognized (You could try the module's force-parameter)"); | ||
327 | return -ENODEV; | ||
328 | } | ||
329 | |||
330 | ret = platform_driver_register(&oaktrail_driver); | ||
331 | if (ret) { | ||
332 | pr_warning("Unable to register platform driver\n"); | ||
333 | goto err_driver_reg; | ||
334 | } | ||
335 | |||
336 | oaktrail_device = platform_device_alloc(DRIVER_NAME, -1); | ||
337 | if (!oaktrail_device) { | ||
338 | pr_warning("Unable to allocate platform device\n"); | ||
339 | ret = -ENOMEM; | ||
340 | goto err_device_alloc; | ||
341 | } | ||
342 | |||
343 | ret = platform_device_add(oaktrail_device); | ||
344 | if (ret) { | ||
345 | pr_warning("Unable to add platform device\n"); | ||
346 | goto err_device_add; | ||
347 | } | ||
348 | |||
349 | if (!acpi_video_backlight_support()) { | ||
350 | ret = oaktrail_backlight_init(); | ||
351 | if (ret) | ||
352 | goto err_backlight; | ||
353 | |||
354 | } else | ||
355 | pr_info("Backlight controlled by ACPI video driver\n"); | ||
356 | |||
357 | ret = oaktrail_rfkill_init(); | ||
358 | if (ret) { | ||
359 | pr_warning("Setup rfkill failed\n"); | ||
360 | goto err_rfkill; | ||
361 | } | ||
362 | |||
363 | pr_info("Driver "DRIVER_VERSION" successfully loaded\n"); | ||
364 | return 0; | ||
365 | |||
366 | err_rfkill: | ||
367 | oaktrail_backlight_exit(); | ||
368 | err_backlight: | ||
369 | platform_device_del(oaktrail_device); | ||
370 | err_device_add: | ||
371 | platform_device_put(oaktrail_device); | ||
372 | err_device_alloc: | ||
373 | platform_driver_unregister(&oaktrail_driver); | ||
374 | err_driver_reg: | ||
375 | |||
376 | return ret; | ||
377 | } | ||
378 | |||
379 | static void __exit oaktrail_cleanup(void) | ||
380 | { | ||
381 | oaktrail_backlight_exit(); | ||
382 | oaktrail_rfkill_cleanup(); | ||
383 | platform_device_unregister(oaktrail_device); | ||
384 | platform_driver_unregister(&oaktrail_driver); | ||
385 | |||
386 | pr_info("Driver unloaded\n"); | ||
387 | } | ||
388 | |||
389 | module_init(oaktrail_init); | ||
390 | module_exit(oaktrail_cleanup); | ||
391 | |||
392 | MODULE_AUTHOR("Yin Kangkai (kangkai.yin@intel.com)"); | ||
393 | MODULE_DESCRIPTION("Intel Oaktrail Platform ACPI Extras"); | ||
394 | MODULE_VERSION(DRIVER_VERSION); | ||
395 | MODULE_LICENSE("GPL"); | ||
396 | MODULE_ALIAS("dmi:*:svnIntelCorporation:pnOakTrailplatform:*"); | ||