Received: by 2002:a25:8b91:0:0:0:0:0 with SMTP id j17csp14961069ybl; Mon, 30 Dec 2019 21:53:38 -0800 (PST) X-Google-Smtp-Source: APXvYqyTAeUrPp8SXthFP6B69ZTNfV8edzAIFaLRcWmsFlGTNMNBHgnTGxvSuxvmxUT5N/Bh9Erm X-Received: by 2002:a9d:5786:: with SMTP id q6mr59068049oth.164.1577771618328; Mon, 30 Dec 2019 21:53:38 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1577771618; cv=none; d=google.com; s=arc-20160816; b=QFewa1s+pBEg83iHuBUIjxKwgT0Y4tQN12tmaCsIwgfhzctU/WayMnYNlUox6cWbDc rUlyjQh/5636XchXma5S1hOBBRXk+l11RQRJXuMbgENrcm2MiTZ2yLsPo/i5O86DGOid jCK+rh4mRGOyLfFCdlvhvYvwNYm8Xtr/bUI3iLcEduhaxw+ztPNni3Cj9g3w4JS4cm3l Oh1rxv/G0Oip/MvnffUEdja+wEq64L3mJBRZ4wrN8Ahss4DeTokP+XfYONQUt+jxpEj5 DtDAmbkqP8sf1y1XS6oO/R5k1deuxnE9WkGHoLQyVEBO2a7gnwb/qLAWTK54147aq/bX IE6w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:cc:to:subject :message-id:date:from:in-reply-to:references:mime-version :dkim-signature; bh=V1Ofy38qc3x0kHvfDpuaLYz5Xb8FT49t+Jk2CJhdDgo=; b=BkVLqi6BmA6se0oE7oW+MMOmP71aOqMZw3ZIevhcdKosRdCWHL2xHVYq5a7jZJbjEb 6tsMHX4jnVx2D1VC2+LFP+LMi1+JCPn+d3vVJzR2L7pU5yM/lAcCN+mg10jQ45K87zN4 zTuMppxRuEGBq03+Rglls09VSne92pMuY4BF3kDa0wspBRm1wZz5ZNjOieIH0455b56z m9XabkA3CycvkTFSGQLRtVfH3UbLDpZ9c9jwDdfKyX6Eh+3JU5Uc7rWYm3SofxjpyUUZ 57JlQYhlDy4O3PFN+twysYSSpvbhH+ClSeH89Oyvk6p3g4gYBZ7NFhRPB0yNteRfXf9J RvpA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@konsulko.com header.s=google header.b=q6PqPaDu; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id n139si23090922oig.121.2019.12.30.21.52.57; Mon, 30 Dec 2019 21:53:38 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@konsulko.com header.s=google header.b=q6PqPaDu; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1725890AbfLaFuz (ORCPT + 99 others); Tue, 31 Dec 2019 00:50:55 -0500 Received: from mail-io1-f67.google.com ([209.85.166.67]:40080 "EHLO mail-io1-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725468AbfLaFuz (ORCPT ); Tue, 31 Dec 2019 00:50:55 -0500 Received: by mail-io1-f67.google.com with SMTP id x1so33202609iop.7 for ; Mon, 30 Dec 2019 21:50:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=konsulko.com; s=google; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc:content-transfer-encoding; bh=V1Ofy38qc3x0kHvfDpuaLYz5Xb8FT49t+Jk2CJhdDgo=; b=q6PqPaDuiMjUA9YUlWm13Nw3Nlgkc8y1KD3dSivkGS6qojotQ8XSVOelRuzGnKFB0z +yBcq6uCN/V60x9FF5Y/H/+AuTJ9sLktubstRtqcuQRNBnKfdpfMITDa5kPMgR5Rv2Ug m3U26pOpPjDAnr7DQiJYUeb6vIRdNQLv7lfS4= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc:content-transfer-encoding; bh=V1Ofy38qc3x0kHvfDpuaLYz5Xb8FT49t+Jk2CJhdDgo=; b=KHQjUE15Je6poRuNCYriIk5wS0TTcpVFLHeuRZiISPNody+M4s13B2Lq07aVBw1VWc ofgnWaWkPstghEthTfex0ex+dLNqxo+3O8ddgH33Ao+xofDbv0OCYIVjln2vkB76I6b5 JWaYyB7u+8LNoUIWVMTxFxOi5i9lMLJbDckL4SGxJnpz0zh72wDsQiR5a0y9/ukvVafF 5+hiBn1n84wU046EF22Rq4Ve2ivK+WE9ImI3B+LkFUqoaM/ZvVJpsHvDAwayTure8NeK b+Q0OYCz6ZETOOaDWtzww0TNEfKcl29YXQRhhQpgV870bc7OiZS9zt90VfUypzuIFNvH D/ew== X-Gm-Message-State: APjAAAX8lEyUw7hoVj33TrBUI63+XQmQ/USnCYQNyynxnyYhjzyIfWGH ICoQsRBERZRyh6DeaX9Sa8JCSO0dMwPkOj7fsqfkPg== X-Received: by 2002:a5d:8782:: with SMTP id f2mr37856163ion.53.1577771453821; Mon, 30 Dec 2019 21:50:53 -0800 (PST) MIME-Version: 1.0 References: <3a17c9e1-f916-0cde-3296-70066dccb2b3@gmail.com> <20191223160712.1e1cfcde@archlinux> In-Reply-To: <20191223160712.1e1cfcde@archlinux> From: Matt Ranostay Date: Tue, 31 Dec 2019 13:50:42 +0800 Message-ID: Subject: Re: [PATCH v2 1/2] iio: chemical: add support for Dynament Premier series single gas sensor To: Jonathan Cameron Cc: ruantu , Hartmut Knaack , Lars-Peter Clausen , Peter Meerwald-Stadler , Tomasz Duszynski , mike.looijmans@topic.nl, Greg Kroah-Hartman , Thomas Gleixner , Arnd Bergmann , "open list:IIO SUBSYSTEM AND DRIVERS" , open list Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Tue, Dec 24, 2019 at 12:07 AM Jonathan Cameron wrote: > > On Tue, 17 Dec 2019 21:17:43 -0800 > Matt Ranostay wrote: > > > > On Dec 17, 2019, at 17:52, ruantu wrote: > > > > > > =EF=BB=BFOn Tue, Dec 17, 2019 at 2:24 AM YuDong Zhang wrote: > > >>> Add support for Dynament Premier series single gas sensor. > > >>> > > >> Just looking the Dynament site and I assume this is for the OEM-1 > > >> Development kit? If so you probably should > > >> note that in the documentation because the sensors themselves are > > >> likely to be used in other end products (and not > > >> always the dev kit) > > >> > > >> Also bit of silly question this is an UART device so why not do > > >> processing in userspace? :) > > >> > > >> - Matt > > > > This is a driver implemented according to the > > Communications protocol>. I think the protocol is standard. This is > > > the idea that emerged after the iio subsystem used serial_bus. > > > > Well egg on my face since only got recently back on this mailing > > list.... looks good over all but the floating point to fixed point > > stuff maybe has some comments. Also I am on holiday till the New Year > > but please CC on future changes! > > The question about whether to allow serial bus drivers in IIO wasn't > entirely obvious so reasonable to raise it. There is a long tradition > of doing serial bus drivers only in userspace. However, there is > nothing fundamentally different about them from spi or i2c drivers. > > Sometimes we have the addition of interrupts or gpio signals (not > true for this one though!). > > Basically it's nicer to have all sensors use a common interface. > We could do that the next level up in a userspace library but I'm not > sure there is stronger reason to do that in this case than for simple i2c > / spi devices. > > So I saw no reason to block serdev devices in IIO. Not many there > yet though! Ah this could come in handy for the EZO line of Atlas Scientific sensors which communicate over UART (well also i2c but it the same command structure as the UART). - Matt > > Thanks, > > Jonathan > > > > > > >>> Signed-off-by: YuDong Zhang > > >>> --- > > >>> MAINTAINERS | 5 + > > >>> drivers/iio/chemical/Kconfig | 10 + > > >>> drivers/iio/chemical/Makefile | 1 + > > >>> drivers/iio/chemical/premier.c | 366 +++++++++++++++++++++++++++++= ++++ > > >>> 4 files changed, 382 insertions(+) > > >>> create mode 100644 drivers/iio/chemical/premier.c > > >>> > > >>> diff --git a/MAINTAINERS b/MAINTAINERS > > >>> index a049abccaa26..ae228ac7adc9 100644 > > >>> --- a/MAINTAINERS > > >>> +++ b/MAINTAINERS > > >>> @@ -5792,6 +5792,11 @@ S: Maintained > > >>> F: drivers/media/usb/dvb-usb-v2/dvb_usb* > > >>> F: drivers/media/usb/dvb-usb-v2/usb_urb.c > > >>> > > >>> +DYNAMENT PREMIER SERIES SINGLE GAS SENSOR DRIVER > > >>> +M: YuDong Zhang > > >>> +S: Maintained > > >>> +F: drivers/iio/chemical/premier.c > > >>> + > > >>> DYNAMIC DEBUG > > >>> M: Jason Baron > > >>> S: Maintained > > >>> diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kc= onfig > > >>> index fa4586037bb8..93c0c108245b 100644 > > >>> --- a/drivers/iio/chemical/Kconfig > > >>> +++ b/drivers/iio/chemical/Kconfig > > >>> @@ -62,6 +62,16 @@ config IAQCORE > > >>> iAQ-Core Continuous/Pulsed VOC (Volatile Organic Compound= s) > > >>> sensors > > >>> > > >>> +config PREMIER > > >>> + tristate "Dynament Premier series sensor" > > >>> + depends on SERIAL_DEV_BUS > > >>> + help > > >>> + Say Y here to build support for the Dynament Premier > > >>> + series sensor. > > >>> + > > >>> + To compile this driver as a module, choose M here: the mo= dule will > > >>> + be called premier. > > >>> + > > >>> config PMS7003 > > >>> tristate "Plantower PMS7003 particulate matter sensor" > > >>> depends on SERIAL_DEV_BUS > > >>> diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/M= akefile > > >>> index f97270bc4034..c8e779d7cf4a 100644 > > >>> --- a/drivers/iio/chemical/Makefile > > >>> +++ b/drivers/iio/chemical/Makefile > > >>> @@ -10,6 +10,7 @@ obj-$(CONFIG_BME680_I2C) +=3D bme680_i2c.o > > >>> obj-$(CONFIG_BME680_SPI) +=3D bme680_spi.o > > >>> obj-$(CONFIG_CCS811) +=3D ccs811.o > > >>> obj-$(CONFIG_IAQCORE) +=3D ams-iaq-core.o > > >>> +obj-$(CONFIG_PREMIER) +=3D premier.o > > >>> obj-$(CONFIG_PMS7003) +=3D pms7003.o > > >>> obj-$(CONFIG_SENSIRION_SGP30) +=3D sgp30.o > > >>> obj-$(CONFIG_SPS30) +=3D sps30.o > > >>> diff --git a/drivers/iio/chemical/premier.c b/drivers/iio/chemical/= premier.c > > >>> new file mode 100644 > > >>> index 000000000000..a226dd9d78cb > > >>> --- /dev/null > > >>> +++ b/drivers/iio/chemical/premier.c > > >>> @@ -0,0 +1,366 @@ > > >>> +// SPDX-License-Identifier: GPL-2.0 > > >>> +/* > > >>> + * Dynament Premier series single gas sensor driver > > >>> + * > > >>> + * Copyright (c) YuDong Zhang > > >>> + */ > > >>> + > > >>> +#include > > >>> +#include > > >>> +#include > > >>> +#include > > >>> +#include > > >>> +#include > > >>> +#include > > >>> +#include > > >>> +#include > > >>> +#include > > >>> +#include > > >>> +#include > > >>> +#include > > >>> + > > >>> +#define PREMIER_DRIVER_NAME "dynament-premier" > > >>> + > > >>> +#define PREMIER_DLE (0x10) > > >>> +#define PREMIER_CMD_RD (0x13) > > >>> +#define PREMIER_CMD_NAK (0x19) > > >>> +#define PREMIER_CMD_DAT (0x1a) > > >>> +#define PREMIER_EOF (0x1f) > > >>> + > > >>> +#define PREMIER_TIMEOUT msecs_to_jiffies(6000) > > >>> + > > >>> +/* > > >>> + * commands have following format: > > >>> + * > > >>> + * +-----+-----+---------+-----+-----+-----------+-----------+ > > >>> + * | DLE | CMD | PAYLOAD | DLE | EOF | CKSUM MSB | CKSUM LSB | > > >>> + * +-----+-----+---------+-----+-----+-----------+-----------+ > > >>> + */ > > >>> +static const u8 premier_cmd_read_live_data_simple[] =3D { 0x10, 0x= 13, 0x06, 0x10, > > >>> + 0x1F, 0x00,= 0x58 }; > > >>> + > > >>> +struct premier_frame { > > >>> + u8 state; > > >>> + u8 is_dat; > > >>> + u8 is_nak; > > >>> + u8 data_len; > > >>> + u8 vi, si, gi, gj; > > >>> + u8 gas[4]; > > >>> + u8 byte_stuffing; > > >>> + u8 checksum_received[2]; > > >>> + u16 checksum_calculated; > > >>> +}; > > >>> + > > >>> +struct premier_data { > > >>> + struct serdev_device *serdev; > > >>> + struct premier_frame frame; > > >>> + struct completion frame_ready; > > >>> + struct mutex lock; /* must be held whenever state gets touc= hed */ > > >>> + struct regulator *vcc; > > >>> +}; > > >>> + > > >>> +static int premier_do_cmd_read_live_data(struct premier_data *stat= e) > > >>> +{ > > >>> + int ret; > > >>> + > > >>> + ret =3D serdev_device_write(state->serdev, > > >>> + premier_cmd_read_live_data_simple= , > > >>> + sizeof(premier_cmd_read_live_data= _simple), > > >>> + PREMIER_TIMEOUT); > > >>> + if (ret < sizeof(premier_cmd_read_live_data_simple)) > > >>> + return ret < 0 ? ret : -EIO; > > >>> + > > >>> + ret =3D wait_for_completion_interruptible_timeout(&state->f= rame_ready, > > >>> + PREMIER_TIM= EOUT); > > >>> + > > >>> + if (!ret) > > >>> + ret =3D -ETIMEDOUT; > > >>> + > > >>> + return ret < 0 ? ret : 0; > > >>> +} > > >>> + > > >>> +static s32 premier_float_to_int_clamped(const u8 *fp) > > >>> +{ > > >>> + int val =3D get_unaligned_le32(fp); > > >>> + int mantissa =3D val & GENMASK(22, 0); > > >>> + /* this is fine since passed float is always non-negative *= / > > >>> + int exp =3D val >> 23; > > >>> + int fraction, shift; > > >>> + > > >>> + /* special case 0 */ > > >>> + if (!exp && !mantissa) > > >>> + return 0; > > >>> + > > >>> + exp -=3D 127; > > >>> + if (exp < 0) { > > >>> + /* return values ranging from 1 to 99 */ > > >>> + return ((((1 << 23) + mantissa) * 100) >> 23) >> (-= exp); > > >>> + } > > >>> + > > >>> + /* return values ranging from 100 to int_max */ > > >>> + shift =3D 23 - exp; > > >>> + val =3D (1 << exp) + (mantissa >> shift); > > >>> + > > >>> + fraction =3D mantissa & GENMASK(shift - 1, 0); > > >>> + > > >>> + return val * 100 + ((fraction * 100) >> shift); > > >>> +} > > >>> + > > >>> +static int premier_read_raw(struct iio_dev *indio_dev, > > >>> + struct iio_chan_spec const *chan, int *= val, > > >>> + int *val2, long mask) > > >>> +{ > > >>> + struct premier_data *state =3D iio_priv(indio_dev); > > >>> + struct premier_frame *frame =3D &state->frame; > > >>> + int ret; > > >>> + s32 val_tmp; > > >>> + > > >>> + switch (mask) { > > >>> + case IIO_CHAN_INFO_PROCESSED: > > >>> + > > >>> + mutex_lock(&state->lock); > > >>> + ret =3D premier_do_cmd_read_live_data(state); > > >>> + if (ret) { > > >>> + mutex_unlock(&state->lock); > > >>> + return ret; > > >>> + } > > >>> + val_tmp =3D premier_float_to_int_clamped(frame->gas= ); > > >>> + mutex_unlock(&state->lock); > > >>> + > > >>> + *val =3D val_tmp / 100; > > >>> + *val2 =3D (val_tmp % 100) * 10000; > > >>> + return IIO_VAL_INT_PLUS_MICRO; > > >>> + default: > > >>> + return -EINVAL; > > >>> + } > > >>> + > > >>> + return -EINVAL; > > >>> +} > > >>> + > > >>> +static const struct iio_info premier_info =3D { > > >>> + .read_raw =3D premier_read_raw, > > >>> +}; > > >>> + > > >>> +static const struct iio_chan_spec premier_channels[] =3D { > > >>> + { > > >>> + .type =3D IIO_MASSCONCENTRATION, > > >>> + .channel =3D 1, > > >>> + .channel2 =3D IIO_MOD_CO2, > > >>> + .scan_index =3D -1, > > >>> + .info_mask_separate =3D BIT(IIO_CHAN_INFO_PROCESSED= ), > > >>> + .modified =3D 1, > > >>> + }, > > >>> + IIO_CHAN_SOFT_TIMESTAMP(0), > > >>> +}; > > >>> + > > >>> +static int premier_receive_buf(struct serdev_device *serdev, > > >>> + const unsigned char *buf, size_t siz= e) > > >>> +{ > > >>> + struct iio_dev *indio_dev =3D serdev_device_get_drvdata(ser= dev); > > >>> + struct premier_data *state =3D iio_priv(indio_dev); > > >>> + struct premier_frame *frame =3D &state->frame; > > >>> + int i; > > >>> + > > >>> + for (i =3D 0; i < size; i++) { > > >>> + if (frame->state > 0 && frame->state <=3D 7) > > >>> + frame->checksum_calculated +=3D buf[i]; > > >>> + > > >>> + switch (frame->state) { > > >>> + case 0: > > >>> + if (buf[i] =3D=3D PREMIER_DLE) { > > >>> + frame->is_dat =3D 0; > > >>> + frame->is_nak =3D 0; > > >>> + frame->checksum_calculated =3D buf[= i]; > > >>> + /* We don't initialize checksum_cal= culated in > > >>> + * the last state in case we didn't= go > > >>> + * there because of noise > > >>> + */ > > >>> + frame->state++; > > >>> + } > > >>> + break; > > >>> + case 1: > > >>> + /* > > >>> + * If noise corrupts a byte in the FSM sequ= ence, > > >>> + * we loop between state 0 and 1, > > >>> + * until we have a valid sequence of DLE&DA= T or DLE&NAK > > >>> + */ > > >>> + if (buf[i] =3D=3D PREMIER_CMD_DAT) { > > >>> + frame->is_dat =3D 1; > > >>> + frame->state++; > > >>> + } else if (buf[i] =3D=3D PREMIER_CMD_NAK) { > > >>> + frame->is_nak =3D 1; > > >>> + frame->state++; > > >>> + } else > > >>> + frame->state =3D 0; > > >>> + break; > > >>> + case 2: > > >>> + if (frame->is_nak) > > >>> + frame->state =3D 0; > > >>> + else if (frame->is_dat) { > > >>> + frame->data_len =3D buf[i] - 4; > > >>> + /* remove version and status bytes = from count */ > > >>> + if (frame->data_len < 4) > > >>> + frame->state =3D 0; > > >>> + /* we check for the upper limit in = state 5 */ > > >>> + else > > >>> + frame->state++; > > >>> + } else > > >>> + frame->state =3D 0; > > >>> + break; > > >>> + case 3: > > >>> + /* Just do nothing for 2 rounds to bypass > > >>> + * the 2 version bytes > > >>> + */ > > >>> + if (frame->vi < 2 - 1) > > >>> + frame->vi++; > > >>> + else { > > >>> + frame->vi =3D 0; > > >>> + frame->state++; > > >>> + } > > >>> + break; > > >>> + case 4: > > >>> + if (frame->si < 2 - 1) > > >>> + frame->si++; > > >>> + else { > > >>> + frame->si =3D 0; > > >>> + frame->state++; > > >>> + } > > >>> + break; > > >>> + case 5: > > >>> + if (frame->gi < frame->data_len - 1) { > > >>> + if (buf[i] !=3D PREMIER_DLE || > > >>> + frame->byte_stuffing) { > > >>> + frame->gas[frame->gj] =3D b= uf[i]; > > >>> + frame->byte_stuffing =3D 0; > > >>> + frame->gj++; > > >>> + if (frame->gj >=3D 4) > > >>> + frame->state =3D 0; > > >>> + /* Don't violate array limi= ts > > >>> + * if data_len corrupt > > >>> + */ > > >>> + } else > > >>> + frame->byte_stuffing =3D 1; > > >>> + frame->gi++; > > >>> + } else { > > >>> + frame->gas[frame->gj] =3D buf[i]; > > >>> + frame->byte_stuffing =3D 0; > > >>> + frame->gi =3D 0; > > >>> + frame->gj =3D 0; > > >>> + frame->state++; > > >>> + } > > >>> + break; > > >>> + case 6: > > >>> + if (buf[i] =3D=3D PREMIER_DLE) > > >>> + frame->state++; > > >>> + else > > >>> + frame->state =3D 0; > > >>> + break; > > >>> + case 7: > > >>> + if (buf[i] =3D=3D PREMIER_EOF) > > >>> + frame->state++; > > >>> + else > > >>> + frame->state =3D 0; > > >>> + break; > > >>> + case 8: > > >>> + frame->checksum_received[1] =3D buf[i]; > > >>> + > > >>> + frame->state++; > > >>> + break; > > >>> + case 9: > > >>> + frame->checksum_received[0] =3D buf[i]; > > >>> + > > >>> + if (frame->checksum_calculated =3D=3D > > >>> + get_unaligned_le16(frame->checksum_rece= ived)) > > >>> + complete(&state->frame_ready); > > >>> + > > >>> + frame->state =3D 0; > > >>> + break; > > >>> + } > > >>> + } > > >>> + > > >>> + return size; > > >>> +} > > >>> + > > >>> +static const struct serdev_device_ops premier_serdev_ops =3D { > > >>> + .receive_buf =3D premier_receive_buf, > > >>> + .write_wakeup =3D serdev_device_write_wakeup, > > >>> +}; > > >>> + > > >>> +static int premier_probe(struct serdev_device *serdev) > > >>> +{ > > >>> + struct premier_data *state; > > >>> + struct iio_dev *indio_dev; > > >>> + int ret; > > >>> + > > >>> + indio_dev =3D devm_iio_device_alloc(&serdev->dev, sizeof(*s= tate)); > > >>> + if (!indio_dev) > > >>> + return -ENOMEM; > > >>> + > > >>> + state =3D iio_priv(indio_dev); > > >>> + serdev_device_set_drvdata(serdev, indio_dev); > > >>> + state->serdev =3D serdev; > > >>> + indio_dev->dev.parent =3D &serdev->dev; > > >>> + indio_dev->info =3D &premier_info; > > >>> + indio_dev->name =3D PREMIER_DRIVER_NAME; > > >>> + indio_dev->channels =3D premier_channels; > > >>> + indio_dev->num_channels =3D ARRAY_SIZE(premier_channels); > > >>> + indio_dev->modes =3D INDIO_DIRECT_MODE; > > >>> + > > >>> + mutex_init(&state->lock); > > >>> + init_completion(&state->frame_ready); > > >>> + > > >>> + state->vcc =3D devm_regulator_get(&serdev->dev, "vcc"); > > >>> + if (IS_ERR(state->vcc)) { > > >>> + ret =3D PTR_ERR(state->vcc); > > >>> + return ret; > > >>> + } > > >>> + > > >>> + serdev_device_set_client_ops(serdev, &premier_serdev_ops); > > >>> + ret =3D devm_serdev_device_open(&serdev->dev, serdev); > > >>> + if (ret) > > >>> + return ret; > > >>> + > > >>> + serdev_device_set_baudrate(serdev, 9600); > > >>> + serdev_device_set_flow_control(serdev, false); > > >>> + > > >>> + ret =3D serdev_device_set_parity(serdev, SERDEV_PARITY_NONE= ); > > >>> + if (ret) > > >>> + return ret; > > >>> + > > >>> + if (state->vcc) { > > >>> + ret =3D regulator_enable(state->vcc); > > >>> + if (ret) > > >>> + return ret; > > >>> + } > > >>> + > > >>> + return devm_iio_device_register(&serdev->dev, indio_dev); > > >>> +} > > >>> + > > >>> +static void premier_remove(struct serdev_device *serdev) > > >>> +{ > > >>> + struct iio_dev *indio_dev =3D serdev_device_get_drvdata(ser= dev); > > >>> + struct premier_data *state =3D iio_priv(indio_dev); > > >>> + > > >>> + if (state->vcc) > > >>> + regulator_disable(state->vcc); > > >>> +} > > >>> + > > >>> +static const struct of_device_id premier_of_match[] =3D { > > >>> + { .compatible =3D "dynament,premier" }, > > >>> + {} > > >>> +}; > > >>> +MODULE_DEVICE_TABLE(of, premier_of_match); > > >>> + > > >>> +static struct serdev_device_driver premier_driver =3D { > > >>> + .driver =3D { > > >>> + .name =3D PREMIER_DRIVER_NAME, > > >>> + .of_match_table =3D premier_of_match, > > >>> + }, > > >>> + .probe =3D premier_probe, > > >>> + .remove =3D premier_remove, > > >>> +}; > > >>> +module_serdev_device_driver(premier_driver); > > >>> + > > >>> +MODULE_AUTHOR("YuDong Zhang "); > > >>> +MODULE_DESCRIPTION("Dynament Premier series single gas sensor driv= er"); > > >>> +MODULE_LICENSE("GPL v2"); > > >>> -- > > >>> 2.24.1 > > >>> >