daily pastebin goal
81%
SHARE
TWEET

Untitled

a guest Dec 28th, 2017 68 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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");
RAW Paste Data
Top