diff options
-rw-r--r-- | drivers/hwmon/lis3lv02d.c | 52 | ||||
-rw-r--r-- | drivers/hwmon/lis3lv02d.h | 13 | ||||
-rw-r--r-- | drivers/hwmon/lis3lv02d_i2c.c | 55 | ||||
-rw-r--r-- | include/linux/lis3lv02d.h | 2 |
4 files changed, 121 insertions, 1 deletions
diff --git a/drivers/hwmon/lis3lv02d.c b/drivers/hwmon/lis3lv02d.c index 383a84938a98..159e402ddec3 100644 --- a/drivers/hwmon/lis3lv02d.c +++ b/drivers/hwmon/lis3lv02d.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <linux/delay.h> | 31 | #include <linux/delay.h> |
32 | #include <linux/wait.h> | 32 | #include <linux/wait.h> |
33 | #include <linux/poll.h> | 33 | #include <linux/poll.h> |
34 | #include <linux/slab.h> | ||
34 | #include <linux/freezer.h> | 35 | #include <linux/freezer.h> |
35 | #include <linux/uaccess.h> | 36 | #include <linux/uaccess.h> |
36 | #include <linux/miscdevice.h> | 37 | #include <linux/miscdevice.h> |
@@ -257,10 +258,46 @@ fail: | |||
257 | return ret; | 258 | return ret; |
258 | } | 259 | } |
259 | 260 | ||
261 | /* | ||
262 | * Order of registers in the list affects to order of the restore process. | ||
263 | * Perhaps it is a good idea to set interrupt enable register as a last one | ||
264 | * after all other configurations | ||
265 | */ | ||
266 | static u8 lis3_wai8_regs[] = { FF_WU_CFG_1, FF_WU_THS_1, FF_WU_DURATION_1, | ||
267 | FF_WU_CFG_2, FF_WU_THS_2, FF_WU_DURATION_2, | ||
268 | CLICK_CFG, CLICK_SRC, CLICK_THSY_X, CLICK_THSZ, | ||
269 | CLICK_TIMELIMIT, CLICK_LATENCY, CLICK_WINDOW, | ||
270 | CTRL_REG1, CTRL_REG2, CTRL_REG3}; | ||
271 | |||
272 | static u8 lis3_wai12_regs[] = {FF_WU_CFG, FF_WU_THS_L, FF_WU_THS_H, | ||
273 | FF_WU_DURATION, DD_CFG, DD_THSI_L, DD_THSI_H, | ||
274 | DD_THSE_L, DD_THSE_H, | ||
275 | CTRL_REG1, CTRL_REG3, CTRL_REG2}; | ||
276 | |||
277 | static inline void lis3_context_save(struct lis3lv02d *lis3) | ||
278 | { | ||
279 | int i; | ||
280 | for (i = 0; i < lis3->regs_size; i++) | ||
281 | lis3->read(lis3, lis3->regs[i], &lis3->reg_cache[i]); | ||
282 | lis3->regs_stored = true; | ||
283 | } | ||
284 | |||
285 | static inline void lis3_context_restore(struct lis3lv02d *lis3) | ||
286 | { | ||
287 | int i; | ||
288 | if (lis3->regs_stored) | ||
289 | for (i = 0; i < lis3->regs_size; i++) | ||
290 | lis3->write(lis3, lis3->regs[i], lis3->reg_cache[i]); | ||
291 | } | ||
292 | |||
260 | void lis3lv02d_poweroff(struct lis3lv02d *lis3) | 293 | void lis3lv02d_poweroff(struct lis3lv02d *lis3) |
261 | { | 294 | { |
295 | if (lis3->reg_ctrl) | ||
296 | lis3_context_save(lis3); | ||
262 | /* disable X,Y,Z axis and power down */ | 297 | /* disable X,Y,Z axis and power down */ |
263 | lis3->write(lis3, CTRL_REG1, 0x00); | 298 | lis3->write(lis3, CTRL_REG1, 0x00); |
299 | if (lis3->reg_ctrl) | ||
300 | lis3->reg_ctrl(lis3, LIS3_REG_OFF); | ||
264 | } | 301 | } |
265 | EXPORT_SYMBOL_GPL(lis3lv02d_poweroff); | 302 | EXPORT_SYMBOL_GPL(lis3lv02d_poweroff); |
266 | 303 | ||
@@ -283,6 +320,8 @@ void lis3lv02d_poweron(struct lis3lv02d *lis3) | |||
283 | reg |= CTRL2_BDU; | 320 | reg |= CTRL2_BDU; |
284 | lis3->write(lis3, CTRL_REG2, reg); | 321 | lis3->write(lis3, CTRL_REG2, reg); |
285 | } | 322 | } |
323 | if (lis3->reg_ctrl) | ||
324 | lis3_context_restore(lis3); | ||
286 | } | 325 | } |
287 | EXPORT_SYMBOL_GPL(lis3lv02d_poweron); | 326 | EXPORT_SYMBOL_GPL(lis3lv02d_poweron); |
288 | 327 | ||
@@ -674,6 +713,7 @@ int lis3lv02d_remove_fs(struct lis3lv02d *lis3) | |||
674 | pm_runtime_disable(lis3->pm_dev); | 713 | pm_runtime_disable(lis3->pm_dev); |
675 | pm_runtime_set_suspended(lis3->pm_dev); | 714 | pm_runtime_set_suspended(lis3->pm_dev); |
676 | } | 715 | } |
716 | kfree(lis3->reg_cache); | ||
677 | return 0; | 717 | return 0; |
678 | } | 718 | } |
679 | EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs); | 719 | EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs); |
@@ -753,6 +793,8 @@ int lis3lv02d_init_device(struct lis3lv02d *dev) | |||
753 | dev->odrs = lis3_12_rates; | 793 | dev->odrs = lis3_12_rates; |
754 | dev->odr_mask = CTRL1_DF0 | CTRL1_DF1; | 794 | dev->odr_mask = CTRL1_DF0 | CTRL1_DF1; |
755 | dev->scale = LIS3_SENSITIVITY_12B; | 795 | dev->scale = LIS3_SENSITIVITY_12B; |
796 | dev->regs = lis3_wai12_regs; | ||
797 | dev->regs_size = ARRAY_SIZE(lis3_wai12_regs); | ||
756 | break; | 798 | break; |
757 | case WAI_8B: | 799 | case WAI_8B: |
758 | printk(KERN_INFO DRIVER_NAME ": 8 bits sensor found\n"); | 800 | printk(KERN_INFO DRIVER_NAME ": 8 bits sensor found\n"); |
@@ -762,6 +804,8 @@ int lis3lv02d_init_device(struct lis3lv02d *dev) | |||
762 | dev->odrs = lis3_8_rates; | 804 | dev->odrs = lis3_8_rates; |
763 | dev->odr_mask = CTRL1_DR; | 805 | dev->odr_mask = CTRL1_DR; |
764 | dev->scale = LIS3_SENSITIVITY_8B; | 806 | dev->scale = LIS3_SENSITIVITY_8B; |
807 | dev->regs = lis3_wai8_regs; | ||
808 | dev->regs_size = ARRAY_SIZE(lis3_wai8_regs); | ||
765 | break; | 809 | break; |
766 | case WAI_3DC: | 810 | case WAI_3DC: |
767 | printk(KERN_INFO DRIVER_NAME ": 8 bits 3DC sensor found\n"); | 811 | printk(KERN_INFO DRIVER_NAME ": 8 bits 3DC sensor found\n"); |
@@ -778,6 +822,14 @@ int lis3lv02d_init_device(struct lis3lv02d *dev) | |||
778 | return -EINVAL; | 822 | return -EINVAL; |
779 | } | 823 | } |
780 | 824 | ||
825 | dev->reg_cache = kzalloc(max(sizeof(lis3_wai8_regs), | ||
826 | sizeof(lis3_wai12_regs)), GFP_KERNEL); | ||
827 | |||
828 | if (dev->reg_cache == NULL) { | ||
829 | printk(KERN_ERR DRIVER_NAME "out of memory\n"); | ||
830 | return -ENOMEM; | ||
831 | } | ||
832 | |||
781 | mutex_init(&dev->mutex); | 833 | mutex_init(&dev->mutex); |
782 | 834 | ||
783 | lis3lv02d_add_fs(dev); | 835 | lis3lv02d_add_fs(dev); |
diff --git a/drivers/hwmon/lis3lv02d.h b/drivers/hwmon/lis3lv02d.h index 633cf1debc52..c5c063de2f48 100644 --- a/drivers/hwmon/lis3lv02d.h +++ b/drivers/hwmon/lis3lv02d.h | |||
@@ -20,6 +20,7 @@ | |||
20 | */ | 20 | */ |
21 | #include <linux/platform_device.h> | 21 | #include <linux/platform_device.h> |
22 | #include <linux/input-polldev.h> | 22 | #include <linux/input-polldev.h> |
23 | #include <linux/regulator/consumer.h> | ||
23 | 24 | ||
24 | /* | 25 | /* |
25 | * This driver tries to support the "digital" accelerometer chips from | 26 | * This driver tries to support the "digital" accelerometer chips from |
@@ -223,11 +224,17 @@ enum lis3lv02d_click_src_8b { | |||
223 | CLICK_IA = 0x40, | 224 | CLICK_IA = 0x40, |
224 | }; | 225 | }; |
225 | 226 | ||
227 | enum lis3lv02d_reg_state { | ||
228 | LIS3_REG_OFF = 0x00, | ||
229 | LIS3_REG_ON = 0x01, | ||
230 | }; | ||
231 | |||
226 | union axis_conversion { | 232 | union axis_conversion { |
227 | struct { | 233 | struct { |
228 | int x, y, z; | 234 | int x, y, z; |
229 | }; | 235 | }; |
230 | int as_array[3]; | 236 | int as_array[3]; |
237 | |||
231 | }; | 238 | }; |
232 | 239 | ||
233 | struct lis3lv02d { | 240 | struct lis3lv02d { |
@@ -236,8 +243,13 @@ struct lis3lv02d { | |||
236 | int (*init) (struct lis3lv02d *lis3); | 243 | int (*init) (struct lis3lv02d *lis3); |
237 | int (*write) (struct lis3lv02d *lis3, int reg, u8 val); | 244 | int (*write) (struct lis3lv02d *lis3, int reg, u8 val); |
238 | int (*read) (struct lis3lv02d *lis3, int reg, u8 *ret); | 245 | int (*read) (struct lis3lv02d *lis3, int reg, u8 *ret); |
246 | int (*reg_ctrl) (struct lis3lv02d *lis3, bool state); | ||
239 | 247 | ||
240 | int *odrs; /* Supported output data rates */ | 248 | int *odrs; /* Supported output data rates */ |
249 | u8 *regs; /* Regs to store / restore */ | ||
250 | int regs_size; | ||
251 | u8 *reg_cache; | ||
252 | bool regs_stored; | ||
241 | u8 odr_mask; /* ODR bit mask */ | 253 | u8 odr_mask; /* ODR bit mask */ |
242 | u8 whoami; /* indicates measurement precision */ | 254 | u8 whoami; /* indicates measurement precision */ |
243 | s16 (*read_data) (struct lis3lv02d *lis3, int reg); | 255 | s16 (*read_data) (struct lis3lv02d *lis3, int reg); |
@@ -250,6 +262,7 @@ struct lis3lv02d { | |||
250 | 262 | ||
251 | struct input_polled_dev *idev; /* input device */ | 263 | struct input_polled_dev *idev; /* input device */ |
252 | struct platform_device *pdev; /* platform device */ | 264 | struct platform_device *pdev; /* platform device */ |
265 | struct regulator_bulk_data regulators[2]; | ||
253 | atomic_t count; /* interrupt count after last read */ | 266 | atomic_t count; /* interrupt count after last read */ |
254 | union axis_conversion ac; /* hw -> logical axis */ | 267 | union axis_conversion ac; /* hw -> logical axis */ |
255 | int mapped_btns[3]; | 268 | int mapped_btns[3]; |
diff --git a/drivers/hwmon/lis3lv02d_i2c.c b/drivers/hwmon/lis3lv02d_i2c.c index d52095603e03..ec35c87819c0 100644 --- a/drivers/hwmon/lis3lv02d_i2c.c +++ b/drivers/hwmon/lis3lv02d_i2c.c | |||
@@ -30,10 +30,29 @@ | |||
30 | #include <linux/err.h> | 30 | #include <linux/err.h> |
31 | #include <linux/i2c.h> | 31 | #include <linux/i2c.h> |
32 | #include <linux/pm_runtime.h> | 32 | #include <linux/pm_runtime.h> |
33 | #include <linux/delay.h> | ||
33 | #include "lis3lv02d.h" | 34 | #include "lis3lv02d.h" |
34 | 35 | ||
35 | #define DRV_NAME "lis3lv02d_i2c" | 36 | #define DRV_NAME "lis3lv02d_i2c" |
36 | 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 | |||
37 | static inline s32 lis3_i2c_write(struct lis3lv02d *lis3, int reg, u8 value) | 56 | static inline s32 lis3_i2c_write(struct lis3lv02d *lis3, int reg, u8 value) |
38 | { | 57 | { |
39 | struct i2c_client *c = lis3->bus_priv; | 58 | struct i2c_client *c = lis3->bus_priv; |
@@ -52,6 +71,13 @@ static int lis3_i2c_init(struct lis3lv02d *lis3) | |||
52 | u8 reg; | 71 | u8 reg; |
53 | int ret; | 72 | int ret; |
54 | 73 | ||
74 | if (lis3->reg_ctrl) | ||
75 | lis3_reg_ctrl(lis3, LIS3_REG_ON); | ||
76 | |||
77 | lis3->read(lis3, WHO_AM_I, ®); | ||
78 | if (reg != lis3->whoami) | ||
79 | printk(KERN_ERR "lis3: power on failure\n"); | ||
80 | |||
55 | /* power up the device */ | 81 | /* power up the device */ |
56 | ret = lis3->read(lis3, CTRL_REG1, ®); | 82 | ret = lis3->read(lis3, CTRL_REG1, ®); |
57 | if (ret < 0) | 83 | if (ret < 0) |
@@ -72,6 +98,10 @@ static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client, | |||
72 | struct lis3lv02d_platform_data *pdata = client->dev.platform_data; | 98 | struct lis3lv02d_platform_data *pdata = client->dev.platform_data; |
73 | 99 | ||
74 | if (pdata) { | 100 | if (pdata) { |
101 | /* Regulator control is optional */ | ||
102 | if (pdata->driver_features & LIS3_USE_REGULATOR_CTRL) | ||
103 | lis3_dev.reg_ctrl = lis3_reg_ctrl; | ||
104 | |||
75 | if (pdata->axis_x) | 105 | if (pdata->axis_x) |
76 | lis3lv02d_axis_map.x = pdata->axis_x; | 106 | lis3lv02d_axis_map.x = pdata->axis_x; |
77 | 107 | ||
@@ -88,6 +118,16 @@ static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client, | |||
88 | goto fail; | 118 | goto fail; |
89 | } | 119 | } |
90 | 120 | ||
121 | if (lis3_dev.reg_ctrl) { | ||
122 | lis3_dev.regulators[0].supply = reg_vdd; | ||
123 | lis3_dev.regulators[1].supply = reg_vdd_io; | ||
124 | ret = regulator_bulk_get(&client->dev, | ||
125 | ARRAY_SIZE(lis3_dev.regulators), | ||
126 | lis3_dev.regulators); | ||
127 | if (ret < 0) | ||
128 | goto fail; | ||
129 | } | ||
130 | |||
91 | lis3_dev.pdata = pdata; | 131 | lis3_dev.pdata = pdata; |
92 | lis3_dev.bus_priv = client; | 132 | lis3_dev.bus_priv = client; |
93 | lis3_dev.init = lis3_i2c_init; | 133 | lis3_dev.init = lis3_i2c_init; |
@@ -98,21 +138,34 @@ static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client, | |||
98 | lis3_dev.pm_dev = &client->dev; | 138 | lis3_dev.pm_dev = &client->dev; |
99 | 139 | ||
100 | i2c_set_clientdata(client, &lis3_dev); | 140 | i2c_set_clientdata(client, &lis3_dev); |
141 | |||
142 | /* Provide power over the init call */ | ||
143 | if (lis3_dev.reg_ctrl) | ||
144 | lis3_reg_ctrl(&lis3_dev, LIS3_REG_ON); | ||
145 | |||
101 | ret = lis3lv02d_init_device(&lis3_dev); | 146 | ret = lis3lv02d_init_device(&lis3_dev); |
147 | |||
148 | if (lis3_dev.reg_ctrl) | ||
149 | lis3_reg_ctrl(&lis3_dev, LIS3_REG_OFF); | ||
102 | fail: | 150 | fail: |
103 | return ret; | 151 | return ret; |
104 | } | 152 | } |
105 | 153 | ||
106 | static int __devexit lis3lv02d_i2c_remove(struct i2c_client *client) | 154 | static int __devexit lis3lv02d_i2c_remove(struct i2c_client *client) |
107 | { | 155 | { |
156 | struct lis3lv02d *lis3 = i2c_get_clientdata(client); | ||
108 | struct lis3lv02d_platform_data *pdata = client->dev.platform_data; | 157 | struct lis3lv02d_platform_data *pdata = client->dev.platform_data; |
109 | 158 | ||
110 | if (pdata && pdata->release_resources) | 159 | if (pdata && pdata->release_resources) |
111 | pdata->release_resources(); | 160 | pdata->release_resources(); |
112 | 161 | ||
113 | lis3lv02d_joystick_disable(); | 162 | lis3lv02d_joystick_disable(); |
163 | lis3lv02d_remove_fs(&lis3_dev); | ||
114 | 164 | ||
115 | return lis3lv02d_remove_fs(&lis3_dev); | 165 | if (lis3_dev.reg_ctrl) |
166 | regulator_bulk_free(ARRAY_SIZE(lis3->regulators), | ||
167 | lis3_dev.regulators); | ||
168 | return 0; | ||
116 | } | 169 | } |
117 | 170 | ||
118 | #ifdef CONFIG_PM | 171 | #ifdef CONFIG_PM |
diff --git a/include/linux/lis3lv02d.h b/include/linux/lis3lv02d.h index 0e8a346424bb..c4a4a52c1de7 100644 --- a/include/linux/lis3lv02d.h +++ b/include/linux/lis3lv02d.h | |||
@@ -64,6 +64,8 @@ struct lis3lv02d_platform_data { | |||
64 | s8 axis_x; | 64 | s8 axis_x; |
65 | s8 axis_y; | 65 | s8 axis_y; |
66 | s8 axis_z; | 66 | s8 axis_z; |
67 | #define LIS3_USE_REGULATOR_CTRL 0x01 | ||
68 | u16 driver_features; | ||
67 | int (*setup_resources)(void); | 69 | int (*setup_resources)(void); |
68 | int (*release_resources)(void); | 70 | int (*release_resources)(void); |
69 | /* Limits for selftest are specified in chip data sheet */ | 71 | /* Limits for selftest are specified in chip data sheet */ |