Commit b20f9e5bddddb5ef0d743d6e0d409ffc8cf9fc56
Exists in
master
and in
7 other branches
Merge branch 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/staging
* 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/staging: (24 commits) hwmon: lis3: Release resources in case of failure hwmon: lis3: Short explanations of platform data fields hwmon: lis3: Enhance lis3 selftest with IRQ line test hwmon: lis3: use block read to access data registers hwmon: lis3: Adjust fuzziness for 8 bit device hwmon: lis3: New parameters to platform data hwmon: lis3: restore axis enabled bits hwmon: lis3: Power on corrections hwmon: lis3: Update coordinates at polled device open hwmon: lis3: Cleanup interrupt handling hwmon: lis3: regulator control hwmon: lis3: pm_runtime support Kirkwood: add fan support for Network Space Max v2 hwmon: add generic GPIO fan driver hwmon: (coretemp) fix reading of microcode revision (v2) hwmon: ({core, pkg, via-cpu}temp) remove unnecessary CONFIG_HOTPLUG_CPU ifdefs hwmon: (pkgtemp) align driver initialization style with coretemp hwmon: LTC4261 Hardware monitoring driver hwmon: (lis3) add axes module parameter for custom axis-mapping hwmon: (hp_accel) Add HP Mini 510x family support ...
Showing 17 changed files Side-by-side Diff
- Documentation/hwmon/ltc4261
- MAINTAINERS
- arch/arm/mach-kirkwood/netspace_v2-setup.c
- drivers/hwmon/Kconfig
- drivers/hwmon/Makefile
- drivers/hwmon/coretemp.c
- drivers/hwmon/gpio-fan.c
- drivers/hwmon/hp_accel.c
- drivers/hwmon/lis3lv02d.c
- drivers/hwmon/lis3lv02d.h
- drivers/hwmon/lis3lv02d_i2c.c
- drivers/hwmon/lis3lv02d_spi.c
- drivers/hwmon/ltc4261.c
- drivers/hwmon/pkgtemp.c
- drivers/hwmon/via-cputemp.c
- include/linux/gpio-fan.h
- include/linux/lis3lv02d.h
Documentation/hwmon/ltc4261
1 | +Kernel driver ltc4261 | |
2 | +===================== | |
3 | + | |
4 | +Supported chips: | |
5 | + * Linear Technology LTC4261 | |
6 | + Prefix: 'ltc4261' | |
7 | + Addresses scanned: - | |
8 | + Datasheet: | |
9 | + http://cds.linear.com/docs/Datasheet/42612fb.pdf | |
10 | + | |
11 | +Author: Guenter Roeck <guenter.roeck@ericsson.com> | |
12 | + | |
13 | + | |
14 | +Description | |
15 | +----------- | |
16 | + | |
17 | +The LTC4261/LTC4261-2 negative voltage Hot Swap controllers allow a board | |
18 | +to be safely inserted and removed from a live backplane. | |
19 | + | |
20 | + | |
21 | +Usage Notes | |
22 | +----------- | |
23 | + | |
24 | +This driver does not probe for LTC4261 devices, since there is no register | |
25 | +which can be safely used to identify the chip. You will have to instantiate | |
26 | +the devices explicitly. | |
27 | + | |
28 | +Example: the following will load the driver for an LTC4261 at address 0x10 | |
29 | +on I2C bus #1: | |
30 | +$ modprobe ltc4261 | |
31 | +$ echo ltc4261 0x10 > /sys/bus/i2c/devices/i2c-1/new_device | |
32 | + | |
33 | + | |
34 | +Sysfs entries | |
35 | +------------- | |
36 | + | |
37 | +Voltage readings provided by this driver are reported as obtained from the ADC | |
38 | +registers. If a set of voltage divider resistors is installed, calculate the | |
39 | +real voltage by multiplying the reported value with (R1+R2)/R2, where R1 is the | |
40 | +value of the divider resistor against the measured voltage and R2 is the value | |
41 | +of the divider resistor against Ground. | |
42 | + | |
43 | +Current reading provided by this driver is reported as obtained from the ADC | |
44 | +Current Sense register. The reported value assumes that a 1 mOhm sense resistor | |
45 | +is installed. If a different sense resistor is installed, calculate the real | |
46 | +current by dividing the reported value by the sense resistor value in mOhm. | |
47 | + | |
48 | +The chip has two voltage sensors, but only one set of voltage alarm status bits. | |
49 | +In many many designs, those alarms are associated with the ADIN2 sensor, due to | |
50 | +the proximity of the ADIN2 pin to the OV pin. ADIN2 is, however, not available | |
51 | +on all chip variants. To ensure that the alarm condition is reported to the user, | |
52 | +report it with both voltage sensors. | |
53 | + | |
54 | +in1_input ADIN2 voltage (mV) | |
55 | +in1_min_alarm ADIN/ADIN2 Undervoltage alarm | |
56 | +in1_max_alarm ADIN/ADIN2 Overvoltage alarm | |
57 | + | |
58 | +in2_input ADIN voltage (mV) | |
59 | +in2_min_alarm ADIN/ADIN2 Undervoltage alarm | |
60 | +in2_max_alarm ADIN/ADIN2 Overvoltage alarm | |
61 | + | |
62 | +curr1_input SENSE current (mA) | |
63 | +curr1_alarm SENSE overcurrent alarm |
MAINTAINERS
... | ... | @@ -3765,6 +3765,13 @@ |
3765 | 3765 | S: Maintained |
3766 | 3766 | F: drivers/scsi/sym53c8xx_2/ |
3767 | 3767 | |
3768 | +LTC4261 HARDWARE MONITOR DRIVER | |
3769 | +M: Guenter Roeck <linux@roeck-us.net> | |
3770 | +L: lm-sensors@lm-sensors.org | |
3771 | +S: Maintained | |
3772 | +F: Documentation/hwmon/ltc4261 | |
3773 | +F: drivers/hwmon/ltc4261.c | |
3774 | + | |
3768 | 3775 | LTP (Linux Test Project) |
3769 | 3776 | M: Rishikesh K Rajak <risrajak@linux.vnet.ibm.com> |
3770 | 3777 | M: Garrett Cooper <yanegomi@gmail.com> |
arch/arm/mach-kirkwood/netspace_v2-setup.c
... | ... | @@ -30,6 +30,7 @@ |
30 | 30 | #include <linux/gpio.h> |
31 | 31 | #include <linux/gpio_keys.h> |
32 | 32 | #include <linux/leds.h> |
33 | +#include <linux/gpio-fan.h> | |
33 | 34 | #include <asm/mach-types.h> |
34 | 35 | #include <asm/mach/arch.h> |
35 | 36 | #include <mach/kirkwood.h> |
... | ... | @@ -137,6 +138,46 @@ |
137 | 138 | }; |
138 | 139 | |
139 | 140 | /***************************************************************************** |
141 | + * GPIO fan | |
142 | + ****************************************************************************/ | |
143 | + | |
144 | +/* Designed for fan 40x40x16: ADDA AD0412LB-D50 6000rpm@12v */ | |
145 | +static struct gpio_fan_speed netspace_max_v2_fan_speed[] = { | |
146 | + { 0, 0 }, | |
147 | + { 1500, 15 }, | |
148 | + { 1700, 14 }, | |
149 | + { 1800, 13 }, | |
150 | + { 2100, 12 }, | |
151 | + { 3100, 11 }, | |
152 | + { 3300, 10 }, | |
153 | + { 4300, 9 }, | |
154 | + { 5500, 8 }, | |
155 | +}; | |
156 | + | |
157 | +static unsigned netspace_max_v2_fan_ctrl[] = { 22, 7, 33, 23 }; | |
158 | + | |
159 | +static struct gpio_fan_alarm netspace_max_v2_fan_alarm = { | |
160 | + .gpio = 25, | |
161 | + .active_low = 1, | |
162 | +}; | |
163 | + | |
164 | +static struct gpio_fan_platform_data netspace_max_v2_fan_data = { | |
165 | + .num_ctrl = ARRAY_SIZE(netspace_max_v2_fan_ctrl), | |
166 | + .ctrl = netspace_max_v2_fan_ctrl, | |
167 | + .alarm = &netspace_max_v2_fan_alarm, | |
168 | + .num_speed = ARRAY_SIZE(netspace_max_v2_fan_speed), | |
169 | + .speed = netspace_max_v2_fan_speed, | |
170 | +}; | |
171 | + | |
172 | +static struct platform_device netspace_max_v2_gpio_fan = { | |
173 | + .name = "gpio-fan", | |
174 | + .id = -1, | |
175 | + .dev = { | |
176 | + .platform_data = &netspace_max_v2_fan_data, | |
177 | + }, | |
178 | +}; | |
179 | + | |
180 | +/***************************************************************************** | |
140 | 181 | * General Setup |
141 | 182 | ****************************************************************************/ |
142 | 183 | |
... | ... | @@ -205,6 +246,8 @@ |
205 | 246 | platform_device_register(&netspace_v2_leds); |
206 | 247 | platform_device_register(&netspace_v2_gpio_leds); |
207 | 248 | platform_device_register(&netspace_v2_gpio_buttons); |
249 | + if (machine_is_netspace_max_v2()) | |
250 | + platform_device_register(&netspace_max_v2_gpio_fan); | |
208 | 251 | |
209 | 252 | if (gpio_request(NETSPACE_V2_GPIO_POWER_OFF, "power-off") == 0 && |
210 | 253 | gpio_direction_output(NETSPACE_V2_GPIO_POWER_OFF, 0) == 0) |
drivers/hwmon/Kconfig
... | ... | @@ -399,6 +399,15 @@ |
399 | 399 | This driver can also be built as a module. If so, the module |
400 | 400 | will be called gl520sm. |
401 | 401 | |
402 | +config SENSORS_GPIO_FAN | |
403 | + tristate "GPIO fan" | |
404 | + depends on GENERIC_GPIO | |
405 | + help | |
406 | + If you say yes here you get support for fans connected to GPIO lines. | |
407 | + | |
408 | + This driver can also be built as a module. If so, the module | |
409 | + will be called gpio-fan. | |
410 | + | |
402 | 411 | config SENSORS_CORETEMP |
403 | 412 | tristate "Intel Core/Core2/Atom temperature sensor" |
404 | 413 | depends on X86 && PCI && EXPERIMENTAL |
... | ... | @@ -653,6 +662,17 @@ |
653 | 662 | |
654 | 663 | This driver can also be built as a module. If so, the module will |
655 | 664 | be called ltc4245. |
665 | + | |
666 | +config SENSORS_LTC4261 | |
667 | + tristate "Linear Technology LTC4261" | |
668 | + depends on I2C && EXPERIMENTAL | |
669 | + default n | |
670 | + help | |
671 | + If you say yes here you get support for Linear Technology LTC4261 | |
672 | + Negative Voltage Hot Swap Controller I2C interface. | |
673 | + | |
674 | + This driver can also be built as a module. If so, the module will | |
675 | + be called ltc4261. | |
656 | 676 | |
657 | 677 | config SENSORS_LM95241 |
658 | 678 | tristate "National Semiconductor LM95241 sensor chip" |
drivers/hwmon/Makefile
... | ... | @@ -51,6 +51,7 @@ |
51 | 51 | obj-$(CONFIG_SENSORS_G760A) += g760a.o |
52 | 52 | obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o |
53 | 53 | obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o |
54 | +obj-$(CONFIG_SENSORS_GPIO_FAN) += gpio-fan.o | |
54 | 55 | obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o |
55 | 56 | obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o |
56 | 57 | obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o |
... | ... | @@ -79,6 +80,7 @@ |
79 | 80 | obj-$(CONFIG_SENSORS_LM95241) += lm95241.o |
80 | 81 | obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o |
81 | 82 | obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o |
83 | +obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o | |
82 | 84 | obj-$(CONFIG_SENSORS_MAX1111) += max1111.o |
83 | 85 | obj-$(CONFIG_SENSORS_MAX1619) += max1619.o |
84 | 86 | obj-$(CONFIG_SENSORS_MAX6650) += max6650.o |
drivers/hwmon/coretemp.c
... | ... | @@ -21,7 +21,6 @@ |
21 | 21 | */ |
22 | 22 | |
23 | 23 | #include <linux/module.h> |
24 | -#include <linux/delay.h> | |
25 | 24 | #include <linux/init.h> |
26 | 25 | #include <linux/slab.h> |
27 | 26 | #include <linux/jiffies.h> |
28 | 27 | |
... | ... | @@ -280,11 +279,9 @@ |
280 | 279 | case 0x1a: |
281 | 280 | dev_warn(dev, "TjMax is assumed as 100 C!\n"); |
282 | 281 | return 100000; |
283 | - break; | |
284 | 282 | case 0x17: |
285 | 283 | case 0x1c: /* Atom CPUs */ |
286 | 284 | return adjust_tjmax(c, id, dev); |
287 | - break; | |
288 | 285 | default: |
289 | 286 | dev_warn(dev, "CPU (model=0x%x) is not supported yet," |
290 | 287 | " using default TjMax of 100C.\n", c->x86_model); |
... | ... | @@ -292,6 +289,15 @@ |
292 | 289 | } |
293 | 290 | } |
294 | 291 | |
292 | +static void __devinit get_ucode_rev_on_cpu(void *edx) | |
293 | +{ | |
294 | + u32 eax; | |
295 | + | |
296 | + wrmsr(MSR_IA32_UCODE_REV, 0, 0); | |
297 | + sync_core(); | |
298 | + rdmsr(MSR_IA32_UCODE_REV, eax, *(u32 *)edx); | |
299 | +} | |
300 | + | |
295 | 301 | static int __devinit coretemp_probe(struct platform_device *pdev) |
296 | 302 | { |
297 | 303 | struct coretemp_data *data; |
298 | 304 | |
... | ... | @@ -327,9 +333,16 @@ |
327 | 333 | |
328 | 334 | if ((c->x86_model == 0xe) && (c->x86_mask < 0xc)) { |
329 | 335 | /* check for microcode update */ |
330 | - rdmsr_on_cpu(data->id, MSR_IA32_UCODE_REV, &eax, &edx); | |
331 | - if (edx < 0x39) { | |
336 | + err = smp_call_function_single(data->id, get_ucode_rev_on_cpu, | |
337 | + &edx, 1); | |
338 | + if (err) { | |
339 | + dev_err(&pdev->dev, | |
340 | + "Cannot determine microcode revision of " | |
341 | + "CPU#%u (%d)!\n", data->id, err); | |
332 | 342 | err = -ENODEV; |
343 | + goto exit_free; | |
344 | + } else if (edx < 0x39) { | |
345 | + err = -ENODEV; | |
333 | 346 | dev_err(&pdev->dev, |
334 | 347 | "Errata AE18 not fixed, update BIOS or " |
335 | 348 | "microcode of the CPU!\n"); |
... | ... | @@ -490,7 +503,7 @@ |
490 | 503 | return err; |
491 | 504 | } |
492 | 505 | |
493 | -static void coretemp_device_remove(unsigned int cpu) | |
506 | +static void __cpuinit coretemp_device_remove(unsigned int cpu) | |
494 | 507 | { |
495 | 508 | struct pdev_entry *p; |
496 | 509 | unsigned int i; |
497 | 510 | |
... | ... | @@ -569,9 +582,8 @@ |
569 | 582 | static void __exit coretemp_exit(void) |
570 | 583 | { |
571 | 584 | struct pdev_entry *p, *n; |
572 | -#ifdef CONFIG_HOTPLUG_CPU | |
585 | + | |
573 | 586 | unregister_hotcpu_notifier(&coretemp_cpu_notifier); |
574 | -#endif | |
575 | 587 | mutex_lock(&pdev_list_mutex); |
576 | 588 | list_for_each_entry_safe(p, n, &pdev_list, list) { |
577 | 589 | platform_device_unregister(p->pdev); |
drivers/hwmon/gpio-fan.c
1 | +/* | |
2 | + * gpio-fan.c - Hwmon driver for fans connected to GPIO lines. | |
3 | + * | |
4 | + * Copyright (C) 2010 LaCie | |
5 | + * | |
6 | + * Author: Simon Guinot <sguinot@lacie.com> | |
7 | + * | |
8 | + * This program is free software; you can redistribute it and/or modify | |
9 | + * it under the terms of the GNU General Public License as published by | |
10 | + * the Free Software Foundation; either version 2 of the License, or | |
11 | + * (at your option) any later version. | |
12 | + * | |
13 | + * This program is distributed in the hope that it will be useful, | |
14 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | + * GNU General Public License for more details. | |
17 | + * | |
18 | + * You should have received a copy of the GNU General Public License | |
19 | + * along with this program; if not, write to the Free Software | |
20 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
21 | + */ | |
22 | + | |
23 | +#include <linux/module.h> | |
24 | +#include <linux/init.h> | |
25 | +#include <linux/slab.h> | |
26 | +#include <linux/interrupt.h> | |
27 | +#include <linux/irq.h> | |
28 | +#include <linux/platform_device.h> | |
29 | +#include <linux/err.h> | |
30 | +#include <linux/mutex.h> | |
31 | +#include <linux/hwmon.h> | |
32 | +#include <linux/gpio.h> | |
33 | +#include <linux/gpio-fan.h> | |
34 | + | |
35 | +struct gpio_fan_data { | |
36 | + struct platform_device *pdev; | |
37 | + struct device *hwmon_dev; | |
38 | + struct mutex lock; /* lock GPIOs operations. */ | |
39 | + int num_ctrl; | |
40 | + unsigned *ctrl; | |
41 | + int num_speed; | |
42 | + struct gpio_fan_speed *speed; | |
43 | + int speed_index; | |
44 | +#ifdef CONFIG_PM | |
45 | + int resume_speed; | |
46 | +#endif | |
47 | + bool pwm_enable; | |
48 | + struct gpio_fan_alarm *alarm; | |
49 | + struct work_struct alarm_work; | |
50 | +}; | |
51 | + | |
52 | +/* | |
53 | + * Alarm GPIO. | |
54 | + */ | |
55 | + | |
56 | +static void fan_alarm_notify(struct work_struct *ws) | |
57 | +{ | |
58 | + struct gpio_fan_data *fan_data = | |
59 | + container_of(ws, struct gpio_fan_data, alarm_work); | |
60 | + | |
61 | + sysfs_notify(&fan_data->pdev->dev.kobj, NULL, "fan1_alarm"); | |
62 | + kobject_uevent(&fan_data->pdev->dev.kobj, KOBJ_CHANGE); | |
63 | +} | |
64 | + | |
65 | +static irqreturn_t fan_alarm_irq_handler(int irq, void *dev_id) | |
66 | +{ | |
67 | + struct gpio_fan_data *fan_data = dev_id; | |
68 | + | |
69 | + schedule_work(&fan_data->alarm_work); | |
70 | + | |
71 | + return IRQ_NONE; | |
72 | +} | |
73 | + | |
74 | +static ssize_t show_fan_alarm(struct device *dev, | |
75 | + struct device_attribute *attr, char *buf) | |
76 | +{ | |
77 | + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); | |
78 | + struct gpio_fan_alarm *alarm = fan_data->alarm; | |
79 | + int value = gpio_get_value(alarm->gpio); | |
80 | + | |
81 | + if (alarm->active_low) | |
82 | + value = !value; | |
83 | + | |
84 | + return sprintf(buf, "%d\n", value); | |
85 | +} | |
86 | + | |
87 | +static DEVICE_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL); | |
88 | + | |
89 | +static int fan_alarm_init(struct gpio_fan_data *fan_data, | |
90 | + struct gpio_fan_alarm *alarm) | |
91 | +{ | |
92 | + int err; | |
93 | + int alarm_irq; | |
94 | + struct platform_device *pdev = fan_data->pdev; | |
95 | + | |
96 | + fan_data->alarm = alarm; | |
97 | + | |
98 | + err = gpio_request(alarm->gpio, "GPIO fan alarm"); | |
99 | + if (err) | |
100 | + return err; | |
101 | + | |
102 | + err = gpio_direction_input(alarm->gpio); | |
103 | + if (err) | |
104 | + goto err_free_gpio; | |
105 | + | |
106 | + err = device_create_file(&pdev->dev, &dev_attr_fan1_alarm); | |
107 | + if (err) | |
108 | + goto err_free_gpio; | |
109 | + | |
110 | + /* | |
111 | + * If the alarm GPIO don't support interrupts, just leave | |
112 | + * without initializing the fail notification support. | |
113 | + */ | |
114 | + alarm_irq = gpio_to_irq(alarm->gpio); | |
115 | + if (alarm_irq < 0) | |
116 | + return 0; | |
117 | + | |
118 | + INIT_WORK(&fan_data->alarm_work, fan_alarm_notify); | |
119 | + set_irq_type(alarm_irq, IRQ_TYPE_EDGE_BOTH); | |
120 | + err = request_irq(alarm_irq, fan_alarm_irq_handler, IRQF_SHARED, | |
121 | + "GPIO fan alarm", fan_data); | |
122 | + if (err) | |
123 | + goto err_free_sysfs; | |
124 | + | |
125 | + return 0; | |
126 | + | |
127 | +err_free_sysfs: | |
128 | + device_remove_file(&pdev->dev, &dev_attr_fan1_alarm); | |
129 | +err_free_gpio: | |
130 | + gpio_free(alarm->gpio); | |
131 | + | |
132 | + return err; | |
133 | +} | |
134 | + | |
135 | +static void fan_alarm_free(struct gpio_fan_data *fan_data) | |
136 | +{ | |
137 | + struct platform_device *pdev = fan_data->pdev; | |
138 | + int alarm_irq = gpio_to_irq(fan_data->alarm->gpio); | |
139 | + | |
140 | + if (alarm_irq >= 0) | |
141 | + free_irq(alarm_irq, fan_data); | |
142 | + device_remove_file(&pdev->dev, &dev_attr_fan1_alarm); | |
143 | + gpio_free(fan_data->alarm->gpio); | |
144 | +} | |
145 | + | |
146 | +/* | |
147 | + * Control GPIOs. | |
148 | + */ | |
149 | + | |
150 | +/* Must be called with fan_data->lock held, except during initialization. */ | |
151 | +static void __set_fan_ctrl(struct gpio_fan_data *fan_data, int ctrl_val) | |
152 | +{ | |
153 | + int i; | |
154 | + | |
155 | + for (i = 0; i < fan_data->num_ctrl; i++) | |
156 | + gpio_set_value(fan_data->ctrl[i], (ctrl_val >> i) & 1); | |
157 | +} | |
158 | + | |
159 | +static int __get_fan_ctrl(struct gpio_fan_data *fan_data) | |
160 | +{ | |
161 | + int i; | |
162 | + int ctrl_val = 0; | |
163 | + | |
164 | + for (i = 0; i < fan_data->num_ctrl; i++) { | |
165 | + int value; | |
166 | + | |
167 | + value = gpio_get_value(fan_data->ctrl[i]); | |
168 | + ctrl_val |= (value << i); | |
169 | + } | |
170 | + return ctrl_val; | |
171 | +} | |
172 | + | |
173 | +/* Must be called with fan_data->lock held, except during initialization. */ | |
174 | +static void set_fan_speed(struct gpio_fan_data *fan_data, int speed_index) | |
175 | +{ | |
176 | + if (fan_data->speed_index == speed_index) | |
177 | + return; | |
178 | + | |
179 | + __set_fan_ctrl(fan_data, fan_data->speed[speed_index].ctrl_val); | |
180 | + fan_data->speed_index = speed_index; | |
181 | +} | |
182 | + | |
183 | +static int get_fan_speed_index(struct gpio_fan_data *fan_data) | |
184 | +{ | |
185 | + int ctrl_val = __get_fan_ctrl(fan_data); | |
186 | + int i; | |
187 | + | |
188 | + for (i = 0; i < fan_data->num_speed; i++) | |
189 | + if (fan_data->speed[i].ctrl_val == ctrl_val) | |
190 | + return i; | |
191 | + | |
192 | + dev_warn(&fan_data->pdev->dev, | |
193 | + "missing speed array entry for GPIO value 0x%x\n", ctrl_val); | |
194 | + | |
195 | + return -EINVAL; | |
196 | +} | |
197 | + | |
198 | +static int rpm_to_speed_index(struct gpio_fan_data *fan_data, int rpm) | |
199 | +{ | |
200 | + struct gpio_fan_speed *speed = fan_data->speed; | |
201 | + int i; | |
202 | + | |
203 | + for (i = 0; i < fan_data->num_speed; i++) | |
204 | + if (speed[i].rpm >= rpm) | |
205 | + return i; | |
206 | + | |
207 | + return fan_data->num_speed - 1; | |
208 | +} | |
209 | + | |
210 | +static ssize_t show_pwm(struct device *dev, | |
211 | + struct device_attribute *attr, char *buf) | |
212 | +{ | |
213 | + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); | |
214 | + u8 pwm = fan_data->speed_index * 255 / (fan_data->num_speed - 1); | |
215 | + | |
216 | + return sprintf(buf, "%d\n", pwm); | |
217 | +} | |
218 | + | |
219 | +static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, | |
220 | + const char *buf, size_t count) | |
221 | +{ | |
222 | + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); | |
223 | + unsigned long pwm; | |
224 | + int speed_index; | |
225 | + int ret = count; | |
226 | + | |
227 | + if (strict_strtoul(buf, 10, &pwm) || pwm > 255) | |
228 | + return -EINVAL; | |
229 | + | |
230 | + mutex_lock(&fan_data->lock); | |
231 | + | |
232 | + if (!fan_data->pwm_enable) { | |
233 | + ret = -EPERM; | |
234 | + goto exit_unlock; | |
235 | + } | |
236 | + | |
237 | + speed_index = DIV_ROUND_UP(pwm * (fan_data->num_speed - 1), 255); | |
238 | + set_fan_speed(fan_data, speed_index); | |
239 | + | |
240 | +exit_unlock: | |
241 | + mutex_unlock(&fan_data->lock); | |
242 | + | |
243 | + return ret; | |
244 | +} | |
245 | + | |
246 | +static ssize_t show_pwm_enable(struct device *dev, | |
247 | + struct device_attribute *attr, char *buf) | |
248 | +{ | |
249 | + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); | |
250 | + | |
251 | + return sprintf(buf, "%d\n", fan_data->pwm_enable); | |
252 | +} | |
253 | + | |
254 | +static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *attr, | |
255 | + const char *buf, size_t count) | |
256 | +{ | |
257 | + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); | |
258 | + unsigned long val; | |
259 | + | |
260 | + if (strict_strtoul(buf, 10, &val) || val > 1) | |
261 | + return -EINVAL; | |
262 | + | |
263 | + if (fan_data->pwm_enable == val) | |
264 | + return count; | |
265 | + | |
266 | + mutex_lock(&fan_data->lock); | |
267 | + | |
268 | + fan_data->pwm_enable = val; | |
269 | + | |
270 | + /* Disable manual control mode: set fan at full speed. */ | |
271 | + if (val == 0) | |
272 | + set_fan_speed(fan_data, fan_data->num_speed - 1); | |
273 | + | |
274 | + mutex_unlock(&fan_data->lock); | |
275 | + | |
276 | + return count; | |
277 | +} | |
278 | + | |
279 | +static ssize_t show_pwm_mode(struct device *dev, | |
280 | + struct device_attribute *attr, char *buf) | |
281 | +{ | |
282 | + return sprintf(buf, "0\n"); | |
283 | +} | |
284 | + | |
285 | +static ssize_t show_rpm_min(struct device *dev, | |
286 | + struct device_attribute *attr, char *buf) | |
287 | +{ | |
288 | + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); | |
289 | + | |
290 | + return sprintf(buf, "%d\n", fan_data->speed[0].rpm); | |
291 | +} | |
292 | + | |
293 | +static ssize_t show_rpm_max(struct device *dev, | |
294 | + struct device_attribute *attr, char *buf) | |
295 | +{ | |
296 | + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); | |
297 | + | |
298 | + return sprintf(buf, "%d\n", | |
299 | + fan_data->speed[fan_data->num_speed - 1].rpm); | |
300 | +} | |
301 | + | |
302 | +static ssize_t show_rpm(struct device *dev, | |
303 | + struct device_attribute *attr, char *buf) | |
304 | +{ | |
305 | + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); | |
306 | + | |
307 | + return sprintf(buf, "%d\n", fan_data->speed[fan_data->speed_index].rpm); | |
308 | +} | |
309 | + | |
310 | +static ssize_t set_rpm(struct device *dev, struct device_attribute *attr, | |
311 | + const char *buf, size_t count) | |
312 | +{ | |
313 | + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); | |
314 | + unsigned long rpm; | |
315 | + int ret = count; | |
316 | + | |
317 | + if (strict_strtoul(buf, 10, &rpm)) | |
318 | + return -EINVAL; | |
319 | + | |
320 | + mutex_lock(&fan_data->lock); | |
321 | + | |
322 | + if (!fan_data->pwm_enable) { | |
323 | + ret = -EPERM; | |
324 | + goto exit_unlock; | |
325 | + } | |
326 | + | |
327 | + set_fan_speed(fan_data, rpm_to_speed_index(fan_data, rpm)); | |
328 | + | |
329 | +exit_unlock: | |
330 | + mutex_unlock(&fan_data->lock); | |
331 | + | |
332 | + return ret; | |
333 | +} | |
334 | + | |
335 | +static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, set_pwm); | |
336 | +static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, | |
337 | + show_pwm_enable, set_pwm_enable); | |
338 | +static DEVICE_ATTR(pwm1_mode, S_IRUGO, show_pwm_mode, NULL); | |
339 | +static DEVICE_ATTR(fan1_min, S_IRUGO, show_rpm_min, NULL); | |
340 | +static DEVICE_ATTR(fan1_max, S_IRUGO, show_rpm_max, NULL); | |
341 | +static DEVICE_ATTR(fan1_input, S_IRUGO, show_rpm, NULL); | |
342 | +static DEVICE_ATTR(fan1_target, S_IRUGO | S_IWUSR, show_rpm, set_rpm); | |
343 | + | |
344 | +static struct attribute *gpio_fan_ctrl_attributes[] = { | |
345 | + &dev_attr_pwm1.attr, | |
346 | + &dev_attr_pwm1_enable.attr, | |
347 | + &dev_attr_pwm1_mode.attr, | |
348 | + &dev_attr_fan1_input.attr, | |
349 | + &dev_attr_fan1_target.attr, | |
350 | + &dev_attr_fan1_min.attr, | |
351 | + &dev_attr_fan1_max.attr, | |
352 | + NULL | |
353 | +}; | |
354 | + | |
355 | +static const struct attribute_group gpio_fan_ctrl_group = { | |
356 | + .attrs = gpio_fan_ctrl_attributes, | |
357 | +}; | |
358 | + | |
359 | +static int fan_ctrl_init(struct gpio_fan_data *fan_data, | |
360 | + struct gpio_fan_platform_data *pdata) | |
361 | +{ | |
362 | + struct platform_device *pdev = fan_data->pdev; | |
363 | + int num_ctrl = pdata->num_ctrl; | |
364 | + unsigned *ctrl = pdata->ctrl; | |
365 | + int i, err; | |
366 | + | |
367 | + for (i = 0; i < num_ctrl; i++) { | |
368 | + err = gpio_request(ctrl[i], "GPIO fan control"); | |
369 | + if (err) | |
370 | + goto err_free_gpio; | |
371 | + | |
372 | + err = gpio_direction_output(ctrl[i], gpio_get_value(ctrl[i])); | |
373 | + if (err) { | |
374 | + gpio_free(ctrl[i]); | |
375 | + goto err_free_gpio; | |
376 | + } | |
377 | + } | |
378 | + | |
379 | + err = sysfs_create_group(&pdev->dev.kobj, &gpio_fan_ctrl_group); | |
380 | + if (err) | |
381 | + goto err_free_gpio; | |
382 | + | |
383 | + fan_data->num_ctrl = num_ctrl; | |
384 | + fan_data->ctrl = ctrl; | |
385 | + fan_data->num_speed = pdata->num_speed; | |
386 | + fan_data->speed = pdata->speed; | |
387 | + fan_data->pwm_enable = true; /* Enable manual fan speed control. */ | |
388 | + fan_data->speed_index = get_fan_speed_index(fan_data); | |
389 | + if (fan_data->speed_index < 0) { | |
390 | + err = -ENODEV; | |
391 | + goto err_free_gpio; | |
392 | + } | |
393 | + | |
394 | + return 0; | |
395 | + | |
396 | +err_free_gpio: | |
397 | + for (i = i - 1; i >= 0; i--) | |
398 | + gpio_free(ctrl[i]); | |
399 | + | |
400 | + return err; | |
401 | +} | |
402 | + | |
403 | +static void fan_ctrl_free(struct gpio_fan_data *fan_data) | |
404 | +{ | |
405 | + struct platform_device *pdev = fan_data->pdev; | |
406 | + int i; | |
407 | + | |
408 | + sysfs_remove_group(&pdev->dev.kobj, &gpio_fan_ctrl_group); | |
409 | + for (i = 0; i < fan_data->num_ctrl; i++) | |
410 | + gpio_free(fan_data->ctrl[i]); | |
411 | +} | |
412 | + | |
413 | +/* | |
414 | + * Platform driver. | |
415 | + */ | |
416 | + | |
417 | +static ssize_t show_name(struct device *dev, | |
418 | + struct device_attribute *attr, char *buf) | |
419 | +{ | |
420 | + return sprintf(buf, "gpio-fan\n"); | |
421 | +} | |
422 | + | |
423 | +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); | |
424 | + | |
425 | +static int __devinit gpio_fan_probe(struct platform_device *pdev) | |
426 | +{ | |
427 | + int err; | |
428 | + struct gpio_fan_data *fan_data; | |
429 | + struct gpio_fan_platform_data *pdata = pdev->dev.platform_data; | |
430 | + | |
431 | + if (!pdata) | |
432 | + return -EINVAL; | |
433 | + | |
434 | + fan_data = kzalloc(sizeof(struct gpio_fan_data), GFP_KERNEL); | |
435 | + if (!fan_data) | |
436 | + return -ENOMEM; | |
437 | + | |
438 | + fan_data->pdev = pdev; | |
439 | + platform_set_drvdata(pdev, fan_data); | |
440 | + mutex_init(&fan_data->lock); | |
441 | + | |
442 | + /* Configure alarm GPIO if available. */ | |
443 | + if (pdata->alarm) { | |
444 | + err = fan_alarm_init(fan_data, pdata->alarm); | |
445 | + if (err) | |
446 | + goto err_free_data; | |
447 | + } | |
448 | + | |
449 | + /* Configure control GPIOs if available. */ | |
450 | + if (pdata->ctrl && pdata->num_ctrl > 0) { | |
451 | + if (!pdata->speed || pdata->num_speed <= 1) { | |
452 | + err = -EINVAL; | |
453 | + goto err_free_alarm; | |
454 | + } | |
455 | + err = fan_ctrl_init(fan_data, pdata); | |
456 | + if (err) | |
457 | + goto err_free_alarm; | |
458 | + } | |
459 | + | |
460 | + err = device_create_file(&pdev->dev, &dev_attr_name); | |
461 | + if (err) | |
462 | + goto err_free_ctrl; | |
463 | + | |
464 | + /* Make this driver part of hwmon class. */ | |
465 | + fan_data->hwmon_dev = hwmon_device_register(&pdev->dev); | |
466 | + if (IS_ERR(fan_data->hwmon_dev)) { | |
467 | + err = PTR_ERR(fan_data->hwmon_dev); | |
468 | + goto err_remove_name; | |
469 | + } | |
470 | + | |
471 | + dev_info(&pdev->dev, "GPIO fan initialized\n"); | |
472 | + | |
473 | + return 0; | |
474 | + | |
475 | +err_remove_name: | |
476 | + device_remove_file(&pdev->dev, &dev_attr_name); | |
477 | +err_free_ctrl: | |
478 | + if (fan_data->ctrl) | |
479 | + fan_ctrl_free(fan_data); | |
480 | +err_free_alarm: | |
481 | + if (fan_data->alarm) | |
482 | + fan_alarm_free(fan_data); | |
483 | +err_free_data: | |
484 | + platform_set_drvdata(pdev, NULL); | |
485 | + kfree(fan_data); | |
486 | + | |
487 | + return err; | |
488 | +} | |
489 | + | |
490 | +static int __devexit gpio_fan_remove(struct platform_device *pdev) | |
491 | +{ | |
492 | + struct gpio_fan_data *fan_data = platform_get_drvdata(pdev); | |
493 | + | |
494 | + hwmon_device_unregister(fan_data->hwmon_dev); | |
495 | + device_remove_file(&pdev->dev, &dev_attr_name); | |
496 | + if (fan_data->alarm) | |
497 | + fan_alarm_free(fan_data); | |
498 | + if (fan_data->ctrl) | |
499 | + fan_ctrl_free(fan_data); | |
500 | + kfree(fan_data); | |
501 | + | |
502 | + return 0; | |
503 | +} | |
504 | + | |
505 | +#ifdef CONFIG_PM | |
506 | +static int gpio_fan_suspend(struct platform_device *pdev, pm_message_t state) | |
507 | +{ | |
508 | + struct gpio_fan_data *fan_data = platform_get_drvdata(pdev); | |
509 | + | |
510 | + if (fan_data->ctrl) { | |
511 | + fan_data->resume_speed = fan_data->speed_index; | |
512 | + set_fan_speed(fan_data, 0); | |
513 | + } | |
514 | + | |
515 | + return 0; | |
516 | +} | |
517 | + | |
518 | +static int gpio_fan_resume(struct platform_device *pdev) | |
519 | +{ | |
520 | + struct gpio_fan_data *fan_data = platform_get_drvdata(pdev); | |
521 | + | |
522 | + if (fan_data->ctrl) | |
523 | + set_fan_speed(fan_data, fan_data->resume_speed); | |
524 | + | |
525 | + return 0; | |
526 | +} | |
527 | +#else | |
528 | +#define gpio_fan_suspend NULL | |
529 | +#define gpio_fan_resume NULL | |
530 | +#endif | |
531 | + | |
532 | +static struct platform_driver gpio_fan_driver = { | |
533 | + .probe = gpio_fan_probe, | |
534 | + .remove = __devexit_p(gpio_fan_remove), | |
535 | + .suspend = gpio_fan_suspend, | |
536 | + .resume = gpio_fan_resume, | |
537 | + .driver = { | |
538 | + .name = "gpio-fan", | |
539 | + }, | |
540 | +}; | |
541 | + | |
542 | +static int __init gpio_fan_init(void) | |
543 | +{ | |
544 | + return platform_driver_register(&gpio_fan_driver); | |
545 | +} | |
546 | + | |
547 | +static void __exit gpio_fan_exit(void) | |
548 | +{ | |
549 | + platform_driver_unregister(&gpio_fan_driver); | |
550 | +} | |
551 | + | |
552 | +module_init(gpio_fan_init); | |
553 | +module_exit(gpio_fan_exit); | |
554 | + | |
555 | +MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>"); | |
556 | +MODULE_DESCRIPTION("GPIO FAN driver"); | |
557 | +MODULE_LICENSE("GPL"); | |
558 | +MODULE_ALIAS("platform:gpio-fan"); |
drivers/hwmon/hp_accel.c
... | ... | @@ -146,7 +146,7 @@ |
146 | 146 | |
147 | 147 | static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi) |
148 | 148 | { |
149 | - lis3_dev.ac = *((struct axis_conversion *)dmi->driver_data); | |
149 | + lis3_dev.ac = *((union axis_conversion *)dmi->driver_data); | |
150 | 150 | printk(KERN_INFO DRIVER_NAME ": hardware type %s found.\n", dmi->ident); |
151 | 151 | |
152 | 152 | return 1; |
... | ... | @@ -154,16 +154,19 @@ |
154 | 154 | |
155 | 155 | /* Represents, for each axis seen by userspace, the corresponding hw axis (+1). |
156 | 156 | * If the value is negative, the opposite of the hw value is used. */ |
157 | -static struct axis_conversion lis3lv02d_axis_normal = {1, 2, 3}; | |
158 | -static struct axis_conversion lis3lv02d_axis_y_inverted = {1, -2, 3}; | |
159 | -static struct axis_conversion lis3lv02d_axis_x_inverted = {-1, 2, 3}; | |
160 | -static struct axis_conversion lis3lv02d_axis_z_inverted = {1, 2, -3}; | |
161 | -static struct axis_conversion lis3lv02d_axis_xy_swap = {2, 1, 3}; | |
162 | -static struct axis_conversion lis3lv02d_axis_xy_rotated_left = {-2, 1, 3}; | |
163 | -static struct axis_conversion lis3lv02d_axis_xy_rotated_left_usd = {-2, 1, -3}; | |
164 | -static struct axis_conversion lis3lv02d_axis_xy_swap_inverted = {-2, -1, 3}; | |
165 | -static struct axis_conversion lis3lv02d_axis_xy_rotated_right = {2, -1, 3}; | |
166 | -static struct axis_conversion lis3lv02d_axis_xy_swap_yz_inverted = {2, -1, -3}; | |
157 | +#define DEFINE_CONV(name, x, y, z) \ | |
158 | + static union axis_conversion lis3lv02d_axis_##name = \ | |
159 | + { .as_array = { x, y, z } } | |
160 | +DEFINE_CONV(normal, 1, 2, 3); | |
161 | +DEFINE_CONV(y_inverted, 1, -2, 3); | |
162 | +DEFINE_CONV(x_inverted, -1, 2, 3); | |
163 | +DEFINE_CONV(z_inverted, 1, 2, -3); | |
164 | +DEFINE_CONV(xy_swap, 2, 1, 3); | |
165 | +DEFINE_CONV(xy_rotated_left, -2, 1, 3); | |
166 | +DEFINE_CONV(xy_rotated_left_usd, -2, 1, -3); | |
167 | +DEFINE_CONV(xy_swap_inverted, -2, -1, 3); | |
168 | +DEFINE_CONV(xy_rotated_right, 2, -1, 3); | |
169 | +DEFINE_CONV(xy_swap_yz_inverted, 2, -1, -3); | |
167 | 170 | |
168 | 171 | #define AXIS_DMI_MATCH(_ident, _name, _axis) { \ |
169 | 172 | .ident = _ident, \ |
... | ... | @@ -222,7 +225,7 @@ |
222 | 225 | AXIS_DMI_MATCH("HPB452x", "HP ProBook 452", y_inverted), |
223 | 226 | AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap), |
224 | 227 | AXIS_DMI_MATCH("HPB532x", "HP ProBook 532", y_inverted), |
225 | - AXIS_DMI_MATCH("Mini5102", "HP Mini 5102", xy_rotated_left_usd), | |
228 | + AXIS_DMI_MATCH("Mini510x", "HP Mini 510", xy_rotated_left_usd), | |
226 | 229 | { NULL, } |
227 | 230 | /* Laptop models without axis info (yet): |
228 | 231 | * "NC6910" "HP Compaq 6910" |
... | ... | @@ -299,7 +302,10 @@ |
299 | 302 | lis3lv02d_enum_resources(device); |
300 | 303 | |
301 | 304 | /* If possible use a "standard" axes order */ |
302 | - if (dmi_check_system(lis3lv02d_dmi_ids) == 0) { | |
305 | + if (lis3_dev.ac.x && lis3_dev.ac.y && lis3_dev.ac.z) { | |
306 | + printk(KERN_INFO DRIVER_NAME ": Using custom axes %d,%d,%d\n", | |
307 | + lis3_dev.ac.x, lis3_dev.ac.y, lis3_dev.ac.z); | |
308 | + } else if (dmi_check_system(lis3lv02d_dmi_ids) == 0) { | |
303 | 309 | printk(KERN_INFO DRIVER_NAME ": laptop model unknown, " |
304 | 310 | "using default axes configuration\n"); |
305 | 311 | lis3_dev.ac = lis3lv02d_axis_normal; |
drivers/hwmon/lis3lv02d.c
... | ... | @@ -31,9 +31,11 @@ |
31 | 31 | #include <linux/delay.h> |
32 | 32 | #include <linux/wait.h> |
33 | 33 | #include <linux/poll.h> |
34 | +#include <linux/slab.h> | |
34 | 35 | #include <linux/freezer.h> |
35 | 36 | #include <linux/uaccess.h> |
36 | 37 | #include <linux/miscdevice.h> |
38 | +#include <linux/pm_runtime.h> | |
37 | 39 | #include <asm/atomic.h> |
38 | 40 | #include "lis3lv02d.h" |
39 | 41 | |
... | ... | @@ -43,6 +45,16 @@ |
43 | 45 | #define MDPS_POLL_INTERVAL 50 |
44 | 46 | #define MDPS_POLL_MIN 0 |
45 | 47 | #define MDPS_POLL_MAX 2000 |
48 | + | |
49 | +#define LIS3_SYSFS_POWERDOWN_DELAY 5000 /* In milliseconds */ | |
50 | + | |
51 | +#define SELFTEST_OK 0 | |
52 | +#define SELFTEST_FAIL -1 | |
53 | +#define SELFTEST_IRQ -2 | |
54 | + | |
55 | +#define IRQ_LINE0 0 | |
56 | +#define IRQ_LINE1 1 | |
57 | + | |
46 | 58 | /* |
47 | 59 | * The sensor can also generate interrupts (DRDY) but it's pretty pointless |
48 | 60 | * because they are generated even if the data do not change. So it's better |
... | ... | @@ -66,8 +78,10 @@ |
66 | 78 | #define LIS3_SENSITIVITY_12B ((LIS3_ACCURACY * 1000) / 1024) |
67 | 79 | #define LIS3_SENSITIVITY_8B (18 * LIS3_ACCURACY) |
68 | 80 | |
69 | -#define LIS3_DEFAULT_FUZZ 3 | |
70 | -#define LIS3_DEFAULT_FLAT 3 | |
81 | +#define LIS3_DEFAULT_FUZZ_12B 3 | |
82 | +#define LIS3_DEFAULT_FLAT_12B 3 | |
83 | +#define LIS3_DEFAULT_FUZZ_8B 1 | |
84 | +#define LIS3_DEFAULT_FLAT_8B 1 | |
71 | 85 | |
72 | 86 | struct lis3lv02d lis3_dev = { |
73 | 87 | .misc_wait = __WAIT_QUEUE_HEAD_INITIALIZER(lis3_dev.misc_wait), |
... | ... | @@ -75,6 +89,30 @@ |
75 | 89 | |
76 | 90 | EXPORT_SYMBOL_GPL(lis3_dev); |
77 | 91 | |
92 | +/* just like param_set_int() but does sanity-check so that it won't point | |
93 | + * over the axis array size | |
94 | + */ | |
95 | +static int param_set_axis(const char *val, const struct kernel_param *kp) | |
96 | +{ | |
97 | + int ret = param_set_int(val, kp); | |
98 | + if (!ret) { | |
99 | + int val = *(int *)kp->arg; | |
100 | + if (val < 0) | |
101 | + val = -val; | |
102 | + if (!val || val > 3) | |
103 | + return -EINVAL; | |
104 | + } | |
105 | + return ret; | |
106 | +} | |
107 | + | |
108 | +static struct kernel_param_ops param_ops_axis = { | |
109 | + .set = param_set_axis, | |
110 | + .get = param_get_int, | |
111 | +}; | |
112 | + | |
113 | +module_param_array_named(axes, lis3_dev.ac.as_array, axis, NULL, 0644); | |
114 | +MODULE_PARM_DESC(axes, "Axis-mapping for x,y,z directions"); | |
115 | + | |
78 | 116 | static s16 lis3lv02d_read_8(struct lis3lv02d *lis3, int reg) |
79 | 117 | { |
80 | 118 | s8 lo; |
... | ... | @@ -123,9 +161,24 @@ |
123 | 161 | int position[3]; |
124 | 162 | int i; |
125 | 163 | |
126 | - position[0] = lis3->read_data(lis3, OUTX); | |
127 | - position[1] = lis3->read_data(lis3, OUTY); | |
128 | - position[2] = lis3->read_data(lis3, OUTZ); | |
164 | + if (lis3->blkread) { | |
165 | + if (lis3_dev.whoami == WAI_12B) { | |
166 | + u16 data[3]; | |
167 | + lis3->blkread(lis3, OUTX_L, 6, (u8 *)data); | |
168 | + for (i = 0; i < 3; i++) | |
169 | + position[i] = (s16)le16_to_cpu(data[i]); | |
170 | + } else { | |
171 | + u8 data[5]; | |
172 | + /* Data: x, dummy, y, dummy, z */ | |
173 | + lis3->blkread(lis3, OUTX, 5, data); | |
174 | + for (i = 0; i < 3; i++) | |
175 | + position[i] = (s8)data[i * 2]; | |
176 | + } | |
177 | + } else { | |
178 | + position[0] = lis3->read_data(lis3, OUTX); | |
179 | + position[1] = lis3->read_data(lis3, OUTY); | |
180 | + position[2] = lis3->read_data(lis3, OUTZ); | |
181 | + } | |
129 | 182 | |
130 | 183 | for (i = 0; i < 3; i++) |
131 | 184 | position[i] = (position[i] * lis3->scale) / LIS3_ACCURACY; |
... | ... | @@ -138,6 +191,7 @@ |
138 | 191 | /* conversion btw sampling rate and the register values */ |
139 | 192 | static int lis3_12_rates[4] = {40, 160, 640, 2560}; |
140 | 193 | static int lis3_8_rates[2] = {100, 400}; |
194 | +static int lis3_3dc_rates[16] = {0, 1, 10, 25, 50, 100, 200, 400, 1600, 5000}; | |
141 | 195 | |
142 | 196 | /* ODR is Output Data Rate */ |
143 | 197 | static int lis3lv02d_get_odr(void) |
... | ... | @@ -156,6 +210,9 @@ |
156 | 210 | u8 ctrl; |
157 | 211 | int i, len, shift; |
158 | 212 | |
213 | + if (!rate) | |
214 | + return -EINVAL; | |
215 | + | |
159 | 216 | lis3_dev.read(&lis3_dev, CTRL_REG1, &ctrl); |
160 | 217 | ctrl &= ~lis3_dev.odr_mask; |
161 | 218 | len = 1 << hweight_long(lis3_dev.odr_mask); /* # of possible values */ |
162 | 219 | |
163 | 220 | |
164 | 221 | |
... | ... | @@ -172,19 +229,42 @@ |
172 | 229 | |
173 | 230 | static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3]) |
174 | 231 | { |
175 | - u8 reg; | |
232 | + u8 ctlreg, reg; | |
176 | 233 | s16 x, y, z; |
177 | 234 | u8 selftest; |
178 | 235 | int ret; |
236 | + u8 ctrl_reg_data; | |
237 | + unsigned char irq_cfg; | |
179 | 238 | |
180 | 239 | mutex_lock(&lis3->mutex); |
181 | - if (lis3_dev.whoami == WAI_12B) | |
182 | - selftest = CTRL1_ST; | |
183 | - else | |
184 | - selftest = CTRL1_STP; | |
185 | 240 | |
186 | - lis3->read(lis3, CTRL_REG1, ®); | |
187 | - lis3->write(lis3, CTRL_REG1, (reg | selftest)); | |
241 | + irq_cfg = lis3->irq_cfg; | |
242 | + if (lis3_dev.whoami == WAI_8B) { | |
243 | + lis3->data_ready_count[IRQ_LINE0] = 0; | |
244 | + lis3->data_ready_count[IRQ_LINE1] = 0; | |
245 | + | |
246 | + /* Change interrupt cfg to data ready for selftest */ | |
247 | + atomic_inc(&lis3_dev.wake_thread); | |
248 | + lis3->irq_cfg = LIS3_IRQ1_DATA_READY | LIS3_IRQ2_DATA_READY; | |
249 | + lis3->read(lis3, CTRL_REG3, &ctrl_reg_data); | |
250 | + lis3->write(lis3, CTRL_REG3, (ctrl_reg_data & | |
251 | + ~(LIS3_IRQ1_MASK | LIS3_IRQ2_MASK)) | | |
252 | + (LIS3_IRQ1_DATA_READY | LIS3_IRQ2_DATA_READY)); | |
253 | + } | |
254 | + | |
255 | + if (lis3_dev.whoami == WAI_3DC) { | |
256 | + ctlreg = CTRL_REG4; | |
257 | + selftest = CTRL4_ST0; | |
258 | + } else { | |
259 | + ctlreg = CTRL_REG1; | |
260 | + if (lis3_dev.whoami == WAI_12B) | |
261 | + selftest = CTRL1_ST; | |
262 | + else | |
263 | + selftest = CTRL1_STP; | |
264 | + } | |
265 | + | |
266 | + lis3->read(lis3, ctlreg, ®); | |
267 | + lis3->write(lis3, ctlreg, (reg | selftest)); | |
188 | 268 | msleep(lis3->pwron_delay / lis3lv02d_get_odr()); |
189 | 269 | |
190 | 270 | /* Read directly to avoid axis remap */ |
... | ... | @@ -193,7 +273,7 @@ |
193 | 273 | z = lis3->read_data(lis3, OUTZ); |
194 | 274 | |
195 | 275 | /* back to normal settings */ |
196 | - lis3->write(lis3, CTRL_REG1, reg); | |
276 | + lis3->write(lis3, ctlreg, reg); | |
197 | 277 | msleep(lis3->pwron_delay / lis3lv02d_get_odr()); |
198 | 278 | |
199 | 279 | results[0] = x - lis3->read_data(lis3, OUTX); |
200 | 280 | |
... | ... | @@ -201,13 +281,33 @@ |
201 | 281 | results[2] = z - lis3->read_data(lis3, OUTZ); |
202 | 282 | |
203 | 283 | ret = 0; |
284 | + | |
285 | + if (lis3_dev.whoami == WAI_8B) { | |
286 | + /* Restore original interrupt configuration */ | |
287 | + atomic_dec(&lis3_dev.wake_thread); | |
288 | + lis3->write(lis3, CTRL_REG3, ctrl_reg_data); | |
289 | + lis3->irq_cfg = irq_cfg; | |
290 | + | |
291 | + if ((irq_cfg & LIS3_IRQ1_MASK) && | |
292 | + lis3->data_ready_count[IRQ_LINE0] < 2) { | |
293 | + ret = SELFTEST_IRQ; | |
294 | + goto fail; | |
295 | + } | |
296 | + | |
297 | + if ((irq_cfg & LIS3_IRQ2_MASK) && | |
298 | + lis3->data_ready_count[IRQ_LINE1] < 2) { | |
299 | + ret = SELFTEST_IRQ; | |
300 | + goto fail; | |
301 | + } | |
302 | + } | |
303 | + | |
204 | 304 | if (lis3->pdata) { |
205 | 305 | int i; |
206 | 306 | for (i = 0; i < 3; i++) { |
207 | 307 | /* Check against selftest acceptance limits */ |
208 | 308 | if ((results[i] < lis3->pdata->st_min_limits[i]) || |
209 | 309 | (results[i] > lis3->pdata->st_max_limits[i])) { |
210 | - ret = -EIO; | |
310 | + ret = SELFTEST_FAIL; | |
211 | 311 | goto fail; |
212 | 312 | } |
213 | 313 | } |
214 | 314 | |
215 | 315 | |
... | ... | @@ -219,10 +319,46 @@ |
219 | 319 | return ret; |
220 | 320 | } |
221 | 321 | |
322 | +/* | |
323 | + * Order of registers in the list affects to order of the restore process. | |
324 | + * Perhaps it is a good idea to set interrupt enable register as a last one | |
325 | + * after all other configurations | |
326 | + */ | |
327 | +static u8 lis3_wai8_regs[] = { FF_WU_CFG_1, FF_WU_THS_1, FF_WU_DURATION_1, | |
328 | + FF_WU_CFG_2, FF_WU_THS_2, FF_WU_DURATION_2, | |
329 | + CLICK_CFG, CLICK_SRC, CLICK_THSY_X, CLICK_THSZ, | |
330 | + CLICK_TIMELIMIT, CLICK_LATENCY, CLICK_WINDOW, | |
331 | + CTRL_REG1, CTRL_REG2, CTRL_REG3}; | |
332 | + | |
333 | +static u8 lis3_wai12_regs[] = {FF_WU_CFG, FF_WU_THS_L, FF_WU_THS_H, | |
334 | + FF_WU_DURATION, DD_CFG, DD_THSI_L, DD_THSI_H, | |
335 | + DD_THSE_L, DD_THSE_H, | |
336 | + CTRL_REG1, CTRL_REG3, CTRL_REG2}; | |
337 | + | |
338 | +static inline void lis3_context_save(struct lis3lv02d *lis3) | |
339 | +{ | |
340 | + int i; | |
341 | + for (i = 0; i < lis3->regs_size; i++) | |
342 | + lis3->read(lis3, lis3->regs[i], &lis3->reg_cache[i]); | |
343 | + lis3->regs_stored = true; | |
344 | +} | |
345 | + | |
346 | +static inline void lis3_context_restore(struct lis3lv02d *lis3) | |
347 | +{ | |
348 | + int i; | |
349 | + if (lis3->regs_stored) | |
350 | + for (i = 0; i < lis3->regs_size; i++) | |
351 | + lis3->write(lis3, lis3->regs[i], lis3->reg_cache[i]); | |
352 | +} | |
353 | + | |
222 | 354 | void lis3lv02d_poweroff(struct lis3lv02d *lis3) |
223 | 355 | { |
356 | + if (lis3->reg_ctrl) | |
357 | + lis3_context_save(lis3); | |
224 | 358 | /* disable X,Y,Z axis and power down */ |
225 | 359 | lis3->write(lis3, CTRL_REG1, 0x00); |
360 | + if (lis3->reg_ctrl) | |
361 | + lis3->reg_ctrl(lis3, LIS3_REG_OFF); | |
226 | 362 | } |
227 | 363 | EXPORT_SYMBOL_GPL(lis3lv02d_poweroff); |
228 | 364 | |
229 | 365 | |
230 | 366 | |
... | ... | @@ -232,19 +368,24 @@ |
232 | 368 | |
233 | 369 | lis3->init(lis3); |
234 | 370 | |
235 | - /* LIS3 power on delay is quite long */ | |
236 | - msleep(lis3->pwron_delay / lis3lv02d_get_odr()); | |
237 | - | |
238 | 371 | /* |
239 | 372 | * Common configuration |
240 | 373 | * BDU: (12 bits sensors only) LSB and MSB values are not updated until |
241 | 374 | * both have been read. So the value read will always be correct. |
375 | + * Set BOOT bit to refresh factory tuning values. | |
242 | 376 | */ |
243 | - if (lis3->whoami == WAI_12B) { | |
244 | - lis3->read(lis3, CTRL_REG2, ®); | |
245 | - reg |= CTRL2_BDU; | |
246 | - lis3->write(lis3, CTRL_REG2, reg); | |
247 | - } | |
377 | + lis3->read(lis3, CTRL_REG2, ®); | |
378 | + if (lis3->whoami == WAI_12B) | |
379 | + reg |= CTRL2_BDU | CTRL2_BOOT; | |
380 | + else | |
381 | + reg |= CTRL2_BOOT_8B; | |
382 | + lis3->write(lis3, CTRL_REG2, reg); | |
383 | + | |
384 | + /* LIS3 power on delay is quite long */ | |
385 | + msleep(lis3->pwron_delay / lis3lv02d_get_odr()); | |
386 | + | |
387 | + if (lis3->reg_ctrl) | |
388 | + lis3_context_restore(lis3); | |
248 | 389 | } |
249 | 390 | EXPORT_SYMBOL_GPL(lis3lv02d_poweron); |
250 | 391 | |
... | ... | @@ -262,6 +403,27 @@ |
262 | 403 | mutex_unlock(&lis3_dev.mutex); |
263 | 404 | } |
264 | 405 | |
406 | +static void lis3lv02d_joystick_open(struct input_polled_dev *pidev) | |
407 | +{ | |
408 | + if (lis3_dev.pm_dev) | |
409 | + pm_runtime_get_sync(lis3_dev.pm_dev); | |
410 | + | |
411 | + if (lis3_dev.pdata && lis3_dev.whoami == WAI_8B && lis3_dev.idev) | |
412 | + atomic_set(&lis3_dev.wake_thread, 1); | |
413 | + /* | |
414 | + * Update coordinates for the case where poll interval is 0 and | |
415 | + * the chip in running purely under interrupt control | |
416 | + */ | |
417 | + lis3lv02d_joystick_poll(pidev); | |
418 | +} | |
419 | + | |
420 | +static void lis3lv02d_joystick_close(struct input_polled_dev *pidev) | |
421 | +{ | |
422 | + atomic_set(&lis3_dev.wake_thread, 0); | |
423 | + if (lis3_dev.pm_dev) | |
424 | + pm_runtime_put(lis3_dev.pm_dev); | |
425 | +} | |
426 | + | |
265 | 427 | static irqreturn_t lis302dl_interrupt(int irq, void *dummy) |
266 | 428 | { |
267 | 429 | if (!test_bit(0, &lis3_dev.misc_opened)) |
... | ... | @@ -277,8 +439,7 @@ |
277 | 439 | wake_up_interruptible(&lis3_dev.misc_wait); |
278 | 440 | kill_fasync(&lis3_dev.async_queue, SIGIO, POLL_IN); |
279 | 441 | out: |
280 | - if (lis3_dev.pdata && lis3_dev.whoami == WAI_8B && lis3_dev.idev && | |
281 | - lis3_dev.idev->input->users) | |
442 | + if (atomic_read(&lis3_dev.wake_thread)) | |
282 | 443 | return IRQ_WAKE_THREAD; |
283 | 444 | return IRQ_HANDLED; |
284 | 445 | } |
285 | 446 | |
286 | 447 | |
287 | 448 | |
288 | 449 | |
289 | 450 | |
290 | 451 | |
291 | 452 | |
292 | 453 | |
293 | 454 | |
294 | 455 | |
295 | 456 | |
296 | 457 | |
... | ... | @@ -309,44 +470,41 @@ |
309 | 470 | mutex_unlock(&lis3->mutex); |
310 | 471 | } |
311 | 472 | |
312 | -static void lis302dl_interrupt_handle_ff_wu(struct lis3lv02d *lis3) | |
473 | +static inline void lis302dl_data_ready(struct lis3lv02d *lis3, int index) | |
313 | 474 | { |
314 | - u8 wu1_src; | |
315 | - u8 wu2_src; | |
475 | + int dummy; | |
316 | 476 | |
317 | - lis3->read(lis3, FF_WU_SRC_1, &wu1_src); | |
318 | - lis3->read(lis3, FF_WU_SRC_2, &wu2_src); | |
319 | - | |
320 | - wu1_src = wu1_src & FF_WU_SRC_IA ? wu1_src : 0; | |
321 | - wu2_src = wu2_src & FF_WU_SRC_IA ? wu2_src : 0; | |
322 | - | |
323 | - /* joystick poll is internally protected by the lis3->mutex. */ | |
324 | - if (wu1_src || wu2_src) | |
325 | - lis3lv02d_joystick_poll(lis3_dev.idev); | |
477 | + /* Dummy read to ack interrupt */ | |
478 | + lis3lv02d_get_xyz(lis3, &dummy, &dummy, &dummy); | |
479 | + lis3->data_ready_count[index]++; | |
326 | 480 | } |
327 | 481 | |
328 | 482 | static irqreturn_t lis302dl_interrupt_thread1_8b(int irq, void *data) |
329 | 483 | { |
330 | - | |
331 | 484 | struct lis3lv02d *lis3 = data; |
485 | + u8 irq_cfg = lis3->irq_cfg & LIS3_IRQ1_MASK; | |
332 | 486 | |
333 | - if ((lis3->pdata->irq_cfg & LIS3_IRQ1_MASK) == LIS3_IRQ1_CLICK) | |
487 | + if (irq_cfg == LIS3_IRQ1_CLICK) | |
334 | 488 | lis302dl_interrupt_handle_click(lis3); |
489 | + else if (unlikely(irq_cfg == LIS3_IRQ1_DATA_READY)) | |
490 | + lis302dl_data_ready(lis3, IRQ_LINE0); | |
335 | 491 | else |
336 | - lis302dl_interrupt_handle_ff_wu(lis3); | |
492 | + lis3lv02d_joystick_poll(lis3->idev); | |
337 | 493 | |
338 | 494 | return IRQ_HANDLED; |
339 | 495 | } |
340 | 496 | |
341 | 497 | static irqreturn_t lis302dl_interrupt_thread2_8b(int irq, void *data) |
342 | 498 | { |
343 | - | |
344 | 499 | struct lis3lv02d *lis3 = data; |
500 | + u8 irq_cfg = lis3->irq_cfg & LIS3_IRQ2_MASK; | |
345 | 501 | |
346 | - if ((lis3->pdata->irq_cfg & LIS3_IRQ2_MASK) == LIS3_IRQ2_CLICK) | |
502 | + if (irq_cfg == LIS3_IRQ2_CLICK) | |
347 | 503 | lis302dl_interrupt_handle_click(lis3); |
504 | + else if (unlikely(irq_cfg == LIS3_IRQ2_DATA_READY)) | |
505 | + lis302dl_data_ready(lis3, IRQ_LINE1); | |
348 | 506 | else |
349 | - lis302dl_interrupt_handle_ff_wu(lis3); | |
507 | + lis3lv02d_joystick_poll(lis3->idev); | |
350 | 508 | |
351 | 509 | return IRQ_HANDLED; |
352 | 510 | } |
... | ... | @@ -356,6 +514,9 @@ |
356 | 514 | if (test_and_set_bit(0, &lis3_dev.misc_opened)) |
357 | 515 | return -EBUSY; /* already open */ |
358 | 516 | |
517 | + if (lis3_dev.pm_dev) | |
518 | + pm_runtime_get_sync(lis3_dev.pm_dev); | |
519 | + | |
359 | 520 | atomic_set(&lis3_dev.count, 0); |
360 | 521 | return 0; |
361 | 522 | } |
... | ... | @@ -364,6 +525,8 @@ |
364 | 525 | { |
365 | 526 | fasync_helper(-1, file, 0, &lis3_dev.async_queue); |
366 | 527 | clear_bit(0, &lis3_dev.misc_opened); /* release the device */ |
528 | + if (lis3_dev.pm_dev) | |
529 | + pm_runtime_put(lis3_dev.pm_dev); | |
367 | 530 | return 0; |
368 | 531 | } |
369 | 532 | |
... | ... | @@ -460,6 +623,8 @@ |
460 | 623 | return -ENOMEM; |
461 | 624 | |
462 | 625 | lis3_dev.idev->poll = lis3lv02d_joystick_poll; |
626 | + lis3_dev.idev->open = lis3lv02d_joystick_open; | |
627 | + lis3_dev.idev->close = lis3lv02d_joystick_close; | |
463 | 628 | lis3_dev.idev->poll_interval = MDPS_POLL_INTERVAL; |
464 | 629 | lis3_dev.idev->poll_interval_min = MDPS_POLL_MIN; |
465 | 630 | lis3_dev.idev->poll_interval_max = MDPS_POLL_MAX; |
... | ... | @@ -473,8 +638,16 @@ |
473 | 638 | |
474 | 639 | set_bit(EV_ABS, input_dev->evbit); |
475 | 640 | max_val = (lis3_dev.mdps_max_val * lis3_dev.scale) / LIS3_ACCURACY; |
476 | - fuzz = (LIS3_DEFAULT_FUZZ * lis3_dev.scale) / LIS3_ACCURACY; | |
477 | - flat = (LIS3_DEFAULT_FLAT * lis3_dev.scale) / LIS3_ACCURACY; | |
641 | + if (lis3_dev.whoami == WAI_12B) { | |
642 | + fuzz = LIS3_DEFAULT_FUZZ_12B; | |
643 | + flat = LIS3_DEFAULT_FLAT_12B; | |
644 | + } else { | |
645 | + fuzz = LIS3_DEFAULT_FUZZ_8B; | |
646 | + flat = LIS3_DEFAULT_FLAT_8B; | |
647 | + } | |
648 | + fuzz = (fuzz * lis3_dev.scale) / LIS3_ACCURACY; | |
649 | + flat = (flat * lis3_dev.scale) / LIS3_ACCURACY; | |
650 | + | |
478 | 651 | input_set_abs_params(input_dev, ABS_X, -max_val, max_val, fuzz, flat); |
479 | 652 | input_set_abs_params(input_dev, ABS_Y, -max_val, max_val, fuzz, flat); |
480 | 653 | input_set_abs_params(input_dev, ABS_Z, -max_val, max_val, fuzz, flat); |
481 | 654 | |
482 | 655 | |
... | ... | @@ -512,14 +685,47 @@ |
512 | 685 | EXPORT_SYMBOL_GPL(lis3lv02d_joystick_disable); |
513 | 686 | |
514 | 687 | /* Sysfs stuff */ |
688 | +static void lis3lv02d_sysfs_poweron(struct lis3lv02d *lis3) | |
689 | +{ | |
690 | + /* | |
691 | + * SYSFS functions are fast visitors so put-call | |
692 | + * immediately after the get-call. However, keep | |
693 | + * chip running for a while and schedule delayed | |
694 | + * suspend. This way periodic sysfs calls doesn't | |
695 | + * suffer from relatively long power up time. | |
696 | + */ | |
697 | + | |
698 | + if (lis3->pm_dev) { | |
699 | + pm_runtime_get_sync(lis3->pm_dev); | |
700 | + pm_runtime_put_noidle(lis3->pm_dev); | |
701 | + pm_schedule_suspend(lis3->pm_dev, LIS3_SYSFS_POWERDOWN_DELAY); | |
702 | + } | |
703 | +} | |
704 | + | |
515 | 705 | static ssize_t lis3lv02d_selftest_show(struct device *dev, |
516 | 706 | struct device_attribute *attr, char *buf) |
517 | 707 | { |
518 | - int result; | |
519 | 708 | s16 values[3]; |
520 | 709 | |
521 | - result = lis3lv02d_selftest(&lis3_dev, values); | |
522 | - return sprintf(buf, "%s %d %d %d\n", result == 0 ? "OK" : "FAIL", | |
710 | + static const char ok[] = "OK"; | |
711 | + static const char fail[] = "FAIL"; | |
712 | + static const char irq[] = "FAIL_IRQ"; | |
713 | + const char *res; | |
714 | + | |
715 | + lis3lv02d_sysfs_poweron(&lis3_dev); | |
716 | + switch (lis3lv02d_selftest(&lis3_dev, values)) { | |
717 | + case SELFTEST_FAIL: | |
718 | + res = fail; | |
719 | + break; | |
720 | + case SELFTEST_IRQ: | |
721 | + res = irq; | |
722 | + break; | |
723 | + case SELFTEST_OK: | |
724 | + default: | |
725 | + res = ok; | |
726 | + break; | |
727 | + } | |
728 | + return sprintf(buf, "%s %d %d %d\n", res, | |
523 | 729 | values[0], values[1], values[2]); |
524 | 730 | } |
525 | 731 | |
... | ... | @@ -528,6 +734,7 @@ |
528 | 734 | { |
529 | 735 | int x, y, z; |
530 | 736 | |
737 | + lis3lv02d_sysfs_poweron(&lis3_dev); | |
531 | 738 | mutex_lock(&lis3_dev.mutex); |
532 | 739 | lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z); |
533 | 740 | mutex_unlock(&lis3_dev.mutex); |
... | ... | @@ -537,6 +744,7 @@ |
537 | 744 | static ssize_t lis3lv02d_rate_show(struct device *dev, |
538 | 745 | struct device_attribute *attr, char *buf) |
539 | 746 | { |
747 | + lis3lv02d_sysfs_poweron(&lis3_dev); | |
540 | 748 | return sprintf(buf, "%d\n", lis3lv02d_get_odr()); |
541 | 749 | } |
542 | 750 | |
... | ... | @@ -549,6 +757,7 @@ |
549 | 757 | if (strict_strtoul(buf, 0, &rate)) |
550 | 758 | return -EINVAL; |
551 | 759 | |
760 | + lis3lv02d_sysfs_poweron(&lis3_dev); | |
552 | 761 | if (lis3lv02d_set_odr(rate)) |
553 | 762 | return -EINVAL; |
554 | 763 | |
... | ... | @@ -585,6 +794,18 @@ |
585 | 794 | { |
586 | 795 | sysfs_remove_group(&lis3->pdev->dev.kobj, &lis3lv02d_attribute_group); |
587 | 796 | platform_device_unregister(lis3->pdev); |
797 | + if (lis3->pm_dev) { | |
798 | + /* Barrier after the sysfs remove */ | |
799 | + pm_runtime_barrier(lis3->pm_dev); | |
800 | + | |
801 | + /* SYSFS may have left chip running. Turn off if necessary */ | |
802 | + if (!pm_runtime_suspended(lis3->pm_dev)) | |
803 | + lis3lv02d_poweroff(&lis3_dev); | |
804 | + | |
805 | + pm_runtime_disable(lis3->pm_dev); | |
806 | + pm_runtime_set_suspended(lis3->pm_dev); | |
807 | + } | |
808 | + kfree(lis3->reg_cache); | |
588 | 809 | return 0; |
589 | 810 | } |
590 | 811 | EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs); |
591 | 812 | |
... | ... | @@ -616,16 +837,16 @@ |
616 | 837 | if (p->wakeup_flags) { |
617 | 838 | dev->write(dev, FF_WU_CFG_1, p->wakeup_flags); |
618 | 839 | dev->write(dev, FF_WU_THS_1, p->wakeup_thresh & 0x7f); |
619 | - /* default to 2.5ms for now */ | |
620 | - dev->write(dev, FF_WU_DURATION_1, 1); | |
840 | + /* pdata value + 1 to keep this backward compatible*/ | |
841 | + dev->write(dev, FF_WU_DURATION_1, p->duration1 + 1); | |
621 | 842 | ctrl2 ^= HP_FF_WU1; /* Xor to keep compatible with old pdata*/ |
622 | 843 | } |
623 | 844 | |
624 | 845 | if (p->wakeup_flags2) { |
625 | 846 | dev->write(dev, FF_WU_CFG_2, p->wakeup_flags2); |
626 | 847 | dev->write(dev, FF_WU_THS_2, p->wakeup_thresh2 & 0x7f); |
627 | - /* default to 2.5ms for now */ | |
628 | - dev->write(dev, FF_WU_DURATION_2, 1); | |
848 | + /* pdata value + 1 to keep this backward compatible*/ | |
849 | + dev->write(dev, FF_WU_DURATION_2, p->duration2 + 1); | |
629 | 850 | ctrl2 ^= HP_FF_WU2; /* Xor to keep compatible with old pdata*/ |
630 | 851 | } |
631 | 852 | /* Configure hipass filters */ |
... | ... | @@ -635,8 +856,8 @@ |
635 | 856 | err = request_threaded_irq(p->irq2, |
636 | 857 | NULL, |
637 | 858 | lis302dl_interrupt_thread2_8b, |
638 | - IRQF_TRIGGER_RISING | | |
639 | - IRQF_ONESHOT, | |
859 | + IRQF_TRIGGER_RISING | IRQF_ONESHOT | | |
860 | + (p->irq_flags2 & IRQF_TRIGGER_MASK), | |
640 | 861 | DRIVER_NAME, &lis3_dev); |
641 | 862 | if (err < 0) |
642 | 863 | printk(KERN_ERR DRIVER_NAME |
... | ... | @@ -652,6 +873,7 @@ |
652 | 873 | { |
653 | 874 | int err; |
654 | 875 | irq_handler_t thread_fn; |
876 | + int irq_flags = 0; | |
655 | 877 | |
656 | 878 | dev->whoami = lis3lv02d_read_8(dev, WHO_AM_I); |
657 | 879 | |
... | ... | @@ -664,6 +886,8 @@ |
664 | 886 | dev->odrs = lis3_12_rates; |
665 | 887 | dev->odr_mask = CTRL1_DF0 | CTRL1_DF1; |
666 | 888 | dev->scale = LIS3_SENSITIVITY_12B; |
889 | + dev->regs = lis3_wai12_regs; | |
890 | + dev->regs_size = ARRAY_SIZE(lis3_wai12_regs); | |
667 | 891 | break; |
668 | 892 | case WAI_8B: |
669 | 893 | printk(KERN_INFO DRIVER_NAME ": 8 bits sensor found\n"); |
670 | 894 | |
671 | 895 | |
672 | 896 | |
673 | 897 | |
... | ... | @@ -673,18 +897,43 @@ |
673 | 897 | dev->odrs = lis3_8_rates; |
674 | 898 | dev->odr_mask = CTRL1_DR; |
675 | 899 | dev->scale = LIS3_SENSITIVITY_8B; |
900 | + dev->regs = lis3_wai8_regs; | |
901 | + dev->regs_size = ARRAY_SIZE(lis3_wai8_regs); | |
676 | 902 | break; |
903 | + case WAI_3DC: | |
904 | + printk(KERN_INFO DRIVER_NAME ": 8 bits 3DC sensor found\n"); | |
905 | + dev->read_data = lis3lv02d_read_8; | |
906 | + dev->mdps_max_val = 128; | |
907 | + dev->pwron_delay = LIS3_PWRON_DELAY_WAI_8B; | |
908 | + dev->odrs = lis3_3dc_rates; | |
909 | + dev->odr_mask = CTRL1_ODR0|CTRL1_ODR1|CTRL1_ODR2|CTRL1_ODR3; | |
910 | + dev->scale = LIS3_SENSITIVITY_8B; | |
911 | + break; | |
677 | 912 | default: |
678 | 913 | printk(KERN_ERR DRIVER_NAME |
679 | 914 | ": unknown sensor type 0x%X\n", dev->whoami); |
680 | 915 | return -EINVAL; |
681 | 916 | } |
682 | 917 | |
918 | + dev->reg_cache = kzalloc(max(sizeof(lis3_wai8_regs), | |
919 | + sizeof(lis3_wai12_regs)), GFP_KERNEL); | |
920 | + | |
921 | + if (dev->reg_cache == NULL) { | |
922 | + printk(KERN_ERR DRIVER_NAME "out of memory\n"); | |
923 | + return -ENOMEM; | |
924 | + } | |
925 | + | |
683 | 926 | mutex_init(&dev->mutex); |
927 | + atomic_set(&dev->wake_thread, 0); | |
684 | 928 | |
685 | 929 | lis3lv02d_add_fs(dev); |
686 | 930 | lis3lv02d_poweron(dev); |
687 | 931 | |
932 | + if (dev->pm_dev) { | |
933 | + pm_runtime_set_active(dev->pm_dev); | |
934 | + pm_runtime_enable(dev->pm_dev); | |
935 | + } | |
936 | + | |
688 | 937 | if (lis3lv02d_joystick_enable()) |
689 | 938 | printk(KERN_ERR DRIVER_NAME ": joystick initialization failed\n"); |
690 | 939 | |
691 | 940 | |
... | ... | @@ -696,8 +945,14 @@ |
696 | 945 | if (dev->whoami == WAI_8B) |
697 | 946 | lis3lv02d_8b_configure(dev, p); |
698 | 947 | |
948 | + irq_flags = p->irq_flags1 & IRQF_TRIGGER_MASK; | |
949 | + | |
950 | + dev->irq_cfg = p->irq_cfg; | |
699 | 951 | if (p->irq_cfg) |
700 | 952 | dev->write(dev, CTRL_REG3, p->irq_cfg); |
953 | + | |
954 | + if (p->default_rate) | |
955 | + lis3lv02d_set_odr(p->default_rate); | |
701 | 956 | } |
702 | 957 | |
703 | 958 | /* bail if we did not get an IRQ from the bus layer */ |
... | ... | @@ -725,7 +980,8 @@ |
725 | 980 | |
726 | 981 | err = request_threaded_irq(dev->irq, lis302dl_interrupt, |
727 | 982 | thread_fn, |
728 | - IRQF_TRIGGER_RISING | IRQF_ONESHOT, | |
983 | + IRQF_TRIGGER_RISING | IRQF_ONESHOT | | |
984 | + irq_flags, | |
729 | 985 | DRIVER_NAME, &lis3_dev); |
730 | 986 | |
731 | 987 | if (err < 0) { |
drivers/hwmon/lis3lv02d.h
... | ... | @@ -20,6 +20,7 @@ |
20 | 20 | */ |
21 | 21 | #include <linux/platform_device.h> |
22 | 22 | #include <linux/input-polldev.h> |
23 | +#include <linux/regulator/consumer.h> | |
23 | 24 | |
24 | 25 | /* |
25 | 26 | * This driver tries to support the "digital" accelerometer chips from |
... | ... | @@ -45,6 +46,7 @@ |
45 | 46 | CTRL_REG1 = 0x20, |
46 | 47 | CTRL_REG2 = 0x21, |
47 | 48 | CTRL_REG3 = 0x22, |
49 | + CTRL_REG4 = 0x23, | |
48 | 50 | HP_FILTER_RESET = 0x23, |
49 | 51 | STATUS_REG = 0x27, |
50 | 52 | OUTX_L = 0x28, |
... | ... | @@ -93,6 +95,7 @@ |
93 | 95 | }; |
94 | 96 | |
95 | 97 | enum lis3_who_am_i { |
98 | + WAI_3DC = 0x33, /* 8 bits: LIS3DC, HP3DC */ | |
96 | 99 | WAI_12B = 0x3A, /* 12 bits: LIS3LV02D[LQ]... */ |
97 | 100 | WAI_8B = 0x3B, /* 8 bits: LIS[23]02D[LQ]... */ |
98 | 101 | WAI_6B = 0x52, /* 6 bits: LIS331DLF - not supported */ |
... | ... | @@ -118,6 +121,13 @@ |
118 | 121 | CTRL1_DR = 0x80, |
119 | 122 | }; |
120 | 123 | |
124 | +enum lis3lv02d_ctrl1_3dc { | |
125 | + CTRL1_ODR0 = 0x10, | |
126 | + CTRL1_ODR1 = 0x20, | |
127 | + CTRL1_ODR2 = 0x40, | |
128 | + CTRL1_ODR3 = 0x80, | |
129 | +}; | |
130 | + | |
121 | 131 | enum lis3lv02d_ctrl2 { |
122 | 132 | CTRL2_DAS = 0x01, |
123 | 133 | CTRL2_SIM = 0x02, |
124 | 134 | |
... | ... | @@ -129,9 +139,18 @@ |
129 | 139 | CTRL2_FS = 0x80, /* Full Scale selection */ |
130 | 140 | }; |
131 | 141 | |
142 | +enum lis3lv02d_ctrl4_3dc { | |
143 | + CTRL4_SIM = 0x01, | |
144 | + CTRL4_ST0 = 0x02, | |
145 | + CTRL4_ST1 = 0x04, | |
146 | + CTRL4_FS0 = 0x10, | |
147 | + CTRL4_FS1 = 0x20, | |
148 | +}; | |
149 | + | |
132 | 150 | enum lis302d_ctrl2 { |
133 | 151 | HP_FF_WU2 = 0x08, |
134 | 152 | HP_FF_WU1 = 0x04, |
153 | + CTRL2_BOOT_8B = 0x40, | |
135 | 154 | }; |
136 | 155 | |
137 | 156 | enum lis3lv02d_ctrl3 { |
138 | 157 | |
139 | 158 | |
140 | 159 | |
141 | 160 | |
... | ... | @@ -206,19 +225,33 @@ |
206 | 225 | CLICK_IA = 0x40, |
207 | 226 | }; |
208 | 227 | |
209 | -struct axis_conversion { | |
210 | - s8 x; | |
211 | - s8 y; | |
212 | - s8 z; | |
228 | +enum lis3lv02d_reg_state { | |
229 | + LIS3_REG_OFF = 0x00, | |
230 | + LIS3_REG_ON = 0x01, | |
213 | 231 | }; |
214 | 232 | |
233 | +union axis_conversion { | |
234 | + struct { | |
235 | + int x, y, z; | |
236 | + }; | |
237 | + int as_array[3]; | |
238 | + | |
239 | +}; | |
240 | + | |
215 | 241 | struct lis3lv02d { |
216 | 242 | void *bus_priv; /* used by the bus layer only */ |
243 | + struct device *pm_dev; /* for pm_runtime purposes */ | |
217 | 244 | int (*init) (struct lis3lv02d *lis3); |
218 | 245 | int (*write) (struct lis3lv02d *lis3, int reg, u8 val); |
219 | 246 | int (*read) (struct lis3lv02d *lis3, int reg, u8 *ret); |
247 | + int (*blkread) (struct lis3lv02d *lis3, int reg, int len, u8 *ret); | |
248 | + int (*reg_ctrl) (struct lis3lv02d *lis3, bool state); | |
220 | 249 | |
221 | 250 | int *odrs; /* Supported output data rates */ |
251 | + u8 *regs; /* Regs to store / restore */ | |
252 | + int regs_size; | |
253 | + u8 *reg_cache; | |
254 | + bool regs_stored; | |
222 | 255 | u8 odr_mask; /* ODR bit mask */ |
223 | 256 | u8 whoami; /* indicates measurement precision */ |
224 | 257 | s16 (*read_data) (struct lis3lv02d *lis3, int reg); |
225 | 258 | |
226 | 259 | |
... | ... | @@ -231,14 +264,18 @@ |
231 | 264 | |
232 | 265 | struct input_polled_dev *idev; /* input device */ |
233 | 266 | struct platform_device *pdev; /* platform device */ |
267 | + struct regulator_bulk_data regulators[2]; | |
234 | 268 | atomic_t count; /* interrupt count after last read */ |
235 | - struct axis_conversion ac; /* hw -> logical axis */ | |
269 | + union axis_conversion ac; /* hw -> logical axis */ | |
236 | 270 | int mapped_btns[3]; |
237 | 271 | |
238 | 272 | u32 irq; /* IRQ number */ |
239 | 273 | struct fasync_struct *async_queue; /* queue for the misc device */ |
240 | 274 | wait_queue_head_t misc_wait; /* Wait queue for the misc device */ |
241 | 275 | unsigned long misc_opened; /* bit0: whether the device is open */ |
276 | + int data_ready_count[2]; | |
277 | + atomic_t wake_thread; | |
278 | + unsigned char irq_cfg; | |
242 | 279 | |
243 | 280 | struct lis3lv02d_platform_data *pdata; /* for passing board config */ |
244 | 281 | struct mutex mutex; /* Serialize poll and selftest */ |
drivers/hwmon/lis3lv02d_i2c.c
... | ... | @@ -29,10 +29,30 @@ |
29 | 29 | #include <linux/init.h> |
30 | 30 | #include <linux/err.h> |
31 | 31 | #include <linux/i2c.h> |
32 | +#include <linux/pm_runtime.h> | |
33 | +#include <linux/delay.h> | |
32 | 34 | #include "lis3lv02d.h" |
33 | 35 | |
34 | 36 | #define DRV_NAME "lis3lv02d_i2c" |
35 | 37 | |
38 | +static const char reg_vdd[] = "Vdd"; | |
39 | +static const char reg_vdd_io[] = "Vdd_IO"; | |
40 | + | |
41 | +static int lis3_reg_ctrl(struct lis3lv02d *lis3, bool state) | |
42 | +{ | |
43 | + int ret; | |
44 | + if (state == LIS3_REG_OFF) { | |
45 | + ret = regulator_bulk_disable(ARRAY_SIZE(lis3->regulators), | |
46 | + lis3->regulators); | |
47 | + } else { | |
48 | + ret = regulator_bulk_enable(ARRAY_SIZE(lis3->regulators), | |
49 | + lis3->regulators); | |
50 | + /* Chip needs time to wakeup. Not mentioned in datasheet */ | |
51 | + usleep_range(10000, 20000); | |
52 | + } | |
53 | + return ret; | |
54 | +} | |
55 | + | |
36 | 56 | static inline s32 lis3_i2c_write(struct lis3lv02d *lis3, int reg, u8 value) |
37 | 57 | { |
38 | 58 | struct i2c_client *c = lis3->bus_priv; |
39 | 59 | |
40 | 60 | |
41 | 61 | |
... | ... | @@ -46,24 +66,38 @@ |
46 | 66 | return 0; |
47 | 67 | } |
48 | 68 | |
69 | +static inline s32 lis3_i2c_blockread(struct lis3lv02d *lis3, int reg, int len, | |
70 | + u8 *v) | |
71 | +{ | |
72 | + struct i2c_client *c = lis3->bus_priv; | |
73 | + reg |= (1 << 7); /* 7th bit enables address auto incrementation */ | |
74 | + return i2c_smbus_read_i2c_block_data(c, reg, len, v); | |
75 | +} | |
76 | + | |
49 | 77 | static int lis3_i2c_init(struct lis3lv02d *lis3) |
50 | 78 | { |
51 | 79 | u8 reg; |
52 | 80 | int ret; |
53 | 81 | |
82 | + if (lis3->reg_ctrl) | |
83 | + lis3_reg_ctrl(lis3, LIS3_REG_ON); | |
84 | + | |
85 | + lis3->read(lis3, WHO_AM_I, ®); | |
86 | + if (reg != lis3->whoami) | |
87 | + printk(KERN_ERR "lis3: power on failure\n"); | |
88 | + | |
54 | 89 | /* power up the device */ |
55 | 90 | ret = lis3->read(lis3, CTRL_REG1, ®); |
56 | 91 | if (ret < 0) |
57 | 92 | return ret; |
58 | 93 | |
59 | - reg |= CTRL1_PD0; | |
94 | + reg |= CTRL1_PD0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen; | |
60 | 95 | return lis3->write(lis3, CTRL_REG1, reg); |
61 | 96 | } |
62 | 97 | |
63 | 98 | /* Default axis mapping but it can be overwritten by platform data */ |
64 | -static struct axis_conversion lis3lv02d_axis_map = { LIS3_DEV_X, | |
65 | - LIS3_DEV_Y, | |
66 | - LIS3_DEV_Z }; | |
99 | +static union axis_conversion lis3lv02d_axis_map = | |
100 | + { .as_array = { LIS3_DEV_X, LIS3_DEV_Y, LIS3_DEV_Z } }; | |
67 | 101 | |
68 | 102 | static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client, |
69 | 103 | const struct i2c_device_id *id) |
... | ... | @@ -72,6 +106,15 @@ |
72 | 106 | struct lis3lv02d_platform_data *pdata = client->dev.platform_data; |
73 | 107 | |
74 | 108 | if (pdata) { |
109 | + /* Regulator control is optional */ | |
110 | + if (pdata->driver_features & LIS3_USE_REGULATOR_CTRL) | |
111 | + lis3_dev.reg_ctrl = lis3_reg_ctrl; | |
112 | + | |
113 | + if ((pdata->driver_features & LIS3_USE_BLOCK_READ) && | |
114 | + (i2c_check_functionality(client->adapter, | |
115 | + I2C_FUNC_SMBUS_I2C_BLOCK))) | |
116 | + lis3_dev.blkread = lis3_i2c_blockread; | |
117 | + | |
75 | 118 | if (pdata->axis_x) |
76 | 119 | lis3lv02d_axis_map.x = pdata->axis_x; |
77 | 120 | |
... | ... | @@ -88,6 +131,16 @@ |
88 | 131 | goto fail; |
89 | 132 | } |
90 | 133 | |
134 | + if (lis3_dev.reg_ctrl) { | |
135 | + lis3_dev.regulators[0].supply = reg_vdd; | |
136 | + lis3_dev.regulators[1].supply = reg_vdd_io; | |
137 | + ret = regulator_bulk_get(&client->dev, | |
138 | + ARRAY_SIZE(lis3_dev.regulators), | |
139 | + lis3_dev.regulators); | |
140 | + if (ret < 0) | |
141 | + goto fail; | |
142 | + } | |
143 | + | |
91 | 144 | lis3_dev.pdata = pdata; |
92 | 145 | lis3_dev.bus_priv = client; |
93 | 146 | lis3_dev.init = lis3_i2c_init; |
94 | 147 | |
95 | 148 | |
96 | 149 | |
... | ... | @@ -95,10 +148,24 @@ |
95 | 148 | lis3_dev.write = lis3_i2c_write; |
96 | 149 | lis3_dev.irq = client->irq; |
97 | 150 | lis3_dev.ac = lis3lv02d_axis_map; |
151 | + lis3_dev.pm_dev = &client->dev; | |
98 | 152 | |
99 | 153 | i2c_set_clientdata(client, &lis3_dev); |
154 | + | |
155 | + /* Provide power over the init call */ | |
156 | + if (lis3_dev.reg_ctrl) | |
157 | + lis3_reg_ctrl(&lis3_dev, LIS3_REG_ON); | |
158 | + | |
100 | 159 | ret = lis3lv02d_init_device(&lis3_dev); |
160 | + | |
161 | + if (lis3_dev.reg_ctrl) | |
162 | + lis3_reg_ctrl(&lis3_dev, LIS3_REG_OFF); | |
163 | + | |
164 | + if (ret == 0) | |
165 | + return 0; | |
101 | 166 | fail: |
167 | + if (pdata && pdata->release_resources) | |
168 | + pdata->release_resources(); | |
102 | 169 | return ret; |
103 | 170 | } |
104 | 171 | |
105 | 172 | |
106 | 173 | |
107 | 174 | |
... | ... | @@ -111,14 +178,18 @@ |
111 | 178 | pdata->release_resources(); |
112 | 179 | |
113 | 180 | lis3lv02d_joystick_disable(); |
114 | - lis3lv02d_poweroff(lis3); | |
181 | + lis3lv02d_remove_fs(&lis3_dev); | |
115 | 182 | |
116 | - return lis3lv02d_remove_fs(&lis3_dev); | |
183 | + if (lis3_dev.reg_ctrl) | |
184 | + regulator_bulk_free(ARRAY_SIZE(lis3->regulators), | |
185 | + lis3_dev.regulators); | |
186 | + return 0; | |
117 | 187 | } |
118 | 188 | |
119 | 189 | #ifdef CONFIG_PM |
120 | -static int lis3lv02d_i2c_suspend(struct i2c_client *client, pm_message_t mesg) | |
190 | +static int lis3lv02d_i2c_suspend(struct device *dev) | |
121 | 191 | { |
192 | + struct i2c_client *client = container_of(dev, struct i2c_client, dev); | |
122 | 193 | struct lis3lv02d *lis3 = i2c_get_clientdata(client); |
123 | 194 | |
124 | 195 | if (!lis3->pdata || !lis3->pdata->wakeup_flags) |
125 | 196 | |
126 | 197 | |
127 | 198 | |
128 | 199 | |
129 | 200 | |
... | ... | @@ -126,25 +197,46 @@ |
126 | 197 | return 0; |
127 | 198 | } |
128 | 199 | |
129 | -static int lis3lv02d_i2c_resume(struct i2c_client *client) | |
200 | +static int lis3lv02d_i2c_resume(struct device *dev) | |
130 | 201 | { |
202 | + struct i2c_client *client = container_of(dev, struct i2c_client, dev); | |
131 | 203 | struct lis3lv02d *lis3 = i2c_get_clientdata(client); |
132 | 204 | |
133 | - if (!lis3->pdata || !lis3->pdata->wakeup_flags) | |
205 | + /* | |
206 | + * pm_runtime documentation says that devices should always | |
207 | + * be powered on at resume. Pm_runtime turns them off after system | |
208 | + * wide resume is complete. | |
209 | + */ | |
210 | + if (!lis3->pdata || !lis3->pdata->wakeup_flags || | |
211 | + pm_runtime_suspended(dev)) | |
134 | 212 | lis3lv02d_poweron(lis3); |
213 | + | |
135 | 214 | return 0; |
136 | 215 | } |
137 | - | |
138 | -static void lis3lv02d_i2c_shutdown(struct i2c_client *client) | |
139 | -{ | |
140 | - lis3lv02d_i2c_suspend(client, PMSG_SUSPEND); | |
141 | -} | |
142 | 216 | #else |
143 | 217 | #define lis3lv02d_i2c_suspend NULL |
144 | 218 | #define lis3lv02d_i2c_resume NULL |
145 | 219 | #define lis3lv02d_i2c_shutdown NULL |
146 | 220 | #endif |
147 | 221 | |
222 | +static int lis3_i2c_runtime_suspend(struct device *dev) | |
223 | +{ | |
224 | + struct i2c_client *client = container_of(dev, struct i2c_client, dev); | |
225 | + struct lis3lv02d *lis3 = i2c_get_clientdata(client); | |
226 | + | |
227 | + lis3lv02d_poweroff(lis3); | |
228 | + return 0; | |
229 | +} | |
230 | + | |
231 | +static int lis3_i2c_runtime_resume(struct device *dev) | |
232 | +{ | |
233 | + struct i2c_client *client = container_of(dev, struct i2c_client, dev); | |
234 | + struct lis3lv02d *lis3 = i2c_get_clientdata(client); | |
235 | + | |
236 | + lis3lv02d_poweron(lis3); | |
237 | + return 0; | |
238 | +} | |
239 | + | |
148 | 240 | static const struct i2c_device_id lis3lv02d_id[] = { |
149 | 241 | {"lis3lv02d", 0 }, |
150 | 242 | {} |
151 | 243 | |
152 | 244 | |
... | ... | @@ -152,14 +244,20 @@ |
152 | 244 | |
153 | 245 | MODULE_DEVICE_TABLE(i2c, lis3lv02d_id); |
154 | 246 | |
247 | +static const struct dev_pm_ops lis3_pm_ops = { | |
248 | + SET_SYSTEM_SLEEP_PM_OPS(lis3lv02d_i2c_suspend, | |
249 | + lis3lv02d_i2c_resume) | |
250 | + SET_RUNTIME_PM_OPS(lis3_i2c_runtime_suspend, | |
251 | + lis3_i2c_runtime_resume, | |
252 | + NULL) | |
253 | +}; | |
254 | + | |
155 | 255 | static struct i2c_driver lis3lv02d_i2c_driver = { |
156 | 256 | .driver = { |
157 | 257 | .name = DRV_NAME, |
158 | 258 | .owner = THIS_MODULE, |
259 | + .pm = &lis3_pm_ops, | |
159 | 260 | }, |
160 | - .suspend = lis3lv02d_i2c_suspend, | |
161 | - .shutdown = lis3lv02d_i2c_shutdown, | |
162 | - .resume = lis3lv02d_i2c_resume, | |
163 | 261 | .probe = lis3lv02d_i2c_probe, |
164 | 262 | .remove = __devexit_p(lis3lv02d_i2c_remove), |
165 | 263 | .id_table = lis3lv02d_id, |
drivers/hwmon/lis3lv02d_spi.c
... | ... | @@ -50,11 +50,12 @@ |
50 | 50 | if (ret < 0) |
51 | 51 | return ret; |
52 | 52 | |
53 | - reg |= CTRL1_PD0; | |
53 | + reg |= CTRL1_PD0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen; | |
54 | 54 | return lis3->write(lis3, CTRL_REG1, reg); |
55 | 55 | } |
56 | 56 | |
57 | -static struct axis_conversion lis3lv02d_axis_normal = { 1, 2, 3 }; | |
57 | +static union axis_conversion lis3lv02d_axis_normal = | |
58 | + { .as_array = { 1, 2, 3 } }; | |
58 | 59 | |
59 | 60 | static int __devinit lis302dl_spi_probe(struct spi_device *spi) |
60 | 61 | { |
drivers/hwmon/ltc4261.c
1 | +/* | |
2 | + * Driver for Linear Technology LTC4261 I2C Negative Voltage Hot Swap Controller | |
3 | + * | |
4 | + * Copyright (C) 2010 Ericsson AB. | |
5 | + * | |
6 | + * Derived from: | |
7 | + * | |
8 | + * Driver for Linear Technology LTC4245 I2C Multiple Supply Hot Swap Controller | |
9 | + * Copyright (C) 2008 Ira W. Snyder <iws@ovro.caltech.edu> | |
10 | + * | |
11 | + * Datasheet: http://cds.linear.com/docs/Datasheet/42612fb.pdf | |
12 | + * | |
13 | + * This program is free software; you can redistribute it and/or modify | |
14 | + * it under the terms of the GNU General Public License as published by | |
15 | + * the Free Software Foundation; either version 2 of the License, or | |
16 | + * (at your option) any later version. | |
17 | + * | |
18 | + * This program is distributed in the hope that it will be useful, | |
19 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 | + * GNU General Public License for more details. | |
22 | + * | |
23 | + * You should have received a copy of the GNU General Public License | |
24 | + * along with this program; if not, write to the Free Software | |
25 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
26 | + */ | |
27 | + | |
28 | +#include <linux/kernel.h> | |
29 | +#include <linux/module.h> | |
30 | +#include <linux/init.h> | |
31 | +#include <linux/err.h> | |
32 | +#include <linux/slab.h> | |
33 | +#include <linux/i2c.h> | |
34 | +#include <linux/hwmon.h> | |
35 | +#include <linux/hwmon-sysfs.h> | |
36 | + | |
37 | +/* chip registers */ | |
38 | +#define LTC4261_STATUS 0x00 /* readonly */ | |
39 | +#define LTC4261_FAULT 0x01 | |
40 | +#define LTC4261_ALERT 0x02 | |
41 | +#define LTC4261_CONTROL 0x03 | |
42 | +#define LTC4261_SENSE_H 0x04 | |
43 | +#define LTC4261_SENSE_L 0x05 | |
44 | +#define LTC4261_ADIN2_H 0x06 | |
45 | +#define LTC4261_ADIN2_L 0x07 | |
46 | +#define LTC4261_ADIN_H 0x08 | |
47 | +#define LTC4261_ADIN_L 0x09 | |
48 | + | |
49 | +/* | |
50 | + * Fault register bits | |
51 | + */ | |
52 | +#define FAULT_OV (1<<0) | |
53 | +#define FAULT_UV (1<<1) | |
54 | +#define FAULT_OC (1<<2) | |
55 | + | |
56 | +struct ltc4261_data { | |
57 | + struct device *hwmon_dev; | |
58 | + | |
59 | + struct mutex update_lock; | |
60 | + bool valid; | |
61 | + unsigned long last_updated; /* in jiffies */ | |
62 | + | |
63 | + /* Registers */ | |
64 | + u8 regs[10]; | |
65 | +}; | |
66 | + | |
67 | +static struct ltc4261_data *ltc4261_update_device(struct device *dev) | |
68 | +{ | |
69 | + struct i2c_client *client = to_i2c_client(dev); | |
70 | + struct ltc4261_data *data = i2c_get_clientdata(client); | |
71 | + struct ltc4261_data *ret = data; | |
72 | + | |
73 | + mutex_lock(&data->update_lock); | |
74 | + | |
75 | + if (time_after(jiffies, data->last_updated + HZ / 4) || !data->valid) { | |
76 | + int i; | |
77 | + | |
78 | + /* Read registers -- 0x00 to 0x09 */ | |
79 | + for (i = 0; i < ARRAY_SIZE(data->regs); i++) { | |
80 | + int val; | |
81 | + | |
82 | + val = i2c_smbus_read_byte_data(client, i); | |
83 | + if (unlikely(val < 0)) { | |
84 | + dev_dbg(dev, | |
85 | + "Failed to read ADC value: error %d", | |
86 | + val); | |
87 | + ret = ERR_PTR(val); | |
88 | + goto abort; | |
89 | + } | |
90 | + data->regs[i] = val; | |
91 | + } | |
92 | + data->last_updated = jiffies; | |
93 | + data->valid = 1; | |
94 | + } | |
95 | +abort: | |
96 | + mutex_unlock(&data->update_lock); | |
97 | + return ret; | |
98 | +} | |
99 | + | |
100 | +/* Return the voltage from the given register in mV or mA */ | |
101 | +static int ltc4261_get_value(struct ltc4261_data *data, u8 reg) | |
102 | +{ | |
103 | + u32 val; | |
104 | + | |
105 | + val = (data->regs[reg] << 2) + (data->regs[reg + 1] >> 6); | |
106 | + | |
107 | + switch (reg) { | |
108 | + case LTC4261_ADIN_H: | |
109 | + case LTC4261_ADIN2_H: | |
110 | + /* 2.5mV resolution. Convert to mV. */ | |
111 | + val = val * 25 / 10; | |
112 | + break; | |
113 | + case LTC4261_SENSE_H: | |
114 | + /* | |
115 | + * 62.5uV resolution. Convert to current as measured with | |
116 | + * an 1 mOhm sense resistor, in mA. If a different sense | |
117 | + * resistor is installed, calculate the actual current by | |
118 | + * dividing the reported current by the sense resistor value | |
119 | + * in mOhm. | |
120 | + */ | |
121 | + val = val * 625 / 10; | |
122 | + break; | |
123 | + default: | |
124 | + /* If we get here, the developer messed up */ | |
125 | + WARN_ON_ONCE(1); | |
126 | + val = 0; | |
127 | + break; | |
128 | + } | |
129 | + | |
130 | + return val; | |
131 | +} | |
132 | + | |
133 | +static ssize_t ltc4261_show_value(struct device *dev, | |
134 | + struct device_attribute *da, char *buf) | |
135 | +{ | |
136 | + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); | |
137 | + struct ltc4261_data *data = ltc4261_update_device(dev); | |
138 | + int value; | |
139 | + | |
140 | + if (IS_ERR(data)) | |
141 | + return PTR_ERR(data); | |
142 | + | |
143 | + value = ltc4261_get_value(data, attr->index); | |
144 | + return snprintf(buf, PAGE_SIZE, "%d\n", value); | |
145 | +} | |
146 | + | |
147 | +static ssize_t ltc4261_show_bool(struct device *dev, | |
148 | + struct device_attribute *da, char *buf) | |
149 | +{ | |
150 | + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); | |
151 | + struct i2c_client *client = to_i2c_client(dev); | |
152 | + struct ltc4261_data *data = ltc4261_update_device(dev); | |
153 | + u8 fault; | |
154 | + | |
155 | + if (IS_ERR(data)) | |
156 | + return PTR_ERR(data); | |
157 | + | |
158 | + fault = data->regs[LTC4261_FAULT] & attr->index; | |
159 | + if (fault) /* Clear reported faults in chip register */ | |
160 | + i2c_smbus_write_byte_data(client, LTC4261_FAULT, ~fault); | |
161 | + | |
162 | + return snprintf(buf, PAGE_SIZE, "%d\n", fault ? 1 : 0); | |
163 | +} | |
164 | + | |
165 | +/* | |
166 | + * These macros are used below in constructing device attribute objects | |
167 | + * for use with sysfs_create_group() to make a sysfs device file | |
168 | + * for each register. | |
169 | + */ | |
170 | + | |
171 | +#define LTC4261_VALUE(name, ltc4261_cmd_idx) \ | |
172 | + static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ | |
173 | + ltc4261_show_value, NULL, ltc4261_cmd_idx) | |
174 | + | |
175 | +#define LTC4261_BOOL(name, mask) \ | |
176 | + static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ | |
177 | + ltc4261_show_bool, NULL, (mask)) | |
178 | + | |
179 | +/* | |
180 | + * Input voltages. | |
181 | + */ | |
182 | +LTC4261_VALUE(in1_input, LTC4261_ADIN_H); | |
183 | +LTC4261_VALUE(in2_input, LTC4261_ADIN2_H); | |
184 | + | |
185 | +/* | |
186 | + * Voltage alarms. The chip has only one set of voltage alarm status bits, | |
187 | + * triggered by input voltage alarms. In many designs, those alarms are | |
188 | + * associated with the ADIN2 sensor, due to the proximity of the ADIN2 pin | |
189 | + * to the OV pin. ADIN2 is, however, not available on all chip variants. | |
190 | + * To ensure that the alarm condition is reported to the user, report it | |
191 | + * with both voltage sensors. | |
192 | + */ | |
193 | +LTC4261_BOOL(in1_min_alarm, FAULT_UV); | |
194 | +LTC4261_BOOL(in1_max_alarm, FAULT_OV); | |
195 | +LTC4261_BOOL(in2_min_alarm, FAULT_UV); | |
196 | +LTC4261_BOOL(in2_max_alarm, FAULT_OV); | |
197 | + | |
198 | +/* Currents (via sense resistor) */ | |
199 | +LTC4261_VALUE(curr1_input, LTC4261_SENSE_H); | |
200 | + | |
201 | +/* Overcurrent alarm */ | |
202 | +LTC4261_BOOL(curr1_max_alarm, FAULT_OC); | |
203 | + | |
204 | +static struct attribute *ltc4261_attributes[] = { | |
205 | + &sensor_dev_attr_in1_input.dev_attr.attr, | |
206 | + &sensor_dev_attr_in1_min_alarm.dev_attr.attr, | |
207 | + &sensor_dev_attr_in1_max_alarm.dev_attr.attr, | |
208 | + &sensor_dev_attr_in2_input.dev_attr.attr, | |
209 | + &sensor_dev_attr_in2_min_alarm.dev_attr.attr, | |
210 | + &sensor_dev_attr_in2_max_alarm.dev_attr.attr, | |
211 | + | |
212 | + &sensor_dev_attr_curr1_input.dev_attr.attr, | |
213 | + &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, | |
214 | + | |
215 | + NULL, | |
216 | +}; | |
217 | + | |
218 | +static const struct attribute_group ltc4261_group = { | |
219 | + .attrs = ltc4261_attributes, | |
220 | +}; | |
221 | + | |
222 | +static int ltc4261_probe(struct i2c_client *client, | |
223 | + const struct i2c_device_id *id) | |
224 | +{ | |
225 | + struct i2c_adapter *adapter = client->adapter; | |
226 | + struct ltc4261_data *data; | |
227 | + int ret; | |
228 | + | |
229 | + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) | |
230 | + return -ENODEV; | |
231 | + | |
232 | + if (i2c_smbus_read_byte_data(client, LTC4261_STATUS) < 0) { | |
233 | + dev_err(&client->dev, "Failed to read register %d:%02x:%02x\n", | |
234 | + adapter->id, client->addr, LTC4261_STATUS); | |
235 | + return -ENODEV; | |
236 | + } | |
237 | + | |
238 | + data = kzalloc(sizeof(*data), GFP_KERNEL); | |
239 | + if (!data) { | |
240 | + ret = -ENOMEM; | |
241 | + goto out_kzalloc; | |
242 | + } | |
243 | + | |
244 | + i2c_set_clientdata(client, data); | |
245 | + mutex_init(&data->update_lock); | |
246 | + | |
247 | + /* Clear faults */ | |
248 | + i2c_smbus_write_byte_data(client, LTC4261_FAULT, 0x00); | |
249 | + | |
250 | + /* Register sysfs hooks */ | |
251 | + ret = sysfs_create_group(&client->dev.kobj, <c4261_group); | |
252 | + if (ret) | |
253 | + goto out_sysfs_create_group; | |
254 | + | |
255 | + data->hwmon_dev = hwmon_device_register(&client->dev); | |
256 | + if (IS_ERR(data->hwmon_dev)) { | |
257 | + ret = PTR_ERR(data->hwmon_dev); | |
258 | + goto out_hwmon_device_register; | |
259 | + } | |
260 | + | |
261 | + return 0; | |
262 | + | |
263 | +out_hwmon_device_register: | |
264 | + sysfs_remove_group(&client->dev.kobj, <c4261_group); | |
265 | +out_sysfs_create_group: | |
266 | + kfree(data); | |
267 | +out_kzalloc: | |
268 | + return ret; | |
269 | +} | |
270 | + | |
271 | +static int ltc4261_remove(struct i2c_client *client) | |
272 | +{ | |
273 | + struct ltc4261_data *data = i2c_get_clientdata(client); | |
274 | + | |
275 | + hwmon_device_unregister(data->hwmon_dev); | |
276 | + sysfs_remove_group(&client->dev.kobj, <c4261_group); | |
277 | + | |
278 | + kfree(data); | |
279 | + | |
280 | + return 0; | |
281 | +} | |
282 | + | |
283 | +static const struct i2c_device_id ltc4261_id[] = { | |
284 | + {"ltc4261", 0}, | |
285 | + {} | |
286 | +}; | |
287 | + | |
288 | +MODULE_DEVICE_TABLE(i2c, ltc4261_id); | |
289 | + | |
290 | +/* This is the driver that will be inserted */ | |
291 | +static struct i2c_driver ltc4261_driver = { | |
292 | + .driver = { | |
293 | + .name = "ltc4261", | |
294 | + }, | |
295 | + .probe = ltc4261_probe, | |
296 | + .remove = ltc4261_remove, | |
297 | + .id_table = ltc4261_id, | |
298 | +}; | |
299 | + | |
300 | +static int __init ltc4261_init(void) | |
301 | +{ | |
302 | + return i2c_add_driver(<c4261_driver); | |
303 | +} | |
304 | + | |
305 | +static void __exit ltc4261_exit(void) | |
306 | +{ | |
307 | + i2c_del_driver(<c4261_driver); | |
308 | +} | |
309 | + | |
310 | +MODULE_AUTHOR("Guenter Roeck <guenter.roeck@ericsson.com>"); | |
311 | +MODULE_DESCRIPTION("LTC4261 driver"); | |
312 | +MODULE_LICENSE("GPL"); | |
313 | + | |
314 | +module_init(ltc4261_init); | |
315 | +module_exit(ltc4261_exit); |
drivers/hwmon/pkgtemp.c
... | ... | @@ -21,7 +21,6 @@ |
21 | 21 | */ |
22 | 22 | |
23 | 23 | #include <linux/module.h> |
24 | -#include <linux/delay.h> | |
25 | 24 | #include <linux/init.h> |
26 | 25 | #include <linux/slab.h> |
27 | 26 | #include <linux/jiffies.h> |
... | ... | @@ -35,6 +34,7 @@ |
35 | 34 | #include <linux/cpu.h> |
36 | 35 | #include <asm/msr.h> |
37 | 36 | #include <asm/processor.h> |
37 | +#include <asm/smp.h> | |
38 | 38 | |
39 | 39 | #define DRVNAME "pkgtemp" |
40 | 40 | |
... | ... | @@ -339,8 +339,7 @@ |
339 | 339 | return err; |
340 | 340 | } |
341 | 341 | |
342 | -#ifdef CONFIG_HOTPLUG_CPU | |
343 | -static void pkgtemp_device_remove(unsigned int cpu) | |
342 | +static void __cpuinit pkgtemp_device_remove(unsigned int cpu) | |
344 | 343 | { |
345 | 344 | struct pdev_entry *p; |
346 | 345 | unsigned int i; |
347 | 346 | |
... | ... | @@ -387,12 +386,10 @@ |
387 | 386 | static struct notifier_block pkgtemp_cpu_notifier __refdata = { |
388 | 387 | .notifier_call = pkgtemp_cpu_callback, |
389 | 388 | }; |
390 | -#endif /* !CONFIG_HOTPLUG_CPU */ | |
391 | 389 | |
392 | 390 | static int __init pkgtemp_init(void) |
393 | 391 | { |
394 | 392 | int i, err = -ENODEV; |
395 | - struct pdev_entry *p, *n; | |
396 | 393 | |
397 | 394 | /* quick check if we run Intel */ |
398 | 395 | if (cpu_data(0).x86_vendor != X86_VENDOR_INTEL) |
399 | 396 | |
400 | 397 | |
401 | 398 | |
402 | 399 | |
403 | 400 | |
... | ... | @@ -402,31 +399,23 @@ |
402 | 399 | if (err) |
403 | 400 | goto exit; |
404 | 401 | |
405 | - for_each_online_cpu(i) { | |
406 | - err = pkgtemp_device_add(i); | |
407 | - if (err) | |
408 | - goto exit_devices_unreg; | |
409 | - } | |
402 | + for_each_online_cpu(i) | |
403 | + pkgtemp_device_add(i); | |
404 | + | |
405 | +#ifndef CONFIG_HOTPLUG_CPU | |
410 | 406 | if (list_empty(&pdev_list)) { |
411 | 407 | err = -ENODEV; |
412 | 408 | goto exit_driver_unreg; |
413 | 409 | } |
410 | +#endif | |
414 | 411 | |
415 | -#ifdef CONFIG_HOTPLUG_CPU | |
416 | 412 | register_hotcpu_notifier(&pkgtemp_cpu_notifier); |
417 | -#endif | |
418 | 413 | return 0; |
419 | 414 | |
420 | -exit_devices_unreg: | |
421 | - mutex_lock(&pdev_list_mutex); | |
422 | - list_for_each_entry_safe(p, n, &pdev_list, list) { | |
423 | - platform_device_unregister(p->pdev); | |
424 | - list_del(&p->list); | |
425 | - kfree(p); | |
426 | - } | |
427 | - mutex_unlock(&pdev_list_mutex); | |
415 | +#ifndef CONFIG_HOTPLUG_CPU | |
428 | 416 | exit_driver_unreg: |
429 | 417 | platform_driver_unregister(&pkgtemp_driver); |
418 | +#endif | |
430 | 419 | exit: |
431 | 420 | return err; |
432 | 421 | } |
433 | 422 | |
... | ... | @@ -434,9 +423,8 @@ |
434 | 423 | static void __exit pkgtemp_exit(void) |
435 | 424 | { |
436 | 425 | struct pdev_entry *p, *n; |
437 | -#ifdef CONFIG_HOTPLUG_CPU | |
426 | + | |
438 | 427 | unregister_hotcpu_notifier(&pkgtemp_cpu_notifier); |
439 | -#endif | |
440 | 428 | mutex_lock(&pdev_list_mutex); |
441 | 429 | list_for_each_entry_safe(p, n, &pdev_list, list) { |
442 | 430 | platform_device_unregister(p->pdev); |
drivers/hwmon/via-cputemp.c
... | ... | @@ -22,10 +22,8 @@ |
22 | 22 | */ |
23 | 23 | |
24 | 24 | #include <linux/module.h> |
25 | -#include <linux/delay.h> | |
26 | 25 | #include <linux/init.h> |
27 | 26 | #include <linux/slab.h> |
28 | -#include <linux/jiffies.h> | |
29 | 27 | #include <linux/hwmon.h> |
30 | 28 | #include <linux/sysfs.h> |
31 | 29 | #include <linux/hwmon-sysfs.h> |
... | ... | @@ -237,8 +235,7 @@ |
237 | 235 | return err; |
238 | 236 | } |
239 | 237 | |
240 | -#ifdef CONFIG_HOTPLUG_CPU | |
241 | -static void via_cputemp_device_remove(unsigned int cpu) | |
238 | +static void __cpuinit via_cputemp_device_remove(unsigned int cpu) | |
242 | 239 | { |
243 | 240 | struct pdev_entry *p, *n; |
244 | 241 | mutex_lock(&pdev_list_mutex); |
... | ... | @@ -272,7 +269,6 @@ |
272 | 269 | static struct notifier_block via_cputemp_cpu_notifier __refdata = { |
273 | 270 | .notifier_call = via_cputemp_cpu_callback, |
274 | 271 | }; |
275 | -#endif /* !CONFIG_HOTPLUG_CPU */ | |
276 | 272 | |
277 | 273 | static int __init via_cputemp_init(void) |
278 | 274 | { |
279 | 275 | |
... | ... | @@ -313,9 +309,7 @@ |
313 | 309 | goto exit_driver_unreg; |
314 | 310 | } |
315 | 311 | |
316 | -#ifdef CONFIG_HOTPLUG_CPU | |
317 | 312 | register_hotcpu_notifier(&via_cputemp_cpu_notifier); |
318 | -#endif | |
319 | 313 | return 0; |
320 | 314 | |
321 | 315 | exit_devices_unreg: |
322 | 316 | |
... | ... | @@ -335,9 +329,8 @@ |
335 | 329 | static void __exit via_cputemp_exit(void) |
336 | 330 | { |
337 | 331 | struct pdev_entry *p, *n; |
338 | -#ifdef CONFIG_HOTPLUG_CPU | |
332 | + | |
339 | 333 | unregister_hotcpu_notifier(&via_cputemp_cpu_notifier); |
340 | -#endif | |
341 | 334 | mutex_lock(&pdev_list_mutex); |
342 | 335 | list_for_each_entry_safe(p, n, &pdev_list, list) { |
343 | 336 | platform_device_unregister(p->pdev); |
include/linux/gpio-fan.h
1 | +/* | |
2 | + * include/linux/gpio-fan.h | |
3 | + * | |
4 | + * Platform data structure for GPIO fan driver | |
5 | + * | |
6 | + * This file is licensed under the terms of the GNU General Public | |
7 | + * License version 2. This program is licensed "as is" without any | |
8 | + * warranty of any kind, whether express or implied. | |
9 | + */ | |
10 | + | |
11 | +#ifndef __LINUX_GPIO_FAN_H | |
12 | +#define __LINUX_GPIO_FAN_H | |
13 | + | |
14 | +struct gpio_fan_alarm { | |
15 | + unsigned gpio; | |
16 | + unsigned active_low; | |
17 | +}; | |
18 | + | |
19 | +struct gpio_fan_speed { | |
20 | + int rpm; | |
21 | + int ctrl_val; | |
22 | +}; | |
23 | + | |
24 | +struct gpio_fan_platform_data { | |
25 | + int num_ctrl; | |
26 | + unsigned *ctrl; /* fan control GPIOs. */ | |
27 | + struct gpio_fan_alarm *alarm; /* fan alarm GPIO. */ | |
28 | + /* | |
29 | + * Speed conversion array: rpm from/to GPIO bit field. | |
30 | + * This array _must_ be sorted in ascending rpm order. | |
31 | + */ | |
32 | + int num_speed; | |
33 | + struct gpio_fan_speed *speed; | |
34 | +}; | |
35 | + | |
36 | +#endif /* __LINUX_GPIO_FAN_H */ |
include/linux/lis3lv02d.h
1 | 1 | #ifndef __LIS3LV02D_H_ |
2 | 2 | #define __LIS3LV02D_H_ |
3 | 3 | |
4 | +/** | |
5 | + * struct lis3lv02d_platform_data - lis3 chip family platform data | |
6 | + * @click_flags: Click detection unit configuration | |
7 | + * @click_thresh_x: Click detection unit x axis threshold | |
8 | + * @click_thresh_y: Click detection unit y axis threshold | |
9 | + * @click_thresh_z: Click detection unit z axis threshold | |
10 | + * @click_time_limit: Click detection unit time parameter | |
11 | + * @click_latency: Click detection unit latency parameter | |
12 | + * @click_window: Click detection unit window parameter | |
13 | + * @irq_cfg: On chip irq source and type configuration (click / | |
14 | + * data available / wake up, open drain, polarity) | |
15 | + * @irq_flags1: Additional irq triggering flags for irq channel 0 | |
16 | + * @irq_flags2: Additional irq triggering flags for irq channel 1 | |
17 | + * @duration1: Wake up unit 1 duration parameter | |
18 | + * @duration2: Wake up unit 2 duration parameter | |
19 | + * @wakeup_flags: Wake up unit 1 flags | |
20 | + * @wakeup_thresh: Wake up unit 1 threshold value | |
21 | + * @wakeup_flags2: Wake up unit 2 flags | |
22 | + * @wakeup_thresh2: Wake up unit 2 threshold value | |
23 | + * @hipass_ctrl: High pass filter control (enable / disable, cut off | |
24 | + * frequency) | |
25 | + * @axis_x: Sensor orientation remapping for x-axis | |
26 | + * @axis_y: Sensor orientation remapping for y-axis | |
27 | + * @axis_z: Sensor orientation remapping for z-axis | |
28 | + * @driver_features: Enable bits for different features. Disabled by default | |
29 | + * @default_rate: Default sampling rate. 0 means reset default | |
30 | + * @setup_resources: Interrupt line setup call back function | |
31 | + * @release_resources: Interrupt line release call back function | |
32 | + * @st_min_limits[3]: Selftest acceptance minimum values | |
33 | + * @st_max_limits[3]: Selftest acceptance maximum values | |
34 | + * @irq2: Irq line 2 number | |
35 | + * | |
36 | + * Platform data is used to setup the sensor chip. Meaning of the different | |
37 | + * chip features can be found from the data sheet. It is publicly available | |
38 | + * at www.st.com web pages. Currently the platform data is used | |
39 | + * only for the 8 bit device. The 8 bit device has two wake up / free fall | |
40 | + * detection units and click detection unit. There are plenty of ways to | |
41 | + * configure the chip which makes is quite hard to explain deeper meaning of | |
42 | + * the fields here. Behaviour of the detection blocks varies heavily depending | |
43 | + * on the configuration. For example, interrupt detection block can use high | |
44 | + * pass filtered data which makes it react to the changes in the acceleration. | |
45 | + * Irq_flags can be used to enable interrupt detection on the both edges. | |
46 | + * With proper chip configuration this produces interrupt when some trigger | |
47 | + * starts and when it goes away. | |
48 | + */ | |
49 | + | |
4 | 50 | struct lis3lv02d_platform_data { |
5 | 51 | /* please note: the 'click' feature is only supported for |
6 | 52 | * LIS[32]02DL variants of the chip and will be ignored for |
... | ... | @@ -36,7 +82,10 @@ |
36 | 82 | #define LIS3_IRQ_OPEN_DRAIN (1 << 6) |
37 | 83 | #define LIS3_IRQ_ACTIVE_LOW (1 << 7) |
38 | 84 | unsigned char irq_cfg; |
39 | - | |
85 | + unsigned char irq_flags1; /* Additional irq edge / level flags */ | |
86 | + unsigned char irq_flags2; /* Additional irq edge / level flags */ | |
87 | + unsigned char duration1; | |
88 | + unsigned char duration2; | |
40 | 89 | #define LIS3_WAKEUP_X_LO (1 << 0) |
41 | 90 | #define LIS3_WAKEUP_X_HI (1 << 1) |
42 | 91 | #define LIS3_WAKEUP_Y_LO (1 << 2) |
... | ... | @@ -64,6 +113,10 @@ |
64 | 113 | s8 axis_x; |
65 | 114 | s8 axis_y; |
66 | 115 | s8 axis_z; |
116 | +#define LIS3_USE_REGULATOR_CTRL 0x01 | |
117 | +#define LIS3_USE_BLOCK_READ 0x02 | |
118 | + u16 driver_features; | |
119 | + int default_rate; | |
67 | 120 | int (*setup_resources)(void); |
68 | 121 | int (*release_resources)(void); |
69 | 122 | /* Limits for selftest are specified in chip data sheet */ |