Advertisement
Guest User

Untitled

a guest
Dec 28th, 2017
357
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 23.09 KB | None | 0 0
  1. From 005cd656b8038be8a651e3e9d8db828fbcf155b1 Mon Sep 17 00:00:00 2001
  2. From: pinkflozd <lk@varjanta.com>
  3. Date: Sat, 1 Aug 2015 18:04:00 +0200
  4. Subject: [PATCH] iio: Capella cm32181 device driver.
  5.  
  6. ---
  7. drivers/iio/light/Kconfig | 11 +
  8. drivers/iio/light/Makefile | 1 +
  9. drivers/iio/light/cm3218x.c | 801 ++++++++++++++++++++++++++++++++++++++++++++
  10. 3 files changed, 813 insertions(+)
  11. create mode 100644 drivers/iio/light/cm3218x.c
  12.  
  13. diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
  14. index 01a1a16..5f104b9 100644
  15. --- a/drivers/iio/light/Kconfig
  16. +++ b/drivers/iio/light/Kconfig
  17. @@ -48,6 +48,17 @@ config CM32181
  18. To compile this driver as a module, choose M here:
  19. the module will be called cm32181.
  20.  
  21. +config CM3218X
  22. + depends on I2C
  23. + tristate "CM3218X driver"
  24. + help
  25. + Say Y here if you use cm3218x.
  26. + This option enables ambient light sensor using
  27. + Capella cm3218x device driver.
  28. +
  29. + To compile this driver as a module, choose M here:
  30. + the module will be called cm3218x.
  31. +
  32. config CM3232
  33. depends on I2C
  34. tristate "CM3232 ambient light sensor"
  35. diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
  36. index ad7c30fe..2fdaeba 100644
  37. --- a/drivers/iio/light/Makefile
  38. +++ b/drivers/iio/light/Makefile
  39. @@ -7,6 +7,7 @@ obj-$(CONFIG_ADJD_S311) += adjd_s311.o
  40. obj-$(CONFIG_AL3320A) += al3320a.o
  41. obj-$(CONFIG_APDS9300) += apds9300.o
  42. obj-$(CONFIG_CM32181) += cm32181.o
  43. +obj-$(CONFIG_CM3218X) += cm3218x.o
  44. obj-$(CONFIG_CM3232) += cm3232.o
  45. obj-$(CONFIG_CM3323) += cm3323.o
  46. obj-$(CONFIG_CM36651) += cm36651.o
  47. diff --git a/drivers/iio/light/cm3218x.c b/drivers/iio/light/cm3218x.c
  48. new file mode 100644
  49. index 0000000..cc63b75
  50. --- /dev/null
  51. +++ b/drivers/iio/light/cm3218x.c
  52. @@ -0,0 +1,801 @@
  53. +/*
  54. + * Copyright (C) 2014 Capella Microsystems Inc.
  55. + * Author: Kevin Tsai <ktsai@capellamicro.com>
  56. + *
  57. + * This program is free software; you can redistribute it and/or modify it
  58. + * under the terms of the GNU General Public License version 2, as published
  59. + * by the Free Software Foundation.
  60. + *
  61. + * Special thanks Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
  62. + * help to add ACPI support.
  63. + *
  64. + */
  65. +
  66. +#include <linux/delay.h>
  67. +#include <linux/err.h>
  68. +#include <linux/i2c.h>
  69. +#include <linux/mutex.h>
  70. +#include <linux/module.h>
  71. +#include <linux/interrupt.h>
  72. +#include <linux/regulator/consumer.h>
  73. +#include <linux/iio/iio.h>
  74. +#include <linux/iio/sysfs.h>
  75. +#include <linux/iio/events.h>
  76. +#include <linux/init.h>
  77. +
  78. +#ifdef CONFIG_ACPI
  79. +#include <linux/acpi.h>
  80. +#endif /* CONFIG_ACPI */
  81. +
  82. +/* Registers Address */
  83. +#define CM3218X_REG_ADDR_CMD 0x00
  84. +#define CM3218X_REG_ADDR_WH 0x01
  85. +#define CM3218X_REG_ADDR_WL 0x02
  86. +#define CM3218X_REG_ADDR_TEST 0x03
  87. +#define CM3218X_REG_ADDR_ALS 0x04
  88. +#define CM3218X_REG_ADDR_STATUS 0x06
  89. +#define CM3218X_REG_ADDR_ID 0x07
  90. +
  91. +/* Number of Configurable Registers */
  92. +#define CM3218X_CONF_REG_NUM 16
  93. +
  94. +/* CMD register */
  95. +#define CM3218X_CMD_ALS_DISABLE BIT(0)
  96. +#define CM3218X_CMD_ALS_INT_EN BIT(1)
  97. +#define CM3218X_CMD_ALS_THRES_WINDOW BIT(2)
  98. +
  99. +#define CM3218X_CMD_ALS_PERS_SHIFT 4
  100. +#define CM3218X_CMD_ALS_PERS_MASK (0x03 << CM3218X_CMD_ALS_PERS_SHIFT)
  101. +#define CM3218X_CMD_ALS_PERS_DEFAULT (0x01 << CM3218X_CMD_ALS_PERS_SHIFT)
  102. +
  103. +#define CM3218X_CMD_ALS_IT_SHIFT 6
  104. +#define CM3218X_CMD_ALS_IT_MASK (0x0F << CM3218X_CMD_ALS_IT_SHIFT)
  105. +#define CM3218X_CMD_ALS_IT_DEFAULT (0x01 << CM3218X_CMD_ALS_IT_SHIFT)
  106. +
  107. +#define CM3218X_CMD_ALS_HS_SHIFT 11
  108. +#define CM3218X_CMD_ALS_HS_MASK (0x01 << CM3218X_CMD_ALS_HS_SHIFT)
  109. +#define CM3218X_CMD_ALS_HS_DEFAULT (0x00 << CM3218X_CMD_ALS_HS_SHIFT)
  110. +
  111. +#define CM3218X_CMD_DEFAULT (CM3218X_CMD_ALS_THRES_WINDOW |\
  112. + CM3218X_CMD_ALS_PERS_DEFAULT |\
  113. + CM3218X_CMD_ALS_IT_DEFAULT |\
  114. + CM3218X_CMD_ALS_HS_DEFAULT)
  115. +
  116. +#define CM3218X_WH_DEFAULT 0xFFFF
  117. +#define CM3218X_WL_DEFAULT 0x0000
  118. +
  119. +#define CM3218X_CALIBSCALE_DEFAULT 100000
  120. +#define CM3218X_CALIBSCALE_RESOLUTION 100000
  121. +#define CM3218X_MLUX_PER_LUX 1000
  122. +#define CM3218X_THRESHOLD_PERCENT 10 /* 10 percent */
  123. +
  124. +#define CM3218X_ARA 0x0C
  125. +
  126. +/* CM3218X family */
  127. +enum {
  128. + cm3218,
  129. + cm32181,
  130. + cm32182
  131. +};
  132. +
  133. +/* CM3218 Family */
  134. +#define CM3218_MLUX_PER_BIT_DEFAULT 5 /* Depend on system */
  135. +#define CM3218_MLUX_PER_BIT_BASE_IT 800000
  136. +static const int CM3218_als_it_bits[] = {0, 1, 2, 3};
  137. +static const int CM3218_als_it_values[] = {100000, 200000, 400000, 800000};
  138. +
  139. +/* CM32181 Family */
  140. +#define CM32181_MLUX_PER_BIT_DEFAULT 5
  141. +#define CM32181_MLUX_PER_BIT_BASE_IT 800000
  142. +static const int CM32181_als_it_bits[] = {12, 8, 0, 1, 2, 3};
  143. +static const int CM32181_als_it_values[] = {
  144. + 25000, 50000, 100000, 200000, 400000, 800000};
  145. +
  146. +struct cm3218x_als_info {
  147. + u32 id;
  148. + int int_type;
  149. +#define CM3218X_INT_TYPE_SMBUS 0
  150. +#define CM3218X_INT_TYPE_I2C 1
  151. + int regs_bmp;
  152. + int calibscale;
  153. + int mlux_per_bit;
  154. + int mlux_per_bit_base_it;
  155. + const int *als_it_bits;
  156. + const int *als_it_values;
  157. + const int num_als_it;
  158. + int als_raw;
  159. +};
  160. +
  161. +static struct cm3218x_als_info cm3218_info = {
  162. + .id = 3218,
  163. + .int_type = CM3218X_INT_TYPE_SMBUS,
  164. + .regs_bmp = 0x0F,
  165. + .calibscale = CM3218X_CALIBSCALE_DEFAULT,
  166. + .mlux_per_bit = CM3218_MLUX_PER_BIT_DEFAULT,
  167. + .mlux_per_bit_base_it = CM3218_MLUX_PER_BIT_BASE_IT,
  168. + .als_it_bits = CM3218_als_it_bits,
  169. + .als_it_values = CM3218_als_it_values,
  170. + .num_als_it = ARRAY_SIZE(CM3218_als_it_bits),
  171. +};
  172. +
  173. +static struct cm3218x_als_info cm32181_info = {
  174. + .id = 32181,
  175. + .int_type = CM3218X_INT_TYPE_I2C,
  176. + .regs_bmp = 0x0F,
  177. + .calibscale = CM3218X_CALIBSCALE_DEFAULT,
  178. + .mlux_per_bit = CM32181_MLUX_PER_BIT_DEFAULT,
  179. + .mlux_per_bit_base_it = CM32181_MLUX_PER_BIT_BASE_IT,
  180. + .als_it_bits = CM32181_als_it_bits,
  181. + .als_it_values = CM32181_als_it_values,
  182. + .num_als_it = ARRAY_SIZE(CM32181_als_it_bits),
  183. +};
  184. +
  185. +static struct cm3218x_als_info cm32182_info = {
  186. + .id = 32182,
  187. + .int_type = CM3218X_INT_TYPE_I2C,
  188. + .regs_bmp = 0x0F,
  189. + .calibscale = CM3218X_CALIBSCALE_DEFAULT,
  190. + .mlux_per_bit = CM32181_MLUX_PER_BIT_DEFAULT,
  191. + .mlux_per_bit_base_it = CM32181_MLUX_PER_BIT_BASE_IT,
  192. + .als_it_bits = CM32181_als_it_bits,
  193. + .als_it_values = CM32181_als_it_values,
  194. + .num_als_it = ARRAY_SIZE(CM32181_als_it_bits),
  195. +};
  196. +
  197. +struct cm3218x_chip {
  198. + struct i2c_client *client;
  199. + struct mutex lock;
  200. + u16 conf_regs[CM3218X_CONF_REG_NUM];
  201. + struct cm3218x_als_info *als_info;
  202. +};
  203. +
  204. +static int cm3218x_get_lux(struct cm3218x_chip *chip);
  205. +static int cm3218x_threshold_update(struct cm3218x_chip *chip, int percent);
  206. +static int cm3218x_read_als_it(struct cm3218x_chip *chip, int *val2);
  207. +
  208. +/**
  209. + * cm3218x_read_ara() - Read ARA register
  210. + * @chip: pointer of struct cm3218x.
  211. + *
  212. + * Read SMBus ARA register.
  213. + *
  214. + * Return: -ENODEV for failed. Otherwise return the register value.
  215. + */
  216. +static int cm3218x_read_ara(struct cm3218x_chip *chip)
  217. +{
  218. + unsigned char rxdata;
  219. + struct i2c_msg msgs[] = {
  220. + {
  221. + .addr = CM3218X_ARA,
  222. + .flags = I2C_M_RD,
  223. + .len = 1,
  224. + .buf = &rxdata,
  225. + },
  226. + };
  227. +
  228. + if (i2c_transfer(chip->client->adapter, msgs, 1) < 0)
  229. + return -ENODEV;
  230. + return rxdata;
  231. +}
  232. +
  233. +/**
  234. + * cm3218x_interrupt_config() - Enable/Disable CM3218X interrupt
  235. + * @chip: pointer of struct cm3218x.
  236. + * @enable: 0 to disable; otherwise to enable
  237. + *
  238. + * Config CM3218X interrupt control bit.
  239. + *
  240. + * Return: 0 for success; otherwise for error code.
  241. + */
  242. +static int cm3218x_interrupt_config(struct cm3218x_chip *chip, int enable)
  243. +{
  244. + struct i2c_client *client = chip->client;
  245. + struct cm3218x_als_info *als_info = chip->als_info;
  246. + int status;
  247. +
  248. + if (!als_info)
  249. + return -ENODEV;
  250. +
  251. + /* Force to clean interrupt */
  252. + if (als_info->int_type == CM3218X_INT_TYPE_I2C) {
  253. + status = i2c_smbus_read_word_data(client,
  254. + CM3218X_REG_ADDR_STATUS);
  255. + if (status < 0)
  256. + als_info->int_type = CM3218X_INT_TYPE_SMBUS;
  257. + }
  258. + if (als_info->int_type == CM3218X_INT_TYPE_SMBUS)
  259. + cm3218x_read_ara(chip);
  260. +
  261. + if (enable)
  262. + chip->conf_regs[CM3218X_REG_ADDR_CMD] |=
  263. + CM3218X_CMD_ALS_INT_EN;
  264. + else
  265. + chip->conf_regs[CM3218X_REG_ADDR_CMD] &=
  266. + ~CM3218X_CMD_ALS_INT_EN;
  267. +
  268. + status = i2c_smbus_write_word_data(client, CM3218X_REG_ADDR_CMD,
  269. + chip->conf_regs[CM3218X_REG_ADDR_CMD]);
  270. +
  271. + if (status < 0)
  272. + return -ENODEV;
  273. +
  274. + return status;
  275. +}
  276. +
  277. +#ifdef CONFIG_ACPI
  278. +/**
  279. + * cm3218x_acpi_get_cpm_info() - Get CPM object from ACPI
  280. + * @client pointer of struct i2c_client.
  281. + * @obj_name pointer of ACPI object name.
  282. + * @count maximum size of return array.
  283. + * @vals pointer of array for return elements.
  284. + *
  285. + * Convert ACPI CPM table to array. Special thanks Srinivas Pandruvada's
  286. + * help to implement this routine.
  287. + *
  288. + * Return: -ENODEV for fail. Otherwise is number of elements.
  289. + */
  290. +static int cm3218x_acpi_get_cpm_info(struct i2c_client *client, char *obj_name,
  291. + int count, u64 *vals)
  292. +{
  293. + acpi_handle handle;
  294. + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
  295. + int i;
  296. + acpi_status status;
  297. + union acpi_object *cpm;
  298. +
  299. + handle = ACPI_HANDLE(&client->dev);
  300. + if (!handle)
  301. + return -ENODEV;
  302. +
  303. + status = acpi_evaluate_object(handle, obj_name, NULL, &buffer);
  304. + if (ACPI_FAILURE(status)) {
  305. + dev_err(&client->dev, "object %s not found\n", obj_name);
  306. + return -ENODEV;
  307. + }
  308. +
  309. + cpm = buffer.pointer;
  310. + for (i = 0; i < cpm->package.count && i < count; ++i) {
  311. + union acpi_object *elem;
  312. + elem = &(cpm->package.elements[i]);
  313. + vals[i] = elem->integer.value;
  314. + }
  315. +
  316. + kfree(buffer.pointer);
  317. +
  318. + return cpm->package.count;
  319. +}
  320. +#endif /* CONFIG_ACPI */
  321. +
  322. +/**
  323. + * cm3218x_reg_init() - Initialize CM3218X registers
  324. + * @chip: pointer of struct cm3218x.
  325. + *
  326. + * Initialize CM3218X ambient light sensor register to default values.
  327. + *
  328. + Return: 0 for success; otherwise for error code.
  329. + */
  330. +static int cm3218x_reg_init(struct cm3218x_chip *chip)
  331. +{
  332. + struct i2c_client *client = chip->client;
  333. + int i;
  334. + s32 ret;
  335. + struct cm3218x_als_info *als_info;
  336. +#ifdef CONFIG_ACPI
  337. + int cpm_elem_count;
  338. + u64 cpm_elems[20];
  339. +#endif /* CONFIG_ACPI */
  340. +
  341. + /* Default device */
  342. + chip->als_info = &cm3218_info;
  343. + chip->conf_regs[CM3218X_REG_ADDR_CMD] = CM3218X_CMD_DEFAULT;
  344. + chip->conf_regs[CM3218X_REG_ADDR_WH] = CM3218X_WH_DEFAULT;
  345. + chip->conf_regs[CM3218X_REG_ADDR_WL] = CM3218X_WL_DEFAULT;
  346. +
  347. + /* Disable interrupt */
  348. + cm3218x_interrupt_config(chip, 0);
  349. +
  350. + /* Disable Test Mode */
  351. + i2c_smbus_write_word_data(client, CM3218X_REG_ADDR_TEST, 0x0000);
  352. +
  353. + /* Disable device */
  354. + i2c_smbus_write_word_data(client, CM3218X_REG_ADDR_CMD,
  355. + CM3218X_CMD_ALS_DISABLE);
  356. +
  357. + /* Identify device */
  358. + ret = i2c_smbus_read_word_data(client, CM3218X_REG_ADDR_ID);
  359. + if (ret < 0)
  360. + return ret;
  361. + switch (ret & 0xFF) {
  362. + case 0x18:
  363. + als_info = chip->als_info = &cm3218_info;
  364. + if (ret & 0x0800)
  365. + als_info->int_type = CM3218X_INT_TYPE_I2C;
  366. + else
  367. + als_info->int_type = CM3218X_INT_TYPE_SMBUS;
  368. + break;
  369. + case 0x81:
  370. + als_info = chip->als_info = &cm32181_info;
  371. + break;
  372. + case 0x82:
  373. + als_info = chip->als_info = &cm32182_info;
  374. + break;
  375. + default:
  376. + return -ENODEV;
  377. + }
  378. +
  379. +#ifdef CONFIG_ACPI
  380. + if (ACPI_HANDLE(&client->dev)) {
  381. + /* Load from ACPI */
  382. + cpm_elem_count = cm3218x_acpi_get_cpm_info(client, "CPM0",
  383. + ARRAY_SIZE(cpm_elems),
  384. + cpm_elems);
  385. + if (cpm_elem_count > 0) {
  386. + int header_num = 3;
  387. + int reg_num = cpm_elem_count - header_num;
  388. +
  389. + als_info->id = cpm_elems[0];
  390. + als_info->regs_bmp = cpm_elems[2];
  391. + for (i = 0; i < reg_num; i++)
  392. + if (als_info->regs_bmp & (1<<i))
  393. + chip->conf_regs[i] =
  394. + cpm_elems[header_num+i];
  395. + }
  396. +
  397. + cpm_elem_count = cm3218x_acpi_get_cpm_info(client, "CPM1",
  398. + ARRAY_SIZE(cpm_elems),
  399. + cpm_elems);
  400. + if (cpm_elem_count > 0) {
  401. + als_info->mlux_per_bit = (int)cpm_elems[0] / 100;
  402. + als_info->calibscale = (int)cpm_elems[1];
  403. + }
  404. + }
  405. +#endif /* CONFIG_ACPI */
  406. +
  407. + /* Force to disable interrupt */
  408. + chip->conf_regs[CM3218X_REG_ADDR_CMD] &= ~CM3218X_CMD_ALS_INT_EN;
  409. +
  410. + /* Initialize registers */
  411. + for (i = 0; i < CM3218X_CONF_REG_NUM; i++) {
  412. + if (als_info->regs_bmp & (1<<i)) {
  413. + ret = i2c_smbus_write_word_data(client, i,
  414. + chip->conf_regs[i]);
  415. + if (ret < 0)
  416. + return ret;
  417. + }
  418. + }
  419. +
  420. + return 0;
  421. +}
  422. +
  423. +/**
  424. + * cm3218x_read_als_it() - Get sensor integration time (ms)
  425. + * @chip: pointer of struct cm3218x
  426. + * @val2: pointer of int to load the als_it value.
  427. + *
  428. + * Report the current integration time in milliseconds.
  429. + *
  430. + * Return: IIO_VAL_INT_PLUS_MICRO for success, otherwise -EINVAL.
  431. + */
  432. +static int cm3218x_read_als_it(struct cm3218x_chip *chip, int *val2)
  433. +{
  434. + struct cm3218x_als_info *als_info = chip->als_info;
  435. + u16 als_it;
  436. + int i;
  437. +
  438. + als_it = chip->conf_regs[CM3218X_REG_ADDR_CMD];
  439. + als_it &= CM3218X_CMD_ALS_IT_MASK;
  440. + als_it >>= CM3218X_CMD_ALS_IT_SHIFT;
  441. + for (i = 0; i < als_info->num_als_it; i++) {
  442. + if (als_it == als_info->als_it_bits[i]) {
  443. + *val2 = als_info->als_it_values[i];
  444. + return IIO_VAL_INT_PLUS_MICRO;
  445. + }
  446. + }
  447. +
  448. + return -EINVAL;
  449. +}
  450. +
  451. +/**
  452. + * cm3218x_write_als_it() - Write sensor integration time
  453. + * @chip: pointer of struct cm3218x.
  454. + * @val: integration time in milliseconds.
  455. + *
  456. + * Convert integration time (ms) to sensor value.
  457. + *
  458. + * Return: i2c_smbus_write_word_data command return value.
  459. + */
  460. +static int cm3218x_write_als_it(struct cm3218x_chip *chip, int val)
  461. +{
  462. + struct i2c_client *client = chip->client;
  463. + struct cm3218x_als_info *als_info = chip->als_info;
  464. + u16 als_it;
  465. + int ret, i;
  466. +
  467. + for (i = 0; i < als_info->num_als_it; i++)
  468. + if (val <= als_info->als_it_values[i])
  469. + break;
  470. + if (i >= als_info->num_als_it)
  471. + i = als_info->num_als_it - 1;
  472. +
  473. + als_it = als_info->als_it_bits[i];
  474. + als_it <<= CM3218X_CMD_ALS_IT_SHIFT;
  475. +
  476. + mutex_lock(&chip->lock);
  477. + chip->conf_regs[CM3218X_REG_ADDR_CMD] &=
  478. + ~CM3218X_CMD_ALS_IT_MASK;
  479. + chip->conf_regs[CM3218X_REG_ADDR_CMD] |=
  480. + als_it;
  481. + ret = i2c_smbus_write_word_data(client, CM3218X_REG_ADDR_CMD,
  482. + chip->conf_regs[CM3218X_REG_ADDR_CMD]);
  483. + mutex_unlock(&chip->lock);
  484. +
  485. + return ret;
  486. +}
  487. +
  488. +/**
  489. + * cm3218x_get_lux() - report current lux value
  490. + * @chip: pointer of struct cm3218x.
  491. + *
  492. + * Convert sensor raw data to lux. It depends on integration
  493. + * time and calibscale variable.
  494. + *
  495. + * Return: Positive value is lux, otherwise is error code.
  496. + */
  497. +static int cm3218x_get_lux(struct cm3218x_chip *chip)
  498. +{
  499. + struct i2c_client *client = chip->client;
  500. + struct cm3218x_als_info *als_info = chip->als_info;
  501. + int ret;
  502. + int als_it;
  503. + u64 tmp;
  504. +
  505. + /* Calculate mlux per bit based on als_it */
  506. + ret = cm3218x_read_als_it(chip, &als_it);
  507. + if (ret < 0)
  508. + return -EINVAL;
  509. + tmp = (__force u64)als_info->mlux_per_bit;
  510. + tmp *= als_info->mlux_per_bit_base_it;
  511. + tmp = div_u64(tmp, als_it);
  512. +
  513. + /* Get als_raw */
  514. + if (!(chip->conf_regs[CM3218X_REG_ADDR_CMD] & CM3218X_CMD_ALS_INT_EN))
  515. + als_info->als_raw = i2c_smbus_read_word_data(
  516. + client,
  517. + CM3218X_REG_ADDR_ALS);
  518. + if (als_info->als_raw < 0)
  519. + return als_info->als_raw;
  520. +
  521. + tmp *= als_info->als_raw;
  522. + tmp *= als_info->calibscale;
  523. + tmp = div_u64(tmp, CM3218X_CALIBSCALE_RESOLUTION);
  524. + tmp = div_u64(tmp, CM3218X_MLUX_PER_LUX);
  525. +
  526. + if (tmp > 0xFFFF)
  527. + tmp = 0xFFFF;
  528. +
  529. + return (int)tmp;
  530. +}
  531. +
  532. +static int cm3218x_read_raw(struct iio_dev *indio_dev,
  533. + struct iio_chan_spec const *chan,
  534. + int *val, int *val2, long mask)
  535. +{
  536. + struct cm3218x_chip *chip = iio_priv(indio_dev);
  537. + struct cm3218x_als_info *als_info = chip->als_info;
  538. + int ret;
  539. +
  540. + switch (mask) {
  541. + case IIO_CHAN_INFO_PROCESSED:
  542. + ret = cm3218x_get_lux(chip);
  543. + if (ret < 0)
  544. + return ret;
  545. + *val = ret;
  546. + return IIO_VAL_INT;
  547. + case IIO_CHAN_INFO_CALIBSCALE:
  548. + *val = als_info->calibscale;
  549. + return IIO_VAL_INT;
  550. + case IIO_CHAN_INFO_INT_TIME:
  551. + *val = 0;
  552. + ret = cm3218x_read_als_it(chip, val2);
  553. + return ret;
  554. + }
  555. +
  556. + return -EINVAL;
  557. +}
  558. +
  559. +static int cm3218x_write_raw(struct iio_dev *indio_dev,
  560. + struct iio_chan_spec const *chan,
  561. + int val, int val2, long mask)
  562. +{
  563. + struct cm3218x_chip *chip = iio_priv(indio_dev);
  564. + struct cm3218x_als_info *als_info = chip->als_info;
  565. + long ms;
  566. +
  567. + switch (mask) {
  568. + case IIO_CHAN_INFO_CALIBSCALE:
  569. + als_info->calibscale = val;
  570. + return val;
  571. + case IIO_CHAN_INFO_INT_TIME:
  572. + ms = val * 1000000 + val2;
  573. + return cm3218x_write_als_it(chip, (int)ms);
  574. + }
  575. +
  576. + return -EINVAL;
  577. +}
  578. +
  579. +/**
  580. + * cm3218x_get_it_available() - Get available ALS IT value
  581. + * @dev: pointer of struct device.
  582. + * @attr: pointer of struct device_attribute.
  583. + * @buf: pointer of return string buffer.
  584. + *
  585. + * Display the available integration time in milliseconds.
  586. + *
  587. + * Return: string length.
  588. + */
  589. +static ssize_t cm3218x_get_it_available(struct device *dev,
  590. + struct device_attribute *attr, char *buf)
  591. +{
  592. + struct cm3218x_chip *chip = iio_priv(dev_to_iio_dev(dev));
  593. + struct cm3218x_als_info *als_info = chip->als_info;
  594. + int i, len;
  595. +
  596. + for (i = 0, len = 0; i < als_info->num_als_it; i++)
  597. + len += scnprintf(buf + len, PAGE_SIZE - len, "%u.%06u ",
  598. + als_info->als_it_values[i]/1000000,
  599. + als_info->als_it_values[i]%1000000);
  600. + return len + scnprintf(buf + len, PAGE_SIZE - len, "\n");
  601. +}
  602. +
  603. +/**
  604. + * cm3218x_threshold_update() - Update the threshold registers.
  605. + * @chip: pointer of struct cm3218x_chip.
  606. + * @percent: +/- percent.
  607. + *
  608. + * Based on the current ALS value, update the hi and low threshold registers.
  609. + *
  610. + * Return: 0 for success; otherwise for error code.
  611. + */
  612. +static int cm3218x_threshold_update(struct cm3218x_chip *chip, int percent)
  613. +{
  614. + struct i2c_client *client = chip->client;
  615. + struct cm3218x_als_info *als_info = chip->als_info;
  616. + int ret;
  617. + int wh, wl;
  618. +
  619. + ret = als_info->als_raw = i2c_smbus_read_word_data(client,
  620. + CM3218X_REG_ADDR_ALS);
  621. + if (ret < 0)
  622. + return ret;
  623. +
  624. + wh = wl = ret;
  625. + ret *= percent;
  626. + ret /= 100;
  627. + if (ret < 1)
  628. + ret = 1;
  629. + wh += ret;
  630. + wl -= ret;
  631. + if (wh > 65535)
  632. + wh = 65535;
  633. + if (wl < 0)
  634. + wl = 0;
  635. +
  636. + chip->conf_regs[CM3218X_REG_ADDR_WH] = wh;
  637. + ret = i2c_smbus_write_word_data(
  638. + client,
  639. + CM3218X_REG_ADDR_WH,
  640. + chip->conf_regs[CM3218X_REG_ADDR_WH]);
  641. + if (ret < 0)
  642. + return ret;
  643. +
  644. + chip->conf_regs[CM3218X_REG_ADDR_WL] = wl;
  645. + ret = i2c_smbus_write_word_data(
  646. + client,
  647. + CM3218X_REG_ADDR_WL,
  648. + chip->conf_regs[CM3218X_REG_ADDR_WL]);
  649. +
  650. + return ret;
  651. +}
  652. +
  653. +/**
  654. + * cm3218x_event_handler() - Interrupt handling routine.
  655. + * @irq: irq number.
  656. + * @private: pointer of void.
  657. + *
  658. + * Clean interrupt and reset threshold registers.
  659. + *
  660. + * Return: IRQ_HANDLED.
  661. + */
  662. +static irqreturn_t cm3218x_event_handler(int irq, void *private)
  663. +{
  664. + struct iio_dev *dev_info = private;
  665. + struct cm3218x_chip *chip = iio_priv(dev_info);
  666. + int ret;
  667. +
  668. + mutex_lock(&chip->lock);
  669. +
  670. + /* Disable interrupt */
  671. + ret = cm3218x_interrupt_config(chip, 0);
  672. + if (ret < 0)
  673. + goto error_handler_unlock;
  674. +
  675. + /* Update Hi/Lo windows */
  676. + ret = cm3218x_threshold_update(chip, CM3218X_THRESHOLD_PERCENT);
  677. + if (ret < 0)
  678. + goto error_handler_unlock;
  679. +
  680. + /* Enable interrupt */
  681. + cm3218x_interrupt_config(chip, 1);
  682. +
  683. +error_handler_unlock:
  684. + mutex_unlock(&chip->lock);
  685. + return IRQ_HANDLED;
  686. +}
  687. +
  688. +static const struct iio_chan_spec cm3218x_channels[] = {
  689. + {
  690. + .type = IIO_LIGHT,
  691. + .info_mask_separate =
  692. + BIT(IIO_CHAN_INFO_PROCESSED) |
  693. + BIT(IIO_CHAN_INFO_CALIBSCALE) |
  694. + BIT(IIO_CHAN_INFO_INT_TIME),
  695. + }
  696. +};
  697. +
  698. +static IIO_DEVICE_ATTR(in_illuminance_integration_time_available,
  699. + S_IRUGO, cm3218x_get_it_available, NULL, 0);
  700. +
  701. +static struct attribute *cm3218x_attributes[] = {
  702. + &iio_dev_attr_in_illuminance_integration_time_available.dev_attr.attr,
  703. + NULL,
  704. +};
  705. +
  706. +static const struct attribute_group cm3218x_attribute_group = {
  707. + .attrs = cm3218x_attributes
  708. +};
  709. +
  710. +static const struct iio_info cm3218x_info = {
  711. + .driver_module = THIS_MODULE,
  712. + .read_raw = &cm3218x_read_raw,
  713. + .write_raw = &cm3218x_write_raw,
  714. + .attrs = &cm3218x_attribute_group,
  715. +};
  716. +
  717. +static int cm3218x_probe(struct i2c_client *client,
  718. + const struct i2c_device_id *id)
  719. +{
  720. + struct cm3218x_chip *chip;
  721. + struct iio_dev *indio_dev;
  722. + int ret;
  723. +
  724. + /* smbus magic? */
  725. + if (client->addr == 0x0c)
  726. + client->addr = 0x48;
  727. +
  728. + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
  729. + if (!indio_dev) {
  730. + dev_err(&client->dev, "devm_iio_device_alloc failed\n");
  731. + return -ENOMEM;
  732. + }
  733. +
  734. + chip = iio_priv(indio_dev);
  735. + i2c_set_clientdata(client, indio_dev);
  736. + chip->client = client;
  737. +
  738. + mutex_init(&chip->lock);
  739. + indio_dev->dev.parent = &client->dev;
  740. + indio_dev->channels = cm3218x_channels;
  741. + indio_dev->num_channels = ARRAY_SIZE(cm3218x_channels);
  742. + indio_dev->info = &cm3218x_info;
  743. + if (id && id->name)
  744. + indio_dev->name = id->name;
  745. + else
  746. + indio_dev->name = (char *)dev_name(&client->dev);
  747. + indio_dev->modes = INDIO_DIRECT_MODE;
  748. +
  749. + ret = cm3218x_reg_init(chip);
  750. + if (ret) {
  751. + dev_err(&client->dev,
  752. + "%s: register init failed\n",
  753. + __func__);
  754. + return ret;
  755. + }
  756. +
  757. + if (client->irq) {
  758. + ret = request_threaded_irq(client->irq,
  759. + NULL,
  760. + cm3218x_event_handler,
  761. + IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
  762. + "cm3218x_event",
  763. + indio_dev);
  764. +
  765. + if (ret < 0) {
  766. + dev_err(&client->dev, "irq request error %d\n",
  767. + -ret);
  768. + goto error_disable_int;
  769. + }
  770. + }
  771. +
  772. + ret = iio_device_register(indio_dev);
  773. + if (ret < 0) {
  774. + dev_err(&client->dev,
  775. + "%s: regist device failed\n",
  776. + __func__);
  777. + goto error_free_irq;
  778. + }
  779. +
  780. + if (client->irq) {
  781. + ret = cm3218x_threshold_update(chip, CM3218X_THRESHOLD_PERCENT);
  782. + if (ret < 0)
  783. + goto error_free_irq;
  784. +
  785. + ret = cm3218x_interrupt_config(chip, 1);
  786. + if (ret < 0)
  787. + goto error_free_irq;
  788. + }
  789. +
  790. + return 0;
  791. +
  792. +error_free_irq:
  793. + if (client->irq)
  794. + free_irq(client->irq, indio_dev);
  795. +error_disable_int:
  796. + cm3218x_interrupt_config(chip, 0);
  797. + return ret;
  798. +}
  799. +
  800. +static int cm3218x_remove(struct i2c_client *client)
  801. +{
  802. + struct iio_dev *indio_dev = i2c_get_clientdata(client);
  803. + struct cm3218x_chip *chip = iio_priv(indio_dev);
  804. +
  805. + cm3218x_interrupt_config(chip, 0);
  806. + if (client->irq)
  807. + free_irq(client->irq, indio_dev);
  808. + iio_device_unregister(indio_dev);
  809. + return 0;
  810. +}
  811. +
  812. +static const struct i2c_device_id cm3218x_id[] = {
  813. + { "cm3218", cm3218},
  814. + { "cm32181", cm32181},
  815. + { "cm32182", cm32182},
  816. + { }
  817. +};
  818. +
  819. +MODULE_DEVICE_TABLE(i2c, cm3218x_id);
  820. +
  821. +static const struct of_device_id cm3218x_of_match[] = {
  822. + { .compatible = "capella,cm3218" },
  823. + { }
  824. +};
  825. +
  826. +#ifdef CONFIG_ACPI
  827. +static const struct acpi_device_id cm3218x_acpi_match[] = {
  828. + { "CPLM3218", 0},
  829. + {},
  830. +};
  831. +
  832. +MODULE_DEVICE_TABLE(acpi, cm3218x_acpi_match);
  833. +#endif /* CONFIG_ACPI */
  834. +
  835. +static struct i2c_driver cm3218x_driver = {
  836. + .driver = {
  837. + .name = "cm3218x",
  838. +#ifdef CONFIG_ACPI
  839. + .acpi_match_table = ACPI_PTR(cm3218x_acpi_match),
  840. +#endif /* CONFIG_ACPI */
  841. + .of_match_table = of_match_ptr(cm3218x_of_match),
  842. + .owner = THIS_MODULE,
  843. + },
  844. + .id_table = cm3218x_id,
  845. + .probe = cm3218x_probe,
  846. + .remove = cm3218x_remove,
  847. +};
  848. +
  849. +module_i2c_driver(cm3218x_driver);
  850. +
  851. +MODULE_AUTHOR("Kevin Tsai <ktsai@capellamicro.com>");
  852. +MODULE_DESCRIPTION("CM3218X ambient light sensor driver");
  853. +MODULE_LICENSE("GPL");
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement