Blame view

drivers/mfd/aat2870-core.c 10.5 KB
2b27bdcc2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
09d6292be   Jin Park   mfd: Add AAT2870 ...
2
3
4
5
6
  /*
   * linux/drivers/mfd/aat2870-core.c
   *
   * Copyright (c) 2011, NVIDIA Corporation.
   * Author: Jin Park <jinyoungp@nvidia.com>
09d6292be   Jin Park   mfd: Add AAT2870 ...
7
8
9
   */
  
  #include <linux/kernel.h>
09d6292be   Jin Park   mfd: Add AAT2870 ...
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
  #include <linux/init.h>
  #include <linux/debugfs.h>
  #include <linux/slab.h>
  #include <linux/uaccess.h>
  #include <linux/i2c.h>
  #include <linux/delay.h>
  #include <linux/gpio.h>
  #include <linux/mfd/core.h>
  #include <linux/mfd/aat2870.h>
  #include <linux/regulator/machine.h>
  
  static struct aat2870_register aat2870_regs[AAT2870_REG_NUM] = {
  	/* readable, writeable, value */
  	{ 0, 1, 0x00 },	/* 0x00 AAT2870_BL_CH_EN */
  	{ 0, 1, 0x16 },	/* 0x01 AAT2870_BLM */
  	{ 0, 1, 0x16 },	/* 0x02 AAT2870_BLS */
  	{ 0, 1, 0x56 },	/* 0x03 AAT2870_BL1 */
  	{ 0, 1, 0x56 },	/* 0x04 AAT2870_BL2 */
  	{ 0, 1, 0x56 },	/* 0x05 AAT2870_BL3 */
  	{ 0, 1, 0x56 },	/* 0x06 AAT2870_BL4 */
  	{ 0, 1, 0x56 },	/* 0x07 AAT2870_BL5 */
  	{ 0, 1, 0x56 },	/* 0x08 AAT2870_BL6 */
  	{ 0, 1, 0x56 },	/* 0x09 AAT2870_BL7 */
  	{ 0, 1, 0x56 },	/* 0x0A AAT2870_BL8 */
  	{ 0, 1, 0x00 },	/* 0x0B AAT2870_FLR */
  	{ 0, 1, 0x03 },	/* 0x0C AAT2870_FM */
  	{ 0, 1, 0x03 },	/* 0x0D AAT2870_FS */
  	{ 0, 1, 0x10 },	/* 0x0E AAT2870_ALS_CFG0 */
  	{ 0, 1, 0x06 },	/* 0x0F AAT2870_ALS_CFG1 */
  	{ 0, 1, 0x00 },	/* 0x10 AAT2870_ALS_CFG2 */
  	{ 1, 0, 0x00 },	/* 0x11 AAT2870_AMB */
  	{ 0, 1, 0x00 },	/* 0x12 AAT2870_ALS0 */
  	{ 0, 1, 0x00 },	/* 0x13 AAT2870_ALS1 */
  	{ 0, 1, 0x00 },	/* 0x14 AAT2870_ALS2 */
  	{ 0, 1, 0x00 },	/* 0x15 AAT2870_ALS3 */
  	{ 0, 1, 0x00 },	/* 0x16 AAT2870_ALS4 */
  	{ 0, 1, 0x00 },	/* 0x17 AAT2870_ALS5 */
  	{ 0, 1, 0x00 },	/* 0x18 AAT2870_ALS6 */
  	{ 0, 1, 0x00 },	/* 0x19 AAT2870_ALS7 */
  	{ 0, 1, 0x00 },	/* 0x1A AAT2870_ALS8 */
  	{ 0, 1, 0x00 },	/* 0x1B AAT2870_ALS9 */
  	{ 0, 1, 0x00 },	/* 0x1C AAT2870_ALSA */
  	{ 0, 1, 0x00 },	/* 0x1D AAT2870_ALSB */
  	{ 0, 1, 0x00 },	/* 0x1E AAT2870_ALSC */
  	{ 0, 1, 0x00 },	/* 0x1F AAT2870_ALSD */
  	{ 0, 1, 0x00 },	/* 0x20 AAT2870_ALSE */
  	{ 0, 1, 0x00 },	/* 0x21 AAT2870_ALSF */
  	{ 0, 1, 0x00 },	/* 0x22 AAT2870_SUB_SET */
  	{ 0, 1, 0x00 },	/* 0x23 AAT2870_SUB_CTRL */
  	{ 0, 1, 0x00 },	/* 0x24 AAT2870_LDO_AB */
  	{ 0, 1, 0x00 },	/* 0x25 AAT2870_LDO_CD */
  	{ 0, 1, 0x00 },	/* 0x26 AAT2870_LDO_EN */
  };
  
  static struct mfd_cell aat2870_devs[] = {
  	{
  		.name = "aat2870-backlight",
  		.id = AAT2870_ID_BL,
  		.pdata_size = sizeof(struct aat2870_bl_platform_data),
  	},
  	{
  		.name = "aat2870-regulator",
  		.id = AAT2870_ID_LDOA,
  		.pdata_size = sizeof(struct regulator_init_data),
  	},
  	{
  		.name = "aat2870-regulator",
  		.id = AAT2870_ID_LDOB,
  		.pdata_size = sizeof(struct regulator_init_data),
  	},
  	{
  		.name = "aat2870-regulator",
  		.id = AAT2870_ID_LDOC,
  		.pdata_size = sizeof(struct regulator_init_data),
  	},
  	{
  		.name = "aat2870-regulator",
  		.id = AAT2870_ID_LDOD,
  		.pdata_size = sizeof(struct regulator_init_data),
  	},
  };
  
  static int __aat2870_read(struct aat2870_data *aat2870, u8 addr, u8 *val)
  {
  	int ret;
  
  	if (addr >= AAT2870_REG_NUM) {
  		dev_err(aat2870->dev, "Invalid address, 0x%02x
  ", addr);
  		return -EINVAL;
  	}
  
  	if (!aat2870->reg_cache[addr].readable) {
  		*val = aat2870->reg_cache[addr].value;
  		goto out;
  	}
  
  	ret = i2c_master_send(aat2870->client, &addr, 1);
  	if (ret < 0)
  		return ret;
  	if (ret != 1)
  		return -EIO;
  
  	ret = i2c_master_recv(aat2870->client, val, 1);
  	if (ret < 0)
  		return ret;
  	if (ret != 1)
  		return -EIO;
  
  out:
  	dev_dbg(aat2870->dev, "read: addr=0x%02x, val=0x%02x
  ", addr, *val);
  	return 0;
  }
  
  static int __aat2870_write(struct aat2870_data *aat2870, u8 addr, u8 val)
  {
  	u8 msg[2];
  	int ret;
  
  	if (addr >= AAT2870_REG_NUM) {
  		dev_err(aat2870->dev, "Invalid address, 0x%02x
  ", addr);
  		return -EINVAL;
  	}
  
  	if (!aat2870->reg_cache[addr].writeable) {
  		dev_err(aat2870->dev, "Address 0x%02x is not writeable
  ",
  			addr);
  		return -EINVAL;
  	}
  
  	msg[0] = addr;
  	msg[1] = val;
  	ret = i2c_master_send(aat2870->client, msg, 2);
  	if (ret < 0)
  		return ret;
  	if (ret != 2)
  		return -EIO;
  
  	aat2870->reg_cache[addr].value = val;
  
  	dev_dbg(aat2870->dev, "write: addr=0x%02x, val=0x%02x
  ", addr, val);
  	return 0;
  }
  
  static int aat2870_read(struct aat2870_data *aat2870, u8 addr, u8 *val)
  {
  	int ret;
  
  	mutex_lock(&aat2870->io_lock);
  	ret = __aat2870_read(aat2870, addr, val);
  	mutex_unlock(&aat2870->io_lock);
  
  	return ret;
  }
  
  static int aat2870_write(struct aat2870_data *aat2870, u8 addr, u8 val)
  {
  	int ret;
  
  	mutex_lock(&aat2870->io_lock);
  	ret = __aat2870_write(aat2870, addr, val);
  	mutex_unlock(&aat2870->io_lock);
  
  	return ret;
  }
  
  static int aat2870_update(struct aat2870_data *aat2870, u8 addr, u8 mask,
  			  u8 val)
  {
  	int change;
  	u8 old_val, new_val;
  	int ret;
  
  	mutex_lock(&aat2870->io_lock);
  
  	ret = __aat2870_read(aat2870, addr, &old_val);
  	if (ret)
  		goto out_unlock;
  
  	new_val = (old_val & ~mask) | (val & mask);
  	change = old_val != new_val;
  	if (change)
  		ret = __aat2870_write(aat2870, addr, new_val);
  
  out_unlock:
  	mutex_unlock(&aat2870->io_lock);
  
  	return ret;
  }
  
  static inline void aat2870_enable(struct aat2870_data *aat2870)
  {
  	if (aat2870->en_pin >= 0)
  		gpio_set_value(aat2870->en_pin, 1);
  
  	aat2870->is_enable = 1;
  }
  
  static inline void aat2870_disable(struct aat2870_data *aat2870)
  {
  	if (aat2870->en_pin >= 0)
  		gpio_set_value(aat2870->en_pin, 0);
  
  	aat2870->is_enable = 0;
  }
  
  #ifdef CONFIG_DEBUG_FS
  static ssize_t aat2870_dump_reg(struct aat2870_data *aat2870, char *buf)
  {
  	u8 addr, val;
  	ssize_t count = 0;
  	int ret;
  
  	count += sprintf(buf, "aat2870 registers
  ");
  	for (addr = 0; addr < AAT2870_REG_NUM; addr++) {
  		count += sprintf(buf + count, "0x%02x: ", addr);
  		if (count >= PAGE_SIZE - 1)
  			break;
  
  		ret = aat2870->read(aat2870, addr, &val);
  		if (ret == 0)
  			count += snprintf(buf + count, PAGE_SIZE - count,
  					  "0x%02x", val);
  		else
  			count += snprintf(buf + count, PAGE_SIZE - count,
  					  "<read fail: %d>", ret);
  
  		if (count >= PAGE_SIZE - 1)
  			break;
  
  		count += snprintf(buf + count, PAGE_SIZE - count, "
  ");
  		if (count >= PAGE_SIZE - 1)
  			break;
  	}
  
  	/* Truncate count; min() would cause a warning */
  	if (count >= PAGE_SIZE)
  		count = PAGE_SIZE - 1;
  
  	return count;
  }
09d6292be   Jin Park   mfd: Add AAT2870 ...
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
  static ssize_t aat2870_reg_read_file(struct file *file, char __user *user_buf,
  				     size_t count, loff_t *ppos)
  {
  	struct aat2870_data *aat2870 = file->private_data;
  	char *buf;
  	ssize_t ret;
  
  	buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
  	if (!buf)
  		return -ENOMEM;
  
  	ret = aat2870_dump_reg(aat2870, buf);
  	if (ret >= 0)
  		ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
  
  	kfree(buf);
  
  	return ret;
  }
  
  static ssize_t aat2870_reg_write_file(struct file *file,
  				      const char __user *user_buf, size_t count,
  				      loff_t *ppos)
  {
  	struct aat2870_data *aat2870 = file->private_data;
  	char buf[32];
c38d66ac9   Jin Park   mfd: Fix aat2870 ...
283
  	ssize_t buf_size;
09d6292be   Jin Park   mfd: Add AAT2870 ...
284
285
286
  	char *start = buf;
  	unsigned long addr, val;
  	int ret;
ecf67ac33   Sachin Kamat   mfd: aat2870: Fix...
287
  	buf_size = min(count, (size_t)(sizeof(buf)-1));
09d6292be   Jin Park   mfd: Add AAT2870 ...
288
289
290
291
292
293
294
295
296
  	if (copy_from_user(buf, user_buf, buf_size)) {
  		dev_err(aat2870->dev, "Failed to copy from user
  ");
  		return -EFAULT;
  	}
  	buf[buf_size] = 0;
  
  	while (*start == ' ')
  		start++;
0ebc1c25c   Lee Jones   mfd: aat2870-core...
297
298
299
  	ret = kstrtoul(start, 16, &addr);
  	if (ret)
  		return ret;
09d6292be   Jin Park   mfd: Add AAT2870 ...
300
301
302
303
304
305
306
307
  	if (addr >= AAT2870_REG_NUM) {
  		dev_err(aat2870->dev, "Invalid address, 0x%lx
  ", addr);
  		return -EINVAL;
  	}
  
  	while (*start == ' ')
  		start++;
8420a2413   Jingoo Han   mfd: Replace stri...
308
309
310
  	ret = kstrtoul(start, 16, &val);
  	if (ret)
  		return ret;
09d6292be   Jin Park   mfd: Add AAT2870 ...
311
312
313
314
315
316
317
318
319
  
  	ret = aat2870->write(aat2870, (u8)addr, (u8)val);
  	if (ret)
  		return ret;
  
  	return buf_size;
  }
  
  static const struct file_operations aat2870_reg_fops = {
234e34058   Stephen Boyd   simple_open: auto...
320
  	.open = simple_open,
09d6292be   Jin Park   mfd: Add AAT2870 ...
321
322
323
324
325
326
327
  	.read = aat2870_reg_read_file,
  	.write = aat2870_reg_write_file,
  };
  
  static void aat2870_init_debugfs(struct aat2870_data *aat2870)
  {
  	aat2870->dentry_root = debugfs_create_dir("aat2870", NULL);
09d6292be   Jin Park   mfd: Add AAT2870 ...
328

dc607f6bb   Greg Kroah-Hartman   mfd: aat2870: no ...
329
330
  	debugfs_create_file("regs", 0644, aat2870->dentry_root, aat2870,
  			    &aat2870_reg_fops);
09d6292be   Jin Park   mfd: Add AAT2870 ...
331
  }
09d6292be   Jin Park   mfd: Add AAT2870 ...
332
333
334
335
  #else
  static inline void aat2870_init_debugfs(struct aat2870_data *aat2870)
  {
  }
09d6292be   Jin Park   mfd: Add AAT2870 ...
336
337
338
339
340
  #endif /* CONFIG_DEBUG_FS */
  
  static int aat2870_i2c_probe(struct i2c_client *client,
  			     const struct i2c_device_id *id)
  {
334a41ce9   Jingoo Han   mfd: Use dev_get_...
341
  	struct aat2870_platform_data *pdata = dev_get_platdata(&client->dev);
09d6292be   Jin Park   mfd: Add AAT2870 ...
342
343
344
  	struct aat2870_data *aat2870;
  	int i, j;
  	int ret = 0;
0287714b4   Jingoo Han   mfd: aat2870: Use...
345
346
  	aat2870 = devm_kzalloc(&client->dev, sizeof(struct aat2870_data),
  				GFP_KERNEL);
4374b20c6   Lee Jones   mfd: aat2870-core...
347
  	if (!aat2870)
0287714b4   Jingoo Han   mfd: aat2870: Use...
348
  		return -ENOMEM;
09d6292be   Jin Park   mfd: Add AAT2870 ...
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
  
  	aat2870->dev = &client->dev;
  	dev_set_drvdata(aat2870->dev, aat2870);
  
  	aat2870->client = client;
  	i2c_set_clientdata(client, aat2870);
  
  	aat2870->reg_cache = aat2870_regs;
  
  	if (pdata->en_pin < 0)
  		aat2870->en_pin = -1;
  	else
  		aat2870->en_pin = pdata->en_pin;
  
  	aat2870->init = pdata->init;
  	aat2870->uninit = pdata->uninit;
  	aat2870->read = aat2870_read;
  	aat2870->write = aat2870_write;
  	aat2870->update = aat2870_update;
  
  	mutex_init(&aat2870->io_lock);
  
  	if (aat2870->init)
  		aat2870->init(aat2870);
  
  	if (aat2870->en_pin >= 0) {
0287714b4   Jingoo Han   mfd: aat2870: Use...
375
376
  		ret = devm_gpio_request_one(&client->dev, aat2870->en_pin,
  					GPIOF_OUT_INIT_HIGH, "aat2870-en");
09d6292be   Jin Park   mfd: Add AAT2870 ...
377
378
379
380
  		if (ret < 0) {
  			dev_err(&client->dev,
  				"Failed to request GPIO %d
  ", aat2870->en_pin);
0287714b4   Jingoo Han   mfd: aat2870: Use...
381
  			return ret;
09d6292be   Jin Park   mfd: Add AAT2870 ...
382
  		}
09d6292be   Jin Park   mfd: Add AAT2870 ...
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
  	}
  
  	aat2870_enable(aat2870);
  
  	for (i = 0; i < pdata->num_subdevs; i++) {
  		for (j = 0; j < ARRAY_SIZE(aat2870_devs); j++) {
  			if ((pdata->subdevs[i].id == aat2870_devs[j].id) &&
  					!strcmp(pdata->subdevs[i].name,
  						aat2870_devs[j].name)) {
  				aat2870_devs[j].platform_data =
  					pdata->subdevs[i].platform_data;
  				break;
  			}
  		}
  	}
  
  	ret = mfd_add_devices(aat2870->dev, 0, aat2870_devs,
0848c94fb   Mark Brown   mfd: core: Push i...
400
  			      ARRAY_SIZE(aat2870_devs), NULL, 0, NULL);
09d6292be   Jin Park   mfd: Add AAT2870 ...
401
402
403
404
405
406
407
408
409
410
411
412
  	if (ret != 0) {
  		dev_err(aat2870->dev, "Failed to add subdev: %d
  ", ret);
  		goto out_disable;
  	}
  
  	aat2870_init_debugfs(aat2870);
  
  	return 0;
  
  out_disable:
  	aat2870_disable(aat2870);
09d6292be   Jin Park   mfd: Add AAT2870 ...
413
414
  	return ret;
  }
5214e5659   Mark Brown   mfd: Convert aat2...
415
416
  #ifdef CONFIG_PM_SLEEP
  static int aat2870_i2c_suspend(struct device *dev)
09d6292be   Jin Park   mfd: Add AAT2870 ...
417
  {
5214e5659   Mark Brown   mfd: Convert aat2...
418
  	struct i2c_client *client = to_i2c_client(dev);
09d6292be   Jin Park   mfd: Add AAT2870 ...
419
420
421
422
423
424
  	struct aat2870_data *aat2870 = i2c_get_clientdata(client);
  
  	aat2870_disable(aat2870);
  
  	return 0;
  }
5214e5659   Mark Brown   mfd: Convert aat2...
425
  static int aat2870_i2c_resume(struct device *dev)
09d6292be   Jin Park   mfd: Add AAT2870 ...
426
  {
5214e5659   Mark Brown   mfd: Convert aat2...
427
  	struct i2c_client *client = to_i2c_client(dev);
09d6292be   Jin Park   mfd: Add AAT2870 ...
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
  	struct aat2870_data *aat2870 = i2c_get_clientdata(client);
  	struct aat2870_register *reg = NULL;
  	int i;
  
  	aat2870_enable(aat2870);
  
  	/* restore registers */
  	for (i = 0; i < AAT2870_REG_NUM; i++) {
  		reg = &aat2870->reg_cache[i];
  		if (reg->writeable)
  			aat2870->write(aat2870, i, reg->value);
  	}
  
  	return 0;
  }
5214e5659   Mark Brown   mfd: Convert aat2...
443
444
445
446
  #endif /* CONFIG_PM_SLEEP */
  
  static SIMPLE_DEV_PM_OPS(aat2870_pm_ops, aat2870_i2c_suspend,
  			 aat2870_i2c_resume);
09d6292be   Jin Park   mfd: Add AAT2870 ...
447

d4e948636   Axel Lin   mfd: Constify aat...
448
  static const struct i2c_device_id aat2870_i2c_id_table[] = {
09d6292be   Jin Park   mfd: Add AAT2870 ...
449
450
451
  	{ "aat2870", 0 },
  	{ }
  };
09d6292be   Jin Park   mfd: Add AAT2870 ...
452
453
454
  
  static struct i2c_driver aat2870_i2c_driver = {
  	.driver = {
17f808a7f   Paul Gortmaker   mfd: aat2870-core...
455
456
457
  		.name			= "aat2870",
  		.pm			= &aat2870_pm_ops,
  		.suppress_bind_attrs	= true,
09d6292be   Jin Park   mfd: Add AAT2870 ...
458
459
  	},
  	.probe		= aat2870_i2c_probe,
09d6292be   Jin Park   mfd: Add AAT2870 ...
460
461
462
463
464
465
466
467
  	.id_table	= aat2870_i2c_id_table,
  };
  
  static int __init aat2870_init(void)
  {
  	return i2c_add_driver(&aat2870_i2c_driver);
  }
  subsys_initcall(aat2870_init);