diff mbox series

[3/3] iio: proximity: Add support for everlight pmd16d17 sensor

Message ID abb0c1c0724be733138276f638e43e98784bd191.1723527641.git.jan.kiszka@siemens.com (mailing list archive)
State Changes Requested
Headers show
Series iio: Add Everlight PM16D17 proximity sensor | expand

Commit Message

Jan Kiszka Aug. 13, 2024, 5:40 a.m. UTC
From: Chao Zeng <chao.zeng@siemens.com>

Add initial support for everlight pm16d17 proximity sensor.

Signed-off-by: Chao Zeng <chao.zeng@siemens.com>
Co-developed-by: Baocheng Su <baocheng.su@siemens.com>
Signed-off-by: Baocheng Su <baocheng.su@siemens.com>
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
---
 drivers/iio/proximity/Kconfig   |  11 ++
 drivers/iio/proximity/Makefile  |   1 +
 drivers/iio/proximity/pm16d17.c | 324 ++++++++++++++++++++++++++++++++
 3 files changed, 336 insertions(+)
 create mode 100644 drivers/iio/proximity/pm16d17.c

Comments

kernel test robot Aug. 15, 2024, 5:51 a.m. UTC | #1
Hi Jan,

kernel test robot noticed the following build warnings:

[auto build test WARNING on jic23-iio/togreg]
[also build test WARNING on robh/for-next linus/master v6.11-rc3 next-20240814]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Jan-Kiszka/dt-bindings-vendor-prefixes-Add-EVERLIGHT/20240814-234801
base:   https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git togreg
patch link:    https://lore.kernel.org/r/abb0c1c0724be733138276f638e43e98784bd191.1723527641.git.jan.kiszka%40siemens.com
patch subject: [PATCH 3/3] iio: proximity: Add support for everlight pmd16d17 sensor
config: x86_64-allyesconfig (https://download.01.org/0day-ci/archive/20240815/202408151352.u8v6HOIQ-lkp@intel.com/config)
compiler: clang version 18.1.5 (https://github.com/llvm/llvm-project 617a15a9eac96088ae5e9134248d8236e34b91b1)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240815/202408151352.u8v6HOIQ-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202408151352.u8v6HOIQ-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/iio/proximity/pm16d17.c:142:2: warning: unannotated fall-through between switch labels [-Wimplicit-fallthrough]
     142 |         default:
         |         ^
   drivers/iio/proximity/pm16d17.c:142:2: note: insert 'break;' to avoid fall-through
     142 |         default:
         |         ^
         |         break; 
>> drivers/iio/proximity/pm16d17.c:187:3: warning: variable 'op_mode_setting_val' is uninitialized when used here [-Wuninitialized]
     187 |                 op_mode_setting_val |= (ilog2(pgain) << 6) & PS_GAIN_MASK;
         |                 ^~~~~~~~~~~~~~~~~~~
   drivers/iio/proximity/pm16d17.c:159:29: note: initialize the variable 'op_mode_setting_val' to silence this warning
     159 |         uint8_t op_mode_setting_val;
         |                                    ^
         |                                     = '\0'
   2 warnings generated.


vim +142 drivers/iio/proximity/pm16d17.c

   109	
   110	static int pm16d17_read_raw(struct iio_dev *indio_dev,
   111				    struct iio_chan_spec const *chan,
   112				    int *val, int *val2, long mask)
   113	{
   114		struct pm16d17_data *data = iio_priv(indio_dev);
   115		unsigned int ps_data_l;
   116		unsigned int ps_data_h;
   117		uint16_t ps_data;
   118		int ret = -EINVAL;
   119	
   120		switch (mask) {
   121		case IIO_CHAN_INFO_RAW:
   122			switch (chan->type) {
   123			case IIO_PROXIMITY:
   124				ret = pm16d17_read_reg(data, PM16D17_PS_DATA_L, &ps_data_l);
   125				if (ret < 0)
   126					return ret;
   127	
   128				ret = pm16d17_read_reg(data, PM16D17_PS_DATA_H, &ps_data_h);
   129				if (ret < 0)
   130					return ret;
   131	
   132				ps_data = (ps_data_h << 8) | ps_data_l;
   133	
   134				dev_dbg(&data->client->dev, "PS data: %x\n", ps_data);
   135	
   136				*val = ps_data;
   137				ret = IIO_VAL_INT;
   138				break;
   139			default:
   140				break;
   141			}
 > 142		default:
   143			break;
   144		}
   145	
   146		return ret;
   147	}
   148	
   149	static const struct iio_info pm16d17_info = {
   150		.read_raw = pm16d17_read_raw,
   151	};
   152	
   153	static int pm16d17_chip_init(struct pm16d17_data *data)
   154	{
   155		struct i2c_client *client = data->client;
   156		struct device_node *np = client->dev.of_node;
   157		const char *conv_time = NULL;
   158		const char *wait_time = NULL;
   159		uint8_t op_mode_setting_val;
   160		uint32_t ps_offset_cancel;
   161		uint8_t offset_lsb;
   162		uint8_t offset_msb;
   163		uint32_t pulse_count;
   164		uint32_t pgain;
   165		unsigned int val;
   166		int ret;
   167	
   168		ret = pm16d17_read_reg(data, PM16D17_DEV_ID, &val);
   169	
   170		if (ret < 0 || (val != DEVICE_ID)) {
   171			dev_err(&client->dev, "Invalid chip id 0x%04x\n", val);
   172			return -ENODEV;
   173		}
   174	
   175		dev_dbg(&client->dev, "Detected PM16D17 with chip id: 0x%04x\n", val);
   176	
   177		ret = pm16d17_write_reg(data, PM16D17_OP_MODE, ENABLE_PS_FUNCTION);
   178		if (ret < 0)
   179			return ret;
   180	
   181		of_property_read_u32(np, "ps-gain", &pgain);
   182		switch (pgain) {
   183		case 1:
   184		case 2:
   185		case 4:
   186		case 8:
 > 187			op_mode_setting_val |= (ilog2(pgain) << 6) & PS_GAIN_MASK;
   188			break;
   189		default:
   190			break;
   191		}
   192	
   193		of_property_read_string(np, "ps-itime", &conv_time);
   194		if (strcmp(conv_time, "0.4") == 0)
   195			op_mode_setting_val |= PITIME_0_POINT_4_MS & PS_ITIME_MASK;
   196		else if (strcmp(conv_time, "0.8") == 0)
   197			op_mode_setting_val |= PITIME_0_POINT_8_MS & PS_ITIME_MASK;
   198		else if (strcmp(conv_time, "1.6") == 0)
   199			op_mode_setting_val |= PITIME_1_POINT_6_MS & PS_ITIME_MASK;
   200		else if (strcmp(conv_time, "3.2") == 0)
   201			op_mode_setting_val |= PITIME_3_POINT_2_MS & PS_ITIME_MASK;
   202		else if (strcmp(conv_time, "6.3") == 0)
   203			op_mode_setting_val |= PITIME_6_POINT_3_MS & PS_ITIME_MASK;
   204		else if (strcmp(conv_time, "12.6") == 0)
   205			op_mode_setting_val |= PITIME_12_POINT_6_MS & PS_ITIME_MASK;
   206		else if (strcmp(conv_time, "25.2") == 0)
   207			op_mode_setting_val |= PITIME_25_POINT_2_MS & PS_ITIME_MASK;
   208		else {
   209			dev_info(&client->dev, "Using default ps itime value\n");
   210			op_mode_setting_val |= PITIME_0_POINT_4_MS & PS_ITIME_MASK;
   211		}
   212	
   213		of_property_read_string(np, "ps-wtime", &wait_time);
   214		if (strcmp(wait_time, "12.5") == 0)
   215			op_mode_setting_val |= PWTIME_12_POINT_5_MS & PS_WTIME_MASK;
   216		else if (strcmp(wait_time, "25") == 0)
   217			op_mode_setting_val |= PWTIME_25_MS & PS_WTIME_MASK;
   218		else if (strcmp(wait_time, "50") == 0)
   219			op_mode_setting_val |= PWTIME_50_MS & PS_WTIME_MASK;
   220		else if (strcmp(wait_time, "100") == 0)
   221			op_mode_setting_val |= PWTIME_100_MS & PS_WTIME_MASK;
   222		else if (strcmp(wait_time, "200") == 0)
   223			op_mode_setting_val |= PWTIME_200_MS & PS_WTIME_MASK;
   224		else if (strcmp(wait_time, "400") == 0)
   225			op_mode_setting_val |= PWTIME_400_MS & PS_WTIME_MASK;
   226		else if (strcmp(wait_time, "800") == 0)
   227			op_mode_setting_val |= PWTIME_800_MS & PS_WTIME_MASK;
   228		else if (strcmp(wait_time, "1600") == 0)
   229			op_mode_setting_val |= PWTIME_1600_MS & PS_WTIME_MASK;
   230		else {
   231			dev_info(&client->dev, "Using default ps wtime value\n");
   232			op_mode_setting_val |= PWTIME_12_POINT_5_MS & PS_WTIME_MASK;
   233		}
   234	
   235		ret = pm16d17_write_reg(data, PM16D17_PS_SETTING, op_mode_setting_val);
   236		if (ret < 0)
   237			return ret;
   238	
   239		of_property_read_u32(np, "ps-ir-led-pulse-count", &pulse_count);
   240		if (pulse_count > 256)
   241			pulse_count = 256;
   242		ret = pm16d17_write_reg(data, PM16D17_VCSEL_DRIVE_PULSE, pulse_count - 1);
   243		if (ret < 0)
   244			return ret;
   245	
   246		of_property_read_u32(np, "ps-offset-cancel", &ps_offset_cancel);
   247		if (ps_offset_cancel != 0) {
   248			ret = pm16d17_write_reg(data, PM16D17_PS_SETTING2, OFFSET_CANCEL_ENABLE);
   249			if (ret < 0)
   250				return ret;
   251	
   252			offset_lsb = ps_offset_cancel & PS_OFFSET_CANCEL_LSB_MASK;
   253			offset_msb = (ps_offset_cancel & PS_OFFSET_CANCEL_MSB_MASK) >> 8;
   254	
   255			ret = pm16d17_write_reg(data, PM16D17_PS_OFFSET_CANCEL_L, offset_lsb);
   256			if (ret < 0)
   257				return ret;
   258	
   259			ret = pm16d17_write_reg(data, PM16D17_PS_OFFSET_CANCEL_H, offset_msb);
   260			if (ret < 0)
   261				return ret;
   262		}
   263	
   264		return 0;
   265	}
   266
kernel test robot Aug. 15, 2024, 9:54 p.m. UTC | #2
Hi Jan,

kernel test robot noticed the following build warnings:

[auto build test WARNING on jic23-iio/togreg]
[also build test WARNING on robh/for-next linus/master v6.11-rc3 next-20240815]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Jan-Kiszka/dt-bindings-vendor-prefixes-Add-EVERLIGHT/20240814-234801
base:   https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git togreg
patch link:    https://lore.kernel.org/r/abb0c1c0724be733138276f638e43e98784bd191.1723527641.git.jan.kiszka%40siemens.com
patch subject: [PATCH 3/3] iio: proximity: Add support for everlight pmd16d17 sensor
config: x86_64-randconfig-101-20240816 (https://download.01.org/0day-ci/archive/20240816/202408160542.UEjIkjkC-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240816/202408160542.UEjIkjkC-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202408160542.UEjIkjkC-lkp@intel.com/

All warnings (new ones prefixed by >>):

   drivers/iio/proximity/pm16d17.c: In function 'pm16d17_chip_init':
>> drivers/iio/proximity/pm16d17.c:194:13: warning: argument 1 null where non-null expected [-Wnonnull]
     194 |         if (strcmp(conv_time, "0.4") == 0)
         |             ^~~~~~~~~~~~~~~~~~~~~~~~
   In file included from include/linux/bitmap.h:13,
                    from include/linux/cpumask.h:12,
                    from arch/x86/include/asm/tlbbatch.h:5,
                    from include/linux/mm_types_task.h:16,
                    from include/linux/sched.h:38,
                    from include/linux/delay.h:23,
                    from drivers/iio/proximity/pm16d17.c:11:
   include/linux/string.h:156:12: note: in a call to function 'strcmp' declared 'nonnull'
     156 | extern int strcmp(const char *,const char *);
         |            ^~~~~~
   drivers/iio/proximity/pm16d17.c:196:18: warning: argument 1 null where non-null expected [-Wnonnull]
     196 |         else if (strcmp(conv_time, "0.8") == 0)
         |                  ^~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/string.h:156:12: note: in a call to function 'strcmp' declared 'nonnull'
     156 | extern int strcmp(const char *,const char *);
         |            ^~~~~~
   drivers/iio/proximity/pm16d17.c:198:18: warning: argument 1 null where non-null expected [-Wnonnull]
     198 |         else if (strcmp(conv_time, "1.6") == 0)
         |                  ^~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/string.h:156:12: note: in a call to function 'strcmp' declared 'nonnull'
     156 | extern int strcmp(const char *,const char *);
         |            ^~~~~~
   drivers/iio/proximity/pm16d17.c:200:18: warning: argument 1 null where non-null expected [-Wnonnull]
     200 |         else if (strcmp(conv_time, "3.2") == 0)
         |                  ^~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/string.h:156:12: note: in a call to function 'strcmp' declared 'nonnull'
     156 | extern int strcmp(const char *,const char *);
         |            ^~~~~~
   drivers/iio/proximity/pm16d17.c:202:18: warning: argument 1 null where non-null expected [-Wnonnull]
     202 |         else if (strcmp(conv_time, "6.3") == 0)
         |                  ^~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/string.h:156:12: note: in a call to function 'strcmp' declared 'nonnull'
     156 | extern int strcmp(const char *,const char *);
         |            ^~~~~~
   drivers/iio/proximity/pm16d17.c:204:18: warning: argument 1 null where non-null expected [-Wnonnull]
     204 |         else if (strcmp(conv_time, "12.6") == 0)
         |                  ^~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/string.h:156:12: note: in a call to function 'strcmp' declared 'nonnull'
     156 | extern int strcmp(const char *,const char *);
         |            ^~~~~~
   drivers/iio/proximity/pm16d17.c:206:18: warning: argument 1 null where non-null expected [-Wnonnull]
     206 |         else if (strcmp(conv_time, "25.2") == 0)
         |                  ^~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/string.h:156:12: note: in a call to function 'strcmp' declared 'nonnull'
     156 | extern int strcmp(const char *,const char *);
         |            ^~~~~~
   drivers/iio/proximity/pm16d17.c:214:13: warning: argument 1 null where non-null expected [-Wnonnull]
     214 |         if (strcmp(wait_time, "12.5") == 0)
         |             ^~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/string.h:156:12: note: in a call to function 'strcmp' declared 'nonnull'
     156 | extern int strcmp(const char *,const char *);
         |            ^~~~~~
   drivers/iio/proximity/pm16d17.c:216:18: warning: argument 1 null where non-null expected [-Wnonnull]
     216 |         else if (strcmp(wait_time, "25") == 0)
         |                  ^~~~~~~~~~~~~~~~~~~~~~~
   include/linux/string.h:156:12: note: in a call to function 'strcmp' declared 'nonnull'
     156 | extern int strcmp(const char *,const char *);
         |            ^~~~~~
   drivers/iio/proximity/pm16d17.c:218:18: warning: argument 1 null where non-null expected [-Wnonnull]
     218 |         else if (strcmp(wait_time, "50") == 0)
         |                  ^~~~~~~~~~~~~~~~~~~~~~~
   include/linux/string.h:156:12: note: in a call to function 'strcmp' declared 'nonnull'
     156 | extern int strcmp(const char *,const char *);
         |            ^~~~~~
   drivers/iio/proximity/pm16d17.c:220:18: warning: argument 1 null where non-null expected [-Wnonnull]
     220 |         else if (strcmp(wait_time, "100") == 0)
         |                  ^~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/string.h:156:12: note: in a call to function 'strcmp' declared 'nonnull'
     156 | extern int strcmp(const char *,const char *);
         |            ^~~~~~
   drivers/iio/proximity/pm16d17.c:222:18: warning: argument 1 null where non-null expected [-Wnonnull]
     222 |         else if (strcmp(wait_time, "200") == 0)
         |                  ^~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/string.h:156:12: note: in a call to function 'strcmp' declared 'nonnull'
     156 | extern int strcmp(const char *,const char *);
         |            ^~~~~~
   drivers/iio/proximity/pm16d17.c:224:18: warning: argument 1 null where non-null expected [-Wnonnull]
     224 |         else if (strcmp(wait_time, "400") == 0)
         |                  ^~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/string.h:156:12: note: in a call to function 'strcmp' declared 'nonnull'
     156 | extern int strcmp(const char *,const char *);
         |            ^~~~~~
   drivers/iio/proximity/pm16d17.c:226:18: warning: argument 1 null where non-null expected [-Wnonnull]
     226 |         else if (strcmp(wait_time, "800") == 0)
         |                  ^~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/string.h:156:12: note: in a call to function 'strcmp' declared 'nonnull'
     156 | extern int strcmp(const char *,const char *);
         |            ^~~~~~
   drivers/iio/proximity/pm16d17.c:228:18: warning: argument 1 null where non-null expected [-Wnonnull]
     228 |         else if (strcmp(wait_time, "1600") == 0)
         |                  ^~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/string.h:156:12: note: in a call to function 'strcmp' declared 'nonnull'
     156 | extern int strcmp(const char *,const char *);
         |            ^~~~~~


vim +194 drivers/iio/proximity/pm16d17.c

   152	
   153	static int pm16d17_chip_init(struct pm16d17_data *data)
   154	{
   155		struct i2c_client *client = data->client;
   156		struct device_node *np = client->dev.of_node;
   157		const char *conv_time = NULL;
   158		const char *wait_time = NULL;
   159		uint8_t op_mode_setting_val;
   160		uint32_t ps_offset_cancel;
   161		uint8_t offset_lsb;
   162		uint8_t offset_msb;
   163		uint32_t pulse_count;
   164		uint32_t pgain;
   165		unsigned int val;
   166		int ret;
   167	
   168		ret = pm16d17_read_reg(data, PM16D17_DEV_ID, &val);
   169	
   170		if (ret < 0 || (val != DEVICE_ID)) {
   171			dev_err(&client->dev, "Invalid chip id 0x%04x\n", val);
   172			return -ENODEV;
   173		}
   174	
   175		dev_dbg(&client->dev, "Detected PM16D17 with chip id: 0x%04x\n", val);
   176	
   177		ret = pm16d17_write_reg(data, PM16D17_OP_MODE, ENABLE_PS_FUNCTION);
   178		if (ret < 0)
   179			return ret;
   180	
   181		of_property_read_u32(np, "ps-gain", &pgain);
   182		switch (pgain) {
   183		case 1:
   184		case 2:
   185		case 4:
   186		case 8:
   187			op_mode_setting_val |= (ilog2(pgain) << 6) & PS_GAIN_MASK;
   188			break;
   189		default:
   190			break;
   191		}
   192	
   193		of_property_read_string(np, "ps-itime", &conv_time);
 > 194		if (strcmp(conv_time, "0.4") == 0)
   195			op_mode_setting_val |= PITIME_0_POINT_4_MS & PS_ITIME_MASK;
   196		else if (strcmp(conv_time, "0.8") == 0)
   197			op_mode_setting_val |= PITIME_0_POINT_8_MS & PS_ITIME_MASK;
   198		else if (strcmp(conv_time, "1.6") == 0)
   199			op_mode_setting_val |= PITIME_1_POINT_6_MS & PS_ITIME_MASK;
   200		else if (strcmp(conv_time, "3.2") == 0)
   201			op_mode_setting_val |= PITIME_3_POINT_2_MS & PS_ITIME_MASK;
   202		else if (strcmp(conv_time, "6.3") == 0)
   203			op_mode_setting_val |= PITIME_6_POINT_3_MS & PS_ITIME_MASK;
   204		else if (strcmp(conv_time, "12.6") == 0)
   205			op_mode_setting_val |= PITIME_12_POINT_6_MS & PS_ITIME_MASK;
   206		else if (strcmp(conv_time, "25.2") == 0)
   207			op_mode_setting_val |= PITIME_25_POINT_2_MS & PS_ITIME_MASK;
   208		else {
   209			dev_info(&client->dev, "Using default ps itime value\n");
   210			op_mode_setting_val |= PITIME_0_POINT_4_MS & PS_ITIME_MASK;
   211		}
   212	
   213		of_property_read_string(np, "ps-wtime", &wait_time);
   214		if (strcmp(wait_time, "12.5") == 0)
   215			op_mode_setting_val |= PWTIME_12_POINT_5_MS & PS_WTIME_MASK;
   216		else if (strcmp(wait_time, "25") == 0)
   217			op_mode_setting_val |= PWTIME_25_MS & PS_WTIME_MASK;
   218		else if (strcmp(wait_time, "50") == 0)
   219			op_mode_setting_val |= PWTIME_50_MS & PS_WTIME_MASK;
   220		else if (strcmp(wait_time, "100") == 0)
   221			op_mode_setting_val |= PWTIME_100_MS & PS_WTIME_MASK;
   222		else if (strcmp(wait_time, "200") == 0)
   223			op_mode_setting_val |= PWTIME_200_MS & PS_WTIME_MASK;
   224		else if (strcmp(wait_time, "400") == 0)
   225			op_mode_setting_val |= PWTIME_400_MS & PS_WTIME_MASK;
   226		else if (strcmp(wait_time, "800") == 0)
   227			op_mode_setting_val |= PWTIME_800_MS & PS_WTIME_MASK;
   228		else if (strcmp(wait_time, "1600") == 0)
   229			op_mode_setting_val |= PWTIME_1600_MS & PS_WTIME_MASK;
   230		else {
   231			dev_info(&client->dev, "Using default ps wtime value\n");
   232			op_mode_setting_val |= PWTIME_12_POINT_5_MS & PS_WTIME_MASK;
   233		}
   234	
   235		ret = pm16d17_write_reg(data, PM16D17_PS_SETTING, op_mode_setting_val);
   236		if (ret < 0)
   237			return ret;
   238	
   239		of_property_read_u32(np, "ps-ir-led-pulse-count", &pulse_count);
   240		if (pulse_count > 256)
   241			pulse_count = 256;
   242		ret = pm16d17_write_reg(data, PM16D17_VCSEL_DRIVE_PULSE, pulse_count - 1);
   243		if (ret < 0)
   244			return ret;
   245	
   246		of_property_read_u32(np, "ps-offset-cancel", &ps_offset_cancel);
   247		if (ps_offset_cancel != 0) {
   248			ret = pm16d17_write_reg(data, PM16D17_PS_SETTING2, OFFSET_CANCEL_ENABLE);
   249			if (ret < 0)
   250				return ret;
   251	
   252			offset_lsb = ps_offset_cancel & PS_OFFSET_CANCEL_LSB_MASK;
   253			offset_msb = (ps_offset_cancel & PS_OFFSET_CANCEL_MSB_MASK) >> 8;
   254	
   255			ret = pm16d17_write_reg(data, PM16D17_PS_OFFSET_CANCEL_L, offset_lsb);
   256			if (ret < 0)
   257				return ret;
   258	
   259			ret = pm16d17_write_reg(data, PM16D17_PS_OFFSET_CANCEL_H, offset_msb);
   260			if (ret < 0)
   261				return ret;
   262		}
   263	
   264		return 0;
   265	}
   266
Jonathan Cameron Aug. 17, 2024, 2:02 p.m. UTC | #3
On Tue, 13 Aug 2024 07:40:42 +0200
Jan Kiszka <jan.kiszka@siemens.com> wrote:

> From: Chao Zeng <chao.zeng@siemens.com>
> 
> Add initial support for everlight pm16d17 proximity sensor.
> 
> Signed-off-by: Chao Zeng <chao.zeng@siemens.com>
> Co-developed-by: Baocheng Su <baocheng.su@siemens.com>
> Signed-off-by: Baocheng Su <baocheng.su@siemens.com>
> Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>

Hi Jan,

Various comments inline. 

Thanks,

Jonathan

> diff --git a/drivers/iio/proximity/pm16d17.c b/drivers/iio/proximity/pm16d17.c
> new file mode 100644
> index 000000000000..94f21fc5e2fb
> --- /dev/null
> +++ b/drivers/iio/proximity/pm16d17.c
> @@ -0,0 +1,324 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) Siemens AG, 2023-2024
> + *
> + * Driver for Everlight PM-16d17 proximity sensor
> + *
> + * Author: Chao Zeng <chao.zeng@siemens.com>
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/i2c.h>
> +#include <linux/init.h>
Unlikely you need this one...

> +#include <linux/interrupt.h>
Not in use yet?
> +#include <linux/irq.h>
> +#include <linux/module.h>

mod_devicetable.h
property.h

Also check these again as you should as currently written have had
of.h and don't, so may be other things missing.
> +#include <linux/regmap.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
Only needed if you are doing custom attrs. So drop.
> +#include <linux/iio/events.h>

Only needed for events. So drop for now.

> +
> +#define PM16D17_DRV_NAME		"pm16d17"
> +#define PM16D17_REGMAP_NAME		"pm16d17_regmap"
Don't have defines for these two. See later for more comments on that.
> +
> +/* Registers Address */
To keep clear distinction between fields and registers I'd add
_REG to end of these.

> +#define PM16D17_OP_MODE			0x00
> +#define PM16D17_INTERRUPT_FLAG		0x01
> +#define PM16D17_PS_SETTING		0x0A
> +#define PM16D17_VCSEL_DRIVE_CURRENT	0x0B
> +#define PM16D17_VCSEL_DRIVE_PULSE	0x0C
> +#define PM16D17_PS_INTUPT_LTHD_L	0x0D
> +#define PM16D17_PS_INTUPT_LTHD_H	0x0E
> +#define PM16D17_PS_INTUPT_HTHD_L	0x0F
> +#define PM16D17_PS_INTUPT_HTHD_H	0x10
> +#define PM16D17_PS_DATA_L		0x11
> +#define PM16D17_PS_DATA_H		0x12
> +#define PM16D17_PS_SETTING2		0x13
> +#define PM16D17_PS_OFFSET_CANCEL_L	0x14
> +#define PM16D17_PS_OFFSET_CANCEL_H	0x15
> +#define PM16D17_DEV_ID			0x18
> +
> +#define DEVICE_ID			0x11
Needs to be prefixed as DEVICE_ID is a very generic define!
PM16D17_DEV_ID_PM16D17 
or something like that to indicate which register and value means
what.

> +
> +#define ENABLE_PS_FUNCTION		BIT(3)
> +#define PS_GAIN_MASK			GENMASK(7, 6)
> +#define PS_ITIME_MASK			GENMASK(5, 3)
> +#define PS_WTIME_MASK			GENMASK(2, 0)
> +#define OFFSET_CANCEL_ENABLE		BIT(7)
> +#define PS_OFFSET_CANCEL_LSB_MASK	GENMASK(7, 0)
> +#define PS_OFFSET_CANCEL_MSB_MASK	GENMASK(15, 8)
> +
> +enum {
> +	PITIME_0_POINT_4_MS = (0 << 3),
> +	PITIME_0_POINT_8_MS = (1 << 3),
> +	PITIME_1_POINT_6_MS = (2 << 3),
> +	PITIME_3_POINT_2_MS = (3 << 3),
> +	PITIME_6_POINT_3_MS = (4 << 3),
> +	PITIME_12_POINT_6_MS = (5 << 3),
> +	PITIME_25_POINT_2_MS = (6 << 3),
Don't shift these values.

Use a suitable mask and FIELD_PREP() to shift them at the
point where you are writing them.

> +};
> +
> +enum {
> +	PWTIME_12_POINT_5_MS = 0,
> +	PWTIME_25_MS,
> +	PWTIME_50_MS,
> +	PWTIME_100_MS,
> +	PWTIME_200_MS,
> +	PWTIME_400_MS,
> +	PWTIME_800_MS,
> +	PWTIME_1600_MS,
> +};
> +
> +struct pm16d17_data {
> +	struct i2c_client *client;
As below. More useful to store &client->dev

> +	struct regmap *regmap;
> +};
> +
> +static const struct regmap_config pm16d17_regmap_config = {
> +	.name = PM16D17_REGMAP_NAME,
> +	.reg_bits = 8,
> +	.val_bits = 8,
> +	.cache_type = REGCACHE_NONE,
> +};
> +
> +static const struct iio_chan_spec pm16d17_channels[] = {
> +	{
> +		.type = IIO_PROXIMITY,
> +		.indexed = 1,
> +		.channel = 0,
> +		.scan_index = -1,
Don't need to set this unless you are registering the buffered
interfaces. So don't set it for now.

> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
> +	}
> +};
> +
> +static inline int pm16d17_write_reg(struct pm16d17_data *data,
> +				    unsigned int reg,
> +				    unsigned int value)
> +{
> +	return regmap_write(data->regmap, reg, value);

Get rid of these wrappers and call regmap_write / read directly inline.
These just make the code a little harder to read for no benefit.

> +}
> +
> +static inline unsigned int pm16d17_read_reg(struct pm16d17_data *data,
> +					    unsigned int reg,
> +					    unsigned int *reg_val)
> +{
> +	return regmap_read(data->regmap, reg, reg_val);
> +}
> +
> +static int pm16d17_read_raw(struct iio_dev *indio_dev,
> +			    struct iio_chan_spec const *chan,
> +			    int *val, int *val2, long mask)
> +{
> +	struct pm16d17_data *data = iio_priv(indio_dev);
> +	unsigned int ps_data_l;
> +	unsigned int ps_data_h;
> +	uint16_t ps_data;
> +	int ret = -EINVAL;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		switch (chan->type) {
> +		case IIO_PROXIMITY:
> +			ret = pm16d17_read_reg(data, PM16D17_PS_DATA_L, &ps_data_l);
> +			if (ret < 0)
> +				return ret;
> +
> +			ret = pm16d17_read_reg(data, PM16D17_PS_DATA_H, &ps_data_h);
> +			if (ret < 0)
> +				return ret;
> +
> +			ps_data = (ps_data_h << 8) | ps_data_l;

read both values into a u8 [2] and use get_unaligned_be16() or similar.

Actually you should be able to do a bulk read (which is obvious once you
stop wrapping regmap calls with function s that suggest something more is going on).
> +
> +			dev_dbg(&data->client->dev, "PS data: %x\n", ps_data);
> +
> +			*val = ps_data;
> +			ret = IIO_VAL_INT;
> +			break;
			return IIO_VAL_INT;
> +		default:
> +			break;
return an error
> +		}
> +	default:
> +		break;
return an error.
> +	}
> +
Should not be able to get here so drop this (with above changes to return early)
> +	return ret;
> +}
> +
> +static const struct iio_info pm16d17_info = {
> +	.read_raw = pm16d17_read_raw,
> +};
> +
> +static int pm16d17_chip_init(struct pm16d17_data *data)
> +{
> +	struct i2c_client *client = data->client;
> +	struct device_node *np = client->dev.of_node;
> +	const char *conv_time = NULL;
> +	const char *wait_time = NULL;
> +	uint8_t op_mode_setting_val;
> +	uint32_t ps_offset_cancel;
> +	uint8_t offset_lsb;
> +	uint8_t offset_msb;
> +	uint32_t pulse_count;
> +	uint32_t pgain;
> +	unsigned int val;
> +	int ret;
> +
> +	ret = pm16d17_read_reg(data, PM16D17_DEV_ID, &val);
> +
No blank line between a call and the check of it's error. Good keep them
logically associated.

> +	if (ret < 0 || (val != DEVICE_ID)) {
> +		dev_err(&client->dev, "Invalid chip id 0x%04x\n", val);
if (ret < 0)
	return dev_err_probe()
if (val != DEVICE_ID)
	dev_info(&client->dev, "Unexpected chip id ..." 
and carry on.

The reason for this is to enable use in future of fallback DT compatibles
so if a new device is released that is backwards compatible it can be supported
by existing distro kernels etc without change.

We used to get this wrong in IIO and haven't yet fixed all drivers so you
will see lots of examples similar to what you have here.


> +		return -ENODEV;
> +	}
> +
> +	dev_dbg(&client->dev, "Detected PM16D17 with chip id: 0x%04x\n", val);
> +
> +	ret = pm16d17_write_reg(data, PM16D17_OP_MODE, ENABLE_PS_FUNCTION);
> +	if (ret < 0)
> +		return ret;
> +
> +	of_property_read_u32(np, "ps-gain", &pgain);
> +	switch (pgain) {
> +	case 1:
> +	case 2:
> +	case 4:
> +	case 8:
> +		op_mode_setting_val |= (ilog2(pgain) << 6) & PS_GAIN_MASK;
> +		break;
> +	default:
> +		break;

Not an error?

> +	}
> +
> +	of_property_read_string(np, "ps-itime", &conv_time);
Is strcmp safe for NULL? Seems unlikely.

Make the time in msecs / usecs of appropriate so you can handle it as an integer.

device_property_read_string()
Use the generic firmware stuff in linux/property.h instead of of calls thoughout.
That enables use of things like ACPI bindings via PRP0001 _HID

> +	if (strcmp(conv_time, "0.4") == 0)
> +		op_mode_setting_val |= PITIME_0_POINT_4_MS & PS_ITIME_MASK;
> +	else if (strcmp(conv_time, "0.8") == 0)
> +		op_mode_setting_val |= PITIME_0_POINT_8_MS & PS_ITIME_MASK;
> +	else if (strcmp(conv_time, "1.6") == 0)
> +		op_mode_setting_val |= PITIME_1_POINT_6_MS & PS_ITIME_MASK;
> +	else if (strcmp(conv_time, "3.2") == 0)
> +		op_mode_setting_val |= PITIME_3_POINT_2_MS & PS_ITIME_MASK;
> +	else if (strcmp(conv_time, "6.3") == 0)
> +		op_mode_setting_val |= PITIME_6_POINT_3_MS & PS_ITIME_MASK;
> +	else if (strcmp(conv_time, "12.6") == 0)
> +		op_mode_setting_val |= PITIME_12_POINT_6_MS & PS_ITIME_MASK;
> +	else if (strcmp(conv_time, "25.2") == 0)
> +		op_mode_setting_val |= PITIME_25_POINT_2_MS & PS_ITIME_MASK;
> +	else {
> +		dev_info(&client->dev, "Using default ps itime value\n");

If a property was there and invalid value error out, otherwise just
use default here without printing anything.

> +		op_mode_setting_val |= PITIME_0_POINT_4_MS & PS_ITIME_MASK;
> +	}
> +
> +	of_property_read_string(np, "ps-wtime", &wait_time);
Again, pick units so it's an integer. Other comments as above.

> +	if (strcmp(wait_time, "12.5") == 0)
> +		op_mode_setting_val |= PWTIME_12_POINT_5_MS & PS_WTIME_MASK;
> +	else if (strcmp(wait_time, "25") == 0)
> +		op_mode_setting_val |= PWTIME_25_MS & PS_WTIME_MASK;
> +	else if (strcmp(wait_time, "50") == 0)
> +		op_mode_setting_val |= PWTIME_50_MS & PS_WTIME_MASK;
> +	else if (strcmp(wait_time, "100") == 0)
> +		op_mode_setting_val |= PWTIME_100_MS & PS_WTIME_MASK;
> +	else if (strcmp(wait_time, "200") == 0)
> +		op_mode_setting_val |= PWTIME_200_MS & PS_WTIME_MASK;
> +	else if (strcmp(wait_time, "400") == 0)
> +		op_mode_setting_val |= PWTIME_400_MS & PS_WTIME_MASK;
> +	else if (strcmp(wait_time, "800") == 0)
> +		op_mode_setting_val |= PWTIME_800_MS & PS_WTIME_MASK;
> +	else if (strcmp(wait_time, "1600") == 0)
> +		op_mode_setting_val |= PWTIME_1600_MS & PS_WTIME_MASK;
> +	else {
> +		dev_info(&client->dev, "Using default ps wtime value\n");
> +		op_mode_setting_val |= PWTIME_12_POINT_5_MS & PS_WTIME_MASK;
> +	}
> +
> +	ret = pm16d17_write_reg(data, PM16D17_PS_SETTING, op_mode_setting_val);
> +	if (ret < 0)
> +		return ret;
> +
> +	of_property_read_u32(np, "ps-ir-led-pulse-count", &pulse_count);
> +	if (pulse_count > 256)
> +		pulse_count = 256;
	pulse_count = min(256, pulse_count);

> +	ret = pm16d17_write_reg(data, PM16D17_VCSEL_DRIVE_PULSE, pulse_count - 1);
> +	if (ret < 0)
> +		return ret;
> +
> +	of_property_read_u32(np, "ps-offset-cancel", &ps_offset_cancel);
> +	if (ps_offset_cancel != 0) {
> +		ret = pm16d17_write_reg(data, PM16D17_PS_SETTING2, OFFSET_CANCEL_ENABLE);
> +		if (ret < 0)
> +			return ret;
> +
> +		offset_lsb = ps_offset_cancel & PS_OFFSET_CANCEL_LSB_MASK;
> +		offset_msb = (ps_offset_cancel & PS_OFFSET_CANCEL_MSB_MASK) >> 8;
> +
> +		ret = pm16d17_write_reg(data, PM16D17_PS_OFFSET_CANCEL_L, offset_lsb);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = pm16d17_write_reg(data, PM16D17_PS_OFFSET_CANCEL_H, offset_msb);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int pm16d17_probe(struct i2c_client *client)
> +{
> +	struct pm16d17_data *data;
> +	struct iio_dev *indio_dev;
> +	int ret;
> +
> +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	indio_dev->dev.parent = &client->dev;

No need to set that - devm_iio_device_alloc() does it for you.

> +	indio_dev->info = &pm16d17_info;
> +	indio_dev->name = PM16D17_DRV_NAME;

There is no particularly reason why the name here and the driver name should
match.  As such, I'd much rather just see the explicit string listed here
directly and that define being dropped.

> +	indio_dev->channels = pm16d17_channels;
> +	indio_dev->num_channels = ARRAY_SIZE(pm16d17_channels);
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	data = iio_priv(indio_dev);
> +	data->client = client;

You only ever use this to get to client->dev.  Just have a struct device
in there instead.  Or you can retrieve it via regmap_get_device()
if you prefer not to store it explicitly.
> +
> +	data->regmap = devm_regmap_init_i2c(client, &pm16d17_regmap_config);
> +	if (IS_ERR(data->regmap)) {
> +		dev_err(&client->dev, "regmap initialization failed.\n");
> +		return PTR_ERR(data->regmap);
For error prints in probe or functions just called from probe use
		return dev_err_probe(&client->dev, PTR_ERR(data->regmap),
				     ....);


> +	}
> +
> +	ret = pm16d17_chip_init(data);
> +	if (ret)
> +		return ret;
> +
> +	return devm_iio_device_register(&client->dev, indio_dev);
> +}
> +
> +static const struct i2c_device_id pm16d17_id[] = {
> +	{"pm16d17", 0},
> +	{}
> +};
> +MODULE_DEVICE_TABLE(i2c, pm16d17_id);
> +
> +static const struct of_device_id pm16d17_of_match[] = {
> +	{ .compatible = "everlight,pm16d17" },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, pm16d17_of_match);
> +
> +static struct i2c_driver pm16d17_driver = {
> +	.driver = {
> +		.name = PM16D17_DRV_NAME,
Put the string in directly here

> +		.of_match_table = pm16d17_of_match,
> +	},
> +	.probe = pm16d17_probe,
> +	.id_table = pm16d17_id,
> +};
> +module_i2c_driver(pm16d17_driver);
> +
> +MODULE_AUTHOR("Chao Zeng <chao.zeng@siemens.com>");
> +MODULE_DESCRIPTION("PM16D17 proximity sensor");
> +MODULE_LICENSE("GPL");
diff mbox series

Patch

diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig
index 2ca3b0bc5eba..4c26bc3a0390 100644
--- a/drivers/iio/proximity/Kconfig
+++ b/drivers/iio/proximity/Kconfig
@@ -96,6 +96,17 @@  config PING
 	  To compile this driver as a module, choose M here: the
 	  module will be called ping.
 
+config PM16D17
+	tristate "PM16D17 proximity sensor"
+	select REGMAP_I2C
+	depends on I2C
+	help
+	 Say Y here to build a driver for the Everlight Devices
+	 PM16D17 proximity sensor.
+
+	 To compile this driver as a module, choose M here: the
+	 module will be called pm16d17.
+
 config RFD77402
 	tristate "RFD77402 ToF sensor"
 	depends on I2C
diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile
index f36598380446..e41bba9c7cd3 100644
--- a/drivers/iio/proximity/Makefile
+++ b/drivers/iio/proximity/Makefile
@@ -11,6 +11,7 @@  obj-$(CONFIG_ISL29501)		+= isl29501.o
 obj-$(CONFIG_LIDAR_LITE_V2)	+= pulsedlight-lidar-lite-v2.o
 obj-$(CONFIG_MB1232)		+= mb1232.o
 obj-$(CONFIG_PING)		+= ping.o
+obj-$(CONFIG_PM16D17)		+= pm16d17.o
 obj-$(CONFIG_RFD77402)		+= rfd77402.o
 obj-$(CONFIG_SRF04)		+= srf04.o
 obj-$(CONFIG_SRF08)		+= srf08.o
diff --git a/drivers/iio/proximity/pm16d17.c b/drivers/iio/proximity/pm16d17.c
new file mode 100644
index 000000000000..94f21fc5e2fb
--- /dev/null
+++ b/drivers/iio/proximity/pm16d17.c
@@ -0,0 +1,324 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Siemens AG, 2023-2024
+ *
+ * Driver for Everlight PM-16d17 proximity sensor
+ *
+ * Author: Chao Zeng <chao.zeng@siemens.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+
+#define PM16D17_DRV_NAME		"pm16d17"
+#define PM16D17_REGMAP_NAME		"pm16d17_regmap"
+
+/* Registers Address */
+#define PM16D17_OP_MODE			0x00
+#define PM16D17_INTERRUPT_FLAG		0x01
+#define PM16D17_PS_SETTING		0x0A
+#define PM16D17_VCSEL_DRIVE_CURRENT	0x0B
+#define PM16D17_VCSEL_DRIVE_PULSE	0x0C
+#define PM16D17_PS_INTUPT_LTHD_L	0x0D
+#define PM16D17_PS_INTUPT_LTHD_H	0x0E
+#define PM16D17_PS_INTUPT_HTHD_L	0x0F
+#define PM16D17_PS_INTUPT_HTHD_H	0x10
+#define PM16D17_PS_DATA_L		0x11
+#define PM16D17_PS_DATA_H		0x12
+#define PM16D17_PS_SETTING2		0x13
+#define PM16D17_PS_OFFSET_CANCEL_L	0x14
+#define PM16D17_PS_OFFSET_CANCEL_H	0x15
+#define PM16D17_DEV_ID			0x18
+
+#define DEVICE_ID			0x11
+
+#define ENABLE_PS_FUNCTION		BIT(3)
+#define PS_GAIN_MASK			GENMASK(7, 6)
+#define PS_ITIME_MASK			GENMASK(5, 3)
+#define PS_WTIME_MASK			GENMASK(2, 0)
+#define OFFSET_CANCEL_ENABLE		BIT(7)
+#define PS_OFFSET_CANCEL_LSB_MASK	GENMASK(7, 0)
+#define PS_OFFSET_CANCEL_MSB_MASK	GENMASK(15, 8)
+
+enum {
+	PITIME_0_POINT_4_MS = (0 << 3),
+	PITIME_0_POINT_8_MS = (1 << 3),
+	PITIME_1_POINT_6_MS = (2 << 3),
+	PITIME_3_POINT_2_MS = (3 << 3),
+	PITIME_6_POINT_3_MS = (4 << 3),
+	PITIME_12_POINT_6_MS = (5 << 3),
+	PITIME_25_POINT_2_MS = (6 << 3),
+};
+
+enum {
+	PWTIME_12_POINT_5_MS = 0,
+	PWTIME_25_MS,
+	PWTIME_50_MS,
+	PWTIME_100_MS,
+	PWTIME_200_MS,
+	PWTIME_400_MS,
+	PWTIME_800_MS,
+	PWTIME_1600_MS,
+};
+
+struct pm16d17_data {
+	struct i2c_client *client;
+	struct regmap *regmap;
+};
+
+static const struct regmap_config pm16d17_regmap_config = {
+	.name = PM16D17_REGMAP_NAME,
+	.reg_bits = 8,
+	.val_bits = 8,
+	.cache_type = REGCACHE_NONE,
+};
+
+static const struct iio_chan_spec pm16d17_channels[] = {
+	{
+		.type = IIO_PROXIMITY,
+		.indexed = 1,
+		.channel = 0,
+		.scan_index = -1,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+	}
+};
+
+static inline int pm16d17_write_reg(struct pm16d17_data *data,
+				    unsigned int reg,
+				    unsigned int value)
+{
+	return regmap_write(data->regmap, reg, value);
+}
+
+static inline unsigned int pm16d17_read_reg(struct pm16d17_data *data,
+					    unsigned int reg,
+					    unsigned int *reg_val)
+{
+	return regmap_read(data->regmap, reg, reg_val);
+}
+
+static int pm16d17_read_raw(struct iio_dev *indio_dev,
+			    struct iio_chan_spec const *chan,
+			    int *val, int *val2, long mask)
+{
+	struct pm16d17_data *data = iio_priv(indio_dev);
+	unsigned int ps_data_l;
+	unsigned int ps_data_h;
+	uint16_t ps_data;
+	int ret = -EINVAL;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		switch (chan->type) {
+		case IIO_PROXIMITY:
+			ret = pm16d17_read_reg(data, PM16D17_PS_DATA_L, &ps_data_l);
+			if (ret < 0)
+				return ret;
+
+			ret = pm16d17_read_reg(data, PM16D17_PS_DATA_H, &ps_data_h);
+			if (ret < 0)
+				return ret;
+
+			ps_data = (ps_data_h << 8) | ps_data_l;
+
+			dev_dbg(&data->client->dev, "PS data: %x\n", ps_data);
+
+			*val = ps_data;
+			ret = IIO_VAL_INT;
+			break;
+		default:
+			break;
+		}
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+static const struct iio_info pm16d17_info = {
+	.read_raw = pm16d17_read_raw,
+};
+
+static int pm16d17_chip_init(struct pm16d17_data *data)
+{
+	struct i2c_client *client = data->client;
+	struct device_node *np = client->dev.of_node;
+	const char *conv_time = NULL;
+	const char *wait_time = NULL;
+	uint8_t op_mode_setting_val;
+	uint32_t ps_offset_cancel;
+	uint8_t offset_lsb;
+	uint8_t offset_msb;
+	uint32_t pulse_count;
+	uint32_t pgain;
+	unsigned int val;
+	int ret;
+
+	ret = pm16d17_read_reg(data, PM16D17_DEV_ID, &val);
+
+	if (ret < 0 || (val != DEVICE_ID)) {
+		dev_err(&client->dev, "Invalid chip id 0x%04x\n", val);
+		return -ENODEV;
+	}
+
+	dev_dbg(&client->dev, "Detected PM16D17 with chip id: 0x%04x\n", val);
+
+	ret = pm16d17_write_reg(data, PM16D17_OP_MODE, ENABLE_PS_FUNCTION);
+	if (ret < 0)
+		return ret;
+
+	of_property_read_u32(np, "ps-gain", &pgain);
+	switch (pgain) {
+	case 1:
+	case 2:
+	case 4:
+	case 8:
+		op_mode_setting_val |= (ilog2(pgain) << 6) & PS_GAIN_MASK;
+		break;
+	default:
+		break;
+	}
+
+	of_property_read_string(np, "ps-itime", &conv_time);
+	if (strcmp(conv_time, "0.4") == 0)
+		op_mode_setting_val |= PITIME_0_POINT_4_MS & PS_ITIME_MASK;
+	else if (strcmp(conv_time, "0.8") == 0)
+		op_mode_setting_val |= PITIME_0_POINT_8_MS & PS_ITIME_MASK;
+	else if (strcmp(conv_time, "1.6") == 0)
+		op_mode_setting_val |= PITIME_1_POINT_6_MS & PS_ITIME_MASK;
+	else if (strcmp(conv_time, "3.2") == 0)
+		op_mode_setting_val |= PITIME_3_POINT_2_MS & PS_ITIME_MASK;
+	else if (strcmp(conv_time, "6.3") == 0)
+		op_mode_setting_val |= PITIME_6_POINT_3_MS & PS_ITIME_MASK;
+	else if (strcmp(conv_time, "12.6") == 0)
+		op_mode_setting_val |= PITIME_12_POINT_6_MS & PS_ITIME_MASK;
+	else if (strcmp(conv_time, "25.2") == 0)
+		op_mode_setting_val |= PITIME_25_POINT_2_MS & PS_ITIME_MASK;
+	else {
+		dev_info(&client->dev, "Using default ps itime value\n");
+		op_mode_setting_val |= PITIME_0_POINT_4_MS & PS_ITIME_MASK;
+	}
+
+	of_property_read_string(np, "ps-wtime", &wait_time);
+	if (strcmp(wait_time, "12.5") == 0)
+		op_mode_setting_val |= PWTIME_12_POINT_5_MS & PS_WTIME_MASK;
+	else if (strcmp(wait_time, "25") == 0)
+		op_mode_setting_val |= PWTIME_25_MS & PS_WTIME_MASK;
+	else if (strcmp(wait_time, "50") == 0)
+		op_mode_setting_val |= PWTIME_50_MS & PS_WTIME_MASK;
+	else if (strcmp(wait_time, "100") == 0)
+		op_mode_setting_val |= PWTIME_100_MS & PS_WTIME_MASK;
+	else if (strcmp(wait_time, "200") == 0)
+		op_mode_setting_val |= PWTIME_200_MS & PS_WTIME_MASK;
+	else if (strcmp(wait_time, "400") == 0)
+		op_mode_setting_val |= PWTIME_400_MS & PS_WTIME_MASK;
+	else if (strcmp(wait_time, "800") == 0)
+		op_mode_setting_val |= PWTIME_800_MS & PS_WTIME_MASK;
+	else if (strcmp(wait_time, "1600") == 0)
+		op_mode_setting_val |= PWTIME_1600_MS & PS_WTIME_MASK;
+	else {
+		dev_info(&client->dev, "Using default ps wtime value\n");
+		op_mode_setting_val |= PWTIME_12_POINT_5_MS & PS_WTIME_MASK;
+	}
+
+	ret = pm16d17_write_reg(data, PM16D17_PS_SETTING, op_mode_setting_val);
+	if (ret < 0)
+		return ret;
+
+	of_property_read_u32(np, "ps-ir-led-pulse-count", &pulse_count);
+	if (pulse_count > 256)
+		pulse_count = 256;
+	ret = pm16d17_write_reg(data, PM16D17_VCSEL_DRIVE_PULSE, pulse_count - 1);
+	if (ret < 0)
+		return ret;
+
+	of_property_read_u32(np, "ps-offset-cancel", &ps_offset_cancel);
+	if (ps_offset_cancel != 0) {
+		ret = pm16d17_write_reg(data, PM16D17_PS_SETTING2, OFFSET_CANCEL_ENABLE);
+		if (ret < 0)
+			return ret;
+
+		offset_lsb = ps_offset_cancel & PS_OFFSET_CANCEL_LSB_MASK;
+		offset_msb = (ps_offset_cancel & PS_OFFSET_CANCEL_MSB_MASK) >> 8;
+
+		ret = pm16d17_write_reg(data, PM16D17_PS_OFFSET_CANCEL_L, offset_lsb);
+		if (ret < 0)
+			return ret;
+
+		ret = pm16d17_write_reg(data, PM16D17_PS_OFFSET_CANCEL_H, offset_msb);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int pm16d17_probe(struct i2c_client *client)
+{
+	struct pm16d17_data *data;
+	struct iio_dev *indio_dev;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->info = &pm16d17_info;
+	indio_dev->name = PM16D17_DRV_NAME;
+	indio_dev->channels = pm16d17_channels;
+	indio_dev->num_channels = ARRAY_SIZE(pm16d17_channels);
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	data = iio_priv(indio_dev);
+	data->client = client;
+
+	data->regmap = devm_regmap_init_i2c(client, &pm16d17_regmap_config);
+	if (IS_ERR(data->regmap)) {
+		dev_err(&client->dev, "regmap initialization failed.\n");
+		return PTR_ERR(data->regmap);
+	}
+
+	ret = pm16d17_chip_init(data);
+	if (ret)
+		return ret;
+
+	return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct i2c_device_id pm16d17_id[] = {
+	{"pm16d17", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, pm16d17_id);
+
+static const struct of_device_id pm16d17_of_match[] = {
+	{ .compatible = "everlight,pm16d17" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, pm16d17_of_match);
+
+static struct i2c_driver pm16d17_driver = {
+	.driver = {
+		.name = PM16D17_DRV_NAME,
+		.of_match_table = pm16d17_of_match,
+	},
+	.probe = pm16d17_probe,
+	.id_table = pm16d17_id,
+};
+module_i2c_driver(pm16d17_driver);
+
+MODULE_AUTHOR("Chao Zeng <chao.zeng@siemens.com>");
+MODULE_DESCRIPTION("PM16D17 proximity sensor");
+MODULE_LICENSE("GPL");