Re: [PATCH 3/5] iio: adc: sunxi-gpadc-iio: enable iio_buffers
,22 @@ static int sunxi_gpadc_read_raw(struct iio_dev > *indio_dev, > int *val, int *val2, long mask) > { > int ret; > + struct sunxi_gpadc_dev *info = iio_priv(indio_dev); > > switch (mask) { > case IIO_CHAN_INFO_PROCESSED: > + if (info->buffered && !info->ts_attached) > + return -EBUSY; there would be iio_device_claim_direct_mode() > + > ret = sunxi_gpadc_temp_read(indio_dev, val); > if (ret) > return ret; > > return IIO_VAL_INT; > case IIO_CHAN_INFO_RAW: > + if (info->buffered) > + return -EBUSY; > + > ret = sunxi_gpadc_adc_read(indio_dev, chan->channel, val); > if (ret) > return ret; > @@ -261,7 +302,29 @@ static irqreturn_t sunxi_gpadc_temp_data_irq_handler(int > irq, void *dev_id) > static irqreturn_t sunxi_gpadc_fifo_data_irq_handler(int irq, void *dev_id) > { > struct sunxi_gpadc_dev *info = dev_id; > - int ret; > + int ret, reg, i, fifo_count; > + > + if (info->buffered) { > + if (regmap_read(info->regmap, SUNXI_GPADC_TP_INT_FIFOS, ®)) > + return IRQ_HANDLED; > + > + fifo_count = (reg & SUNXI_GPADC_RXA_CNT) >> 8; > + /* Sometimes, the interrupt occurs when the FIFO is empty. */ > + if (!fifo_count) > + return IRQ_HANDLED; > + > + for (i = 0; i < fifo_count; i++) { > + if (regmap_read(info->regmap, SUNXI_GPADC_TP_DATA, > + &info->buffer.buffer[i])) > + return IRQ_HANDLED; > + } > + > + info->buffer.buff_size = i; > + > + iio_push_to_buffers(info->indio_dev, &info->buffer); > + > + return IRQ_HANDLED; > + } > > ret = regmap_read(info->regmap, SUNXI_GPADC_TP_DATA, &info->adc_data); > if (ret == 0) > @@ -270,6 +333,58 @@ static irqreturn_t sunxi_gpadc_fifo_data_irq_handler(int > irq, void *dev_id) > return IRQ_HANDLED; > } > > +static int sunxi_gpadc_buffer_postenable(struct iio_dev *indio_dev) > +{ > + struct sunxi_gpadc_dev *info = iio_priv(indio_dev); > + unsigned int reg; > + int ret; > + > + reg = SUNXI_GPADC_TP_FIFO_TRIG_LEVEL(1) | SUNXI_GPADC_TP_FIFO_FLUSH; > + regmap_update_bits(info->regmap, SUNXI_GPADC_TP_INT_FIFOC, reg, reg); > + > + if (info->ts_attached) { > + reg = SUNXI_GPADC_STYLUS_UP_DEBOUNCE(5) | > + SUNXI_GPADC_STYLUS_UP_DEBOUNCE_EN; > + > + if (info->flags & SUNXI_GPADC_ARCH_SUN6I) > + reg |= SUNXI_GPADC_SUN6I_TP_MODE_EN; > + else > + reg |= SUNXI_GPADC_TP_MODE_EN; > + } else { > + if (info->flags & SUNXI_GPADC_ARCH_SUN6I) > + reg = SUNXI_GPADC_SUN6I_TP_MODE_EN | > + SUNXI_GPADC_SUN6I_TP_ADC_SELECT; > + else > + reg = SUNXI_GPADC_TP_MODE_EN | > + SUNXI_GPADC_TP_ADC_SELECT; > + } > + > + if (regmap_write(info->regmap, SUNXI_GPADC_TP_CTRL1, reg)) > + return ret; > + > + info->buffered = true; > + > + enable_irq(info->fifo_data_irq); > + > + return 0; > +} > + > +static int sunxi_gpadc_buffer_predisable(struct iio_dev *indio_dev) > +{ > + struct sunxi_gpadc_dev *info = iio_priv(indio_dev); > + > + disable_irq(info->fifo_data_irq); > + > + info->buffered = false; > + > + return 0; > +} > + > +static const struct iio_buffer_setup_ops sunxi_gpadc_buffer_setup_ops = { > + .postenable = sunxi_gpadc_buffer_postenable, > + .predisable = sunxi_gpadc_buffer_predisable, > +}; > + > static int sunxi_gpadc_probe(struct platform_device *pdev) > { > struct sunxi_gpadc_dev *info; > @@ -286,16 +401,20 @@ static int sunxi_gpadc_probe(struct platform_device > *pdev) > info = iio_priv(indio_dev); > > info->regmap = sunxi_gpadc_mfd_dev->regmap; > + info->indio_dev = indio_dev; > init_completion(&info->completion); > indio_dev->name = dev_name(&pdev->dev); > indio_dev->dev.parent = &pdev->dev; > indio_dev->dev.of_node = pdev->dev.of_node; > indio_dev->info = &sunxi_gpadc_iio_info; > - indio_dev->modes = INDIO_DIRECT_MODE; > + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; > indio_dev->num_channels = ARRAY_SIZE(sunxi_gpadc_channels); > indio_dev->channels = sunxi_gpadc_channels; > + indio_dev->setup_ops = &sunxi_gpadc_buffer_setup_ops; > > info->flags = platform_get_device_id(pdev)->driver_data; > + info->ts_attached = of_property_read_bool(pdev->dev.parent->of_node, > + "allwinner,ts-attached"); > > regmap_write(info->regmap, SUNXI_GPADC_TP_CTRL0, SUNXI_GPADC_FS_DIV(7) | >SUNXI_GPADC_ADC_CLK_DIVIDER(2) | SUNXI_GPADC_T_ACQ(63)); > @@ -305,6 +424,8 @@ static int sunxi_gpadc_probe(struct platform_device *pdev) > else > regmap_write(info->regmap, SUNXI_GPADC_TP_CTRL1, >SUNXI_GPADC_TP_MODE_EN); > + regmap_write(info->regmap, SUNXI_GPADC_TP_CTRL2, > + SUNXI_GPADC_TP_SENSITIVE_ADJUST(15)); > regmap_write(info->regmap, SUNXI_GPADC_TP_CTRL3, SUNXI_GPADC_FILTER_EN | >SUNXI_GPADC_FILTER_TYPE(1)); > regmap_write(info->regmap, SUNXI_GPADC_TP_TPR, > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH] iio: humidity: hdc100x: add triggered buffer support for HDC100X
HDC100X_REG_CONFIG_ACQ_MODE); > + mutex_unlock(&data->lock); > + if (ret) > + return ret; > + > + return iio_triggered_buffer_postenable(indio_dev); > +} > + > +static int hdc100x_buffer_predisable(struct iio_dev *indio_dev) > +{ > + struct hdc100x_data *data = iio_priv(indio_dev); > + int ret; > + > + /* First detach poll func, then reset ACQ mode. OK to disable buffer */ > + ret = iio_triggered_buffer_predisable(indio_dev); > + if (ret) > + return ret; > + > + mutex_lock(&data->lock); > + ret = hdc100x_update_config(data, HDC100X_REG_CONFIG_ACQ_MODE, 0); > + mutex_unlock(&data->lock); > + return ret; > +} > + > +static const struct iio_buffer_setup_ops hdc_buffer_setup_ops = { > + .preenable = NULL, > + .postenable = hdc100x_buffer_postenable, > + .predisable = hdc100x_buffer_predisable, > + .postdisable = NULL, > +}; > + > +static irqreturn_t hdc100x_trigger_handler(int irq, void *p) > +{ > + struct iio_poll_func *pf = p; > + struct iio_dev *indio_dev = pf->indio_dev; > + struct hdc100x_data *data = iio_priv(indio_dev); > + struct i2c_client *client = data->client; > + int delay = data->adc_int_us[0] + data->adc_int_us[1]; > + int ret, i, bit; > + u8 recv_buf[4]; > + s16 push_buf[8]; /* 2x s16 + padding + 8 byte timestamp */ two different buffers really needed? for ALL_CHANNEL_MASK can read directly to push_buf; why is this a special case anyway? if only first channel set, can read directly to push_buf if only second channel set, have to move data within push_buf don't do endianness conversion if not strictly needed > + > + /* dual read starts at temp register */ > + ret = i2c_smbus_write_byte(client, HDC100X_REG_TEMP); > + if (ret < 0) { > + dev_err(&client->dev, "cannot start measurement\n"); > + goto err; > + } > + usleep_range(delay, delay + 1000); > + > + ret = i2c_master_recv(client, recv_buf, sizeof(recv_buf)); > + if (ret < 0) { > + dev_err(&client->dev, "cannot read sensor data\n"); > + goto err; > + } > + if (*indio_dev->active_scan_mask == HDC100X_ALL_CHANNEL_MASK) { > + push_buf[0] = recv_buf[0] << 8 | recv_buf[1]; > + push_buf[1] = recv_buf[2] << 8 | recv_buf[3]; > + } else { > + i = 0; > + for_each_set_bit(bit, indio_dev->active_scan_mask, loop probably overkill for exactly two channels > + indio_dev->masklength) { > + push_buf[i] = bit ? (recv_buf[2] << 8 | recv_buf[3]) > + : (recv_buf[0] << 8 | recv_buf[1]); > + i++; > + } > + } > + iio_push_to_buffers_with_timestamp(indio_dev, push_buf, > +iio_get_time_ns(indio_dev)); > +err: > + iio_trigger_notify_done(indio_dev->trig); > + > + return IRQ_HANDLED; > +} > + > static const struct iio_info hdc100x_info = { > .read_raw = hdc100x_read_raw, > .write_raw = hdc100x_write_raw, > @@ -271,9 +379,10 @@ static int hdc100x_probe(struct i2c_client *client, > { > struct iio_dev *indio_dev; > struct hdc100x_data *data; > + int ret; > > - if (!i2c_check_functionality(client->adapter, > - I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE)) > + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA | > + I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C)) > return -EOPNOTSUPP; > > indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); > @@ -296,8 +405,30 @@ static int hdc100x_probe(struct i2c_client *client, > /* be sure we are in a known state */ > hdc100x_set_it_time(data, 0, hdc100x_int_time[0][0]); > hdc100x_set_it_time(data, 1, hdc100x_int_time[1][0]); > + hdc100x_update_config(data, HDC100X_REG_CONFIG_ACQ_MODE, 0); > + > + ret = iio_triggered_buffer_setup(indio_dev, NULL, > + hdc100x_trigger_handler, > + &hdc_buffer_setup_ops); > + if (ret < 0) { > + dev_err(&client->dev, "iio triggered buffer setup failed\n"); > + return ret; > + } > + ret = iio_device_register(indio_dev); > + if (ret < 0) > + iio_triggered_buffer_cleanup(indio_dev); > + > + return ret; > +} > + > +static int hdc100x_remove(struct i2c_client *client) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(client); > + > + iio_device_unregister(indio_dev); > + iio_triggered_buffer_cleanup(indio_dev); > > - return devm_iio_device_register(&client->dev, indio_dev); > + return 0; > } > > static const struct i2c_device_id hdc100x_id[] = { > @@ -311,6 +442,7 @@ static struct i2c_driver hdc100x_driver = { > .name = "hdc100x", > }, > .probe = hdc100x_probe, > + .remove = hdc100x_remove, > .id_table = hdc100x_id, > }; > module_i2c_driver(hdc100x_driver); > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH 1/1] iio: vcnl4000: Add IR current adjust support
> > > > comments below; nice addition > > > > > > > > it seems this patch clashes with the recent changes to this driver in > > > > iio-testing; can you rebase on top please? > > > > > > Where is iio-testing? I couldn't found it in linux. > > > > https://git.kernel.org/cgit/linux/kernel/git/jic23/iio.git/log/?h=testing > > Means we have to do development here and this testing branch will get > merged to main branch later. yes, Jonathan will queue up stuff in testing, then send to GregHK, and then to mainline p. -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH 2/2] iio: accel: Add support for Domintech DMARD06 accelerometer
rr(&client->dev, "I2C check functionality failed\n"); > + return -ENXIO; > + } > + > + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dmard06)); > + if (!indio_dev) { > + dev_err(&client->dev, "Failed to allocate iio device\n"); > + return -ENOMEM; > + } > + > + dmard06 = iio_priv(indio_dev); > + dmard06->client = client; > + > + error = dmard06_read_chip_id(dmard06); > + if (error) > + return error; > + > + i2c_set_clientdata(client, indio_dev); > + indio_dev->dev.parent = &client->dev; > + indio_dev->name = DMARD06_DRV_NAME; > + indio_dev->modes = INDIO_DIRECT_MODE; > + indio_dev->channels = dmard06_channels; > + indio_dev->num_channels = ARRAY_SIZE(dmard06_channels); > + indio_dev->info = &dmard06_info; > + > + return devm_iio_device_register(&client->dev, indio_dev); > +} > + > +static const struct i2c_device_id dmard06_id[] = { > + { "dmard06", 0 }, > + { } > +}; > +MODULE_DEVICE_TABLE(i2c, dmard06_id); > + > +static const struct of_device_id dmard06_of_match[] = { > + { .compatible = "domintech,dmard06" }, > + { } > +}; > +MODULE_DEVICE_TABLE(of, dmard06_of_match); > + > +static struct i2c_driver dmard06_driver = { > + .probe = dmard06_probe, > + .id_table = dmard06_id, > + .driver = { > + .name = DMARD06_DRV_NAME, > + .of_match_table = of_match_ptr(dmard06_of_match), > + }, > +}; > +module_i2c_driver(dmard06_driver); > + > +MODULE_AUTHOR("Aleksei Mamlin "); > +MODULE_DESCRIPTION("Domintech DMARD06 accelerometer driver"); > +MODULE_LICENSE("GPL v2"); > -- Peter Meerwald-Stadler +43-664-218 (mobile)
RE: [PATCH V1 1/1] iio: as6200: add AS6200 temperature sensor driver from ams AG
vember 2015 12:52 > To: Florian Lobmaier ; ji...@kernel.org > Cc: Elitsa Polizoeva ; knaac...@gmx.de; > pme...@pmeerw.net; linux-kernel@vger.kernel.org; linux-...@vger.kernel.org > Subject: Re: [PATCH V1 1/1] iio: as6200: add AS6200 temperature sensor driver > from ams AG > > On 11/10/2015 10:38 AM, Florian Lobmaier wrote: > > The AS6200 is a compact temperature sensor chip with I2C interface. > > > > Add a driver to support the AS6200 temperature sensor. > > > > Signed-off-by: Elitsa Polizoeva > > Signed-off-by: Florian Lobmaier > > Hi, > > Thanks for the patch. > > As Peter already said this patch introduces a lot of custom ABI, none of > which is documented and most of which is probably not acceptable since. The > whole idea of a framework is that you expose the capabilities of a device > through a standard device independent ABI, this allows to write generic > applications and libraries that can manage the devices through this ABI. > This allows to leverage existing developments rather than starting from > scratch each time. If your device uses a complete custom ABI there is not > much point of having a driver in the first place since any application needs > device specific knowledge anyway to talk to the driver, in this case you > could just directly implement the I2C communication in the application. > > Please also consider reading and following Documentation/CodingStyle > > [...] > > +static int as6200_read_reg(struct i2c_client *client, > > +u8 reg_num, u16 *reg_val) > > +{ > > +int err = 0; > > +char tx_buf[1]; > > +char rx_buf[2]; > > + > > +if ((reg_num >= 0) & (reg_num <= 3)) { tx_buf[0] = reg_num; err = > > +i2c_master_send(client, tx_buf, 1); if (err == 1) err = > > +i2c_master_recv(client, rx_buf, 2); > > This is not thread safe. Another thread could interrupt between > i2c_master_send() and i2c_master_recv() and cause undefined behavior. Use > i2c_smbus_read_word_swapped() which makes sure that the I2C communication > happens atomically. > > > +if (err == 2) { > > +*reg_val = rx_buf[0]; > > +*reg_val = *reg_val << 8; > > +*reg_val = *reg_val | rx_buf[1]; > > +} > > +return err; > > +} else { > > +return -EINVAL; > > +} > > +} > [...] > > + > > +static irqreturn_t alert_isr(int irq, void *dev_id) { > > +dev_warn(dev_id, "Temperature outside of limits!"); > > Please use IIO threshold events for this. Such out-of-band communication is > really not acceptable. > > > +return IRQ_HANDLED; > > +} > > + > > +static int setupIRQ(struct iio_dev *indio_dev, bool set_gpio, u8 pol) > > +{ int err; struct as6200_data *data = iio_priv(indio_dev); struct > > +device *dev = &data->client->dev; int gpio = -1; int irq_num; int > > +irq_trig; > > + > > +if (pol == 1) > > +irq_trig = IRQF_TRIGGER_RISING; > > +else > > +irq_trig = IRQF_TRIGGER_FALLING; > > + > > +if (set_gpio) { > > +gpio = of_get_named_gpio_flags(dev->of_node, > > +"as6200,irq-gpio", 0, 0); > > +err = gpio_request(gpio, "as6200_irq"); if (err) { dev_info(dev, "%s: > > +requesting gpio %d failed\n", as6200_id[0].name, gpio); return err; } > > +err = gpio_direction_input(gpio); if (err) { dev_info(dev, "%s: gpio > > +%d cannot apply direction\n", as6200_id[0].name, gpio); return err; } > > +} irq_num = gpio_to_irq(gpio); dev_info(dev, "%s: registering for IRQ > > +%d\n", as6200_id[0].name, irq_num); > > Please drop all these dev_info(). That's just noise in the boot log, imagine > every driver did this you wouldn't be able to spot the critical information. > > > +err = request_irq(irq_num, alert_isr, irq_trig, as6200_id[0].name, > > +dev); > > Don't do all the GPIO translation. This pin is only used as a interrupt, so > specify it directly as an interrupt and use it that way as well. > > > +if (err) { > > +dev_info(dev, "%s: error requesting irq %d\n", as6200_id[0].name, > > +err); > > For errors use dev_err. Also the as6200_id[0].name is redundant since > dev_info/dev_err already prefixes the message with the device name. > > > +return err; > > +} > > +dev_info(dev, "%s: registered for IRQ %d\n", as6200_id[0].name, > > +irq_num); mutex_lock(&data->update_lock); > > +data->irqn = irq_num; > > +mutex_unlock(&data->update_lock); > > What exactly is protect by that mutex here? > > > + > > +return 0; > > +} > > + > [...] > > +if (err == 0) { > > +if ((val < -40) | (val > 150)) { > > +dev_info(&client->dev, > > +"Value for THIGH is invalid min = -40%cC, max = 150°C, val = %d°C", > > +(unsigned char)(248), val); > > Please no out-of-band error reporting. > > > +return count; > > +} > > +val = (val * 1) / 625; > > +reg_val = val << 4; > [...] > > +static int as6200_probe(struct i2c_client *client, const struct > > +i2c_device_id *id) { struct iio_dev *indio_dev = NULL; struct > > +as6200_data *data = NULL; > > No need to initialize those here with dummy, this will just hide warnings in > case you forgot to initialize them with actual data. > > > + > > +indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); > > + > > +if (!indio_dev) > > +return -ENOMEM; > > + > > +data = iio_priv(indio_dev); > > +i2c_set_clientdata(client, indio_dev); > > +data->client = client; > > + > > +indio_dev->dev.parent = &client->dev; indio_dev->name = > > +dev_name(&client->dev); > > use id->name for this. dev_name() contains things like the I2C bus and device > address, etc. Whereas the IIO device name should describe the type of device. > > > +indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &as6200_info; > > + > > +indio_dev->channels = as6200_channels; indio_dev->num_channels = > > +ARRAY_SIZE(as6200_channels); > > + > > +initClientData(data); > > +mutex_init(&data->update_lock); > > +setupIRQ(indio_dev, true, 0); > > + > > +return iio_device_register(indio_dev); > > Error handling is missing here, you need to free the resources that were > acquired in case of an error. > > > +} > > + > [...] > > > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH v3 1/4] iio: adc: rockchip_saradc: reset saradc controller before programming it
> SARADC controller needs to be reset before programming it, otherwise > it will not function properly. nitpicking on wording below > Signed-off-by: Caesar Wang > Cc: Jonathan Cameron > Cc: Heiko Stuebner > Cc: Rob Herring > Cc: linux-...@vger.kernel.org > Cc: linux-rockc...@lists.infradead.org > Tested-by: Guenter Roeck > > --- > > Changes in v3: > - %s/devm_reset_control_get_optional()/devm_reset_control_get() > - add Guente's test tag. > > Changes in v2: > - Make the reset as an optional property, since it should work > with old devicetrees as well. > > .../bindings/iio/adc/rockchip-saradc.txt | 7 + > drivers/iio/adc/Kconfig| 1 + > drivers/iio/adc/rockchip_saradc.c | 30 > ++ > 3 files changed, 38 insertions(+) > > diff --git a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt > b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt > index bf99e2f..205593f 100644 > --- a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt > +++ b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt > @@ -16,6 +16,11 @@ Required properties: > - vref-supply: The regulator supply ADC reference voltage. > - #io-channel-cells: Should be 1, see ../iio-bindings.txt > > +Optional properties: > +- resets: Must contain an entry for each entry in reset-names if need support > + this option. See ../reset/reset.txt for details. '... if need support this option.' doesn't sound right, maybe simply: '... if needed.' or drop this clause. > +- reset-names: Must include the name "saradc-apb". > + > Example: > saradc: saradc@2006c000 { > compatible = "rockchip,saradc"; > @@ -23,6 +28,8 @@ Example: > interrupts = ; > clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>; > clock-names = "saradc", "apb_pclk"; > + resets = <&cru SRST_SARADC>; > + reset-names = "saradc-apb"; > #io-channel-cells = <1>; > vref-supply = <&vcc18>; > }; > diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig > index 1de31bd..7675772 100644 > --- a/drivers/iio/adc/Kconfig > +++ b/drivers/iio/adc/Kconfig > @@ -389,6 +389,7 @@ config QCOM_SPMI_VADC > config ROCKCHIP_SARADC > tristate "Rockchip SARADC driver" > depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST) > + depends on RESET_CONTROLLER > help > Say yes here to build support for the SARADC found in SoCs from > Rockchip. > diff --git a/drivers/iio/adc/rockchip_saradc.c > b/drivers/iio/adc/rockchip_saradc.c > index f9ad6c2..85d7012 100644 > --- a/drivers/iio/adc/rockchip_saradc.c > +++ b/drivers/iio/adc/rockchip_saradc.c > @@ -21,6 +21,8 @@ > #include > #include > #include > +#include > +#include > #include > #include > > @@ -53,6 +55,7 @@ struct rockchip_saradc { > struct clk *clk; > struct completion completion; > struct regulator*vref; > + struct reset_control*reset; > const struct rockchip_saradc_data *data; > u16 last_val; > }; > @@ -190,6 +193,16 @@ static const struct of_device_id rockchip_saradc_match[] > = { > }; > MODULE_DEVICE_TABLE(of, rockchip_saradc_match); > > +/** > + * Reset SARADC Controller. > + */ > +static void rockchip_saradc_reset_controller(struct reset_control *reset) > +{ > + reset_control_assert(reset); > + usleep_range(10, 20); > + reset_control_deassert(reset); > +} > + > static int rockchip_saradc_probe(struct platform_device *pdev) > { > struct rockchip_saradc *info = NULL; > @@ -218,6 +231,20 @@ static int rockchip_saradc_probe(struct platform_device > *pdev) > if (IS_ERR(info->regs)) > return PTR_ERR(info->regs); > > + /* > + * The reset should be an optional property, as it should work > + * with old devicetrees as well > + */ > + info->reset = devm_reset_control_get(&pdev->dev, "saradc-apb"); > + if (IS_ERR(info->reset)) { > + ret = PTR_ERR(info->reset); > + if (ret != -ENOENT) > + return ret; > + > + dev_dbg(&pdev->dev, "no reset control found\n"); > + info->reset = NULL; > + } > + > init_completion(&info->completion); > > irq = platform_get_irq(pdev, 0); > @@ -252,6 +279,9 @@ static int rockchip_saradc_probe(struct platform_device > *pdev) > return PTR_ERR(info->vref); > } > > + if (info->reset) > + rockchip_saradc_reset_controller(info->reset); > + > /* >* Use a default value for the converter clock. >* This may become user-configurable in the future. > -- Peter Meerwald-Stadler +43-664-218 (mobile)
RE: [PATCH V2 1/1] iio: as6200: add AS6200 temperature sensor driver from ams AG
+ as6200_set_im, 0); > +static IIO_DEVICE_ATTR(pol, S_IWUSR | S_IRUGO, as6200_show_pol, > + as6200_set_pol, 0); > +static IIO_DEVICE_ATTR(cf, S_IWUSR | S_IRUGO, as6200_show_cf, > + as6200_set_cf, 0); > +static IIO_DEVICE_ATTR(ss, S_IWUSR | S_IRUGO, as6200_show_ss, > + as6200_set_ss, 0); > + > +static IIO_CONST_ATTR(sampling_frequency_available, "4 1 0.25 0.125"); > + > +static struct attribute *as6200_attrs[] = { > + &iio_dev_attr_thigh.dev_attr.attr, > + &iio_dev_attr_tlow.dev_attr.attr, > + &iio_dev_attr_thigh_reg.dev_attr.attr, > + &iio_dev_attr_tlow_reg.dev_attr.attr, > + &iio_dev_attr_config.dev_attr.attr, > + &iio_dev_attr_al.dev_attr.attr, > + &iio_dev_attr_sm.dev_attr.attr, > + &iio_dev_attr_im.dev_attr.attr, > + &iio_dev_attr_pol.dev_attr.attr, > + &iio_dev_attr_cf.dev_attr.attr, > + &iio_dev_attr_ss.dev_attr.attr, > + &iio_const_attr_sampling_frequency_available.dev_attr.attr, > + NULL, > +}; > + > +static struct attribute_group as6200_attr_group = { > + .attrs = as6200_attrs, > +}; > + > +static const struct iio_chan_spec as6200_channels[] = { > + { > + .type = IIO_TEMP, > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | > + BIT(IIO_CHAN_INFO_PROCESSED) | > + BIT(IIO_CHAN_INFO_SCALE), > + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), > + } > +}; > + > +static const struct iio_info as6200_info = { > + .read_raw = as6200_read_raw, > + .write_raw = as6200_write_raw, > + .attrs = &as6200_attr_group, > + .driver_module = THIS_MODULE, > +}; > + > +static int as6200_i2c_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + struct iio_dev *indio_dev; > + struct as6200_data *data; > + int ret; > + > + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); > + if (!indio_dev) > + return -ENOMEM; > + > + data = iio_priv(indio_dev); > + i2c_set_clientdata(client, indio_dev); > + data->client = client; > + > + data->regmap = devm_regmap_init_i2c(client, &as6200_regmap_config); > + if (IS_ERR(data->regmap)) { > + ret = PTR_ERR(data->regmap); > + dev_err(&client->dev, "regmap init failed: %d\n", ret); > + return ret; > + } > + > + indio_dev->dev.parent = &client->dev; > + indio_dev->name = id->name; > + indio_dev->modes = INDIO_DIRECT_MODE; > + indio_dev->info = &as6200_info; > + > + indio_dev->channels = as6200_channels; > + indio_dev->num_channels = ARRAY_SIZE(as6200_channels); > + > + ret = as6200_setup_irq(indio_dev, true, 0); > + if (ret < 0) if setup_irq() fails, irq has not been requested and no cleanup is needed > + goto cleanup_irq; > + > + return iio_device_register(indio_dev); however, if iio_device_register() fails, the irq needs to be freed > + > +cleanup_irq: > + free_irq(client->irq, &client->dev); > + > + return ret; > +} > + > +static int as6200_i2c_remove(struct i2c_client *client) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(client); > + > + iio_device_unregister(indio_dev); > + free_irq(client->irq, &client->dev); > + return 0; > +} > + > +static const struct of_device_id as6200_of_match[] = { > + { .compatible = "ams,as6200", }, > + {}, > +}; > +MODULE_DEVICE_TABLE(of, as6200_of_match); > + > + > +static const struct i2c_device_id as6200_i2c_id[] = { > + { "as6200", 0 }, > + { } > +}; > +MODULE_DEVICE_TABLE(i2c, as6200_id); > + > +static struct i2c_driver as6200_i2c_driver = { > + .driver = { > + .name = "as6200", > + .owner = THIS_MODULE, > + .of_match_table = as6200_of_match, > + }, > + .probe = as6200_i2c_probe, > + .remove = as6200_i2c_remove, > + .id_table = as6200_i2c_id, > +}; > + > +module_i2c_driver(as6200_i2c_driver); > + > +MODULE_DESCRIPTION("ams AS6200 temperature sensor"); > +MODULE_AUTHOR("Elitsa Polizoeva "); > +MODULE_AUTHOR("Florian Lobmaier "); > +MODULE_LICENSE("GPL"); > > -Original Message- > From: Peter Meerwald-Stadler [mailto:pme...@pmeerw.net] > Sent: Dienstag, 21. Juni 2016 15:12 > To: Florian Lobmaier > Cc: ji...@kernel.org; Elitsa Polizoeva ; > knaac...@gmx.de; linux-kernel@v
RE: [PATCH V3 1/1] iio: as6200: add AS6200 temperature sensor driver from ams AG
uct iio_dev *indio_dev = dev_to_iio_dev(dev); > + struct as6200_data *data = iio_priv(indio_dev); > + struct i2c_client *client = data->client; > + int err; > + u16 val; > + > + err = kstrtou16(buf, 0, &val); > + if (err == 0) { > + switch (val) { > + case 1: > + case 2: > + case 4: > + case 6: > + err = regmap_update_bits(data->regmap, > + AS6200_REG_CONFIG, AS6200_CONFIG_CF_MASK, > + val << AS6200_CONFIG_CF_SHIFT); > + if (err < 0) > + return err; > + break; > + default: > + dev_info(&client->dev, > + "Value for CF field invalid, val = %hx", val); > + return count; > + } > + } else > + dev_info(&client->dev, > + "Converting value for CF field failed, err = %hx", > + err); error handing still missing? > + return count; > +} > + > +static ssize_t as6200_set_ss(struct device *dev, > + struct device_attribute *attr, const char *buf, size_t count) > +{ > + struct iio_dev *indio_dev = dev_to_iio_dev(dev); > + struct as6200_data *data = iio_priv(indio_dev); > + struct i2c_client *client = data->client; > + int err; > + u16 val; > + > + err = kstrtou16(buf, 0, &val); > + if (err == 0) { > + err = regmap_update_bits(data->regmap, AS6200_REG_CONFIG, > + AS6200_CONFIG_SS_MASK, val << AS6200_CONFIG_SS_SHIFT); > + if (err < 0) > + return err; > + } else > + dev_info(&client->dev, > + "Converting value for SS field failed, err = %hx", > + err); > + return count; > +} > + > +static IIO_DEVICE_ATTR(thigh, S_IWUSR | S_IRUGO, as6200_show_thigh, > + as6200_set_thigh, 0); > +static IIO_DEVICE_ATTR(tlow, S_IWUSR | S_IRUGO, as6200_show_tlow, > + as6200_set_tlow, 0); > +static IIO_DEVICE_ATTR(thigh_reg, S_IWUSR | S_IRUGO, as6200_show_thigh_reg, > + as6200_set_thigh_reg, 0); > +static IIO_DEVICE_ATTR(tlow_reg, S_IWUSR | S_IRUGO, as6200_show_tlow_reg, > + as6200_set_tlow_reg, 0); > +static IIO_DEVICE_ATTR(al, S_IRUGO, as6200_show_al, NULL, 0); > +static IIO_DEVICE_ATTR(sm, S_IWUSR | S_IRUGO, as6200_show_sm, > + as6200_set_sm, 0); > +static IIO_DEVICE_ATTR(im, S_IWUSR | S_IRUGO, as6200_show_im, > + as6200_set_im, 0); > +static IIO_DEVICE_ATTR(pol, S_IWUSR | S_IRUGO, as6200_show_pol, > + as6200_set_pol, 0); > +static IIO_DEVICE_ATTR(cf, S_IWUSR | S_IRUGO, as6200_show_cf, > + as6200_set_cf, 0); > +static IIO_DEVICE_ATTR(ss, S_IWUSR | S_IRUGO, as6200_show_ss, > + as6200_set_ss, 0); > + > +static IIO_CONST_ATTR(sampling_frequency_available, "4 1 0.25 0.125"); > + > +static struct attribute *as6200_attrs[] = { > + &iio_dev_attr_thigh.dev_attr.attr, > + &iio_dev_attr_tlow.dev_attr.attr, > + &iio_dev_attr_thigh_reg.dev_attr.attr, > + &iio_dev_attr_tlow_reg.dev_attr.attr, > + &iio_dev_attr_al.dev_attr.attr, > + &iio_dev_attr_sm.dev_attr.attr, > + &iio_dev_attr_im.dev_attr.attr, > + &iio_dev_attr_pol.dev_attr.attr, > + &iio_dev_attr_cf.dev_attr.attr, > + &iio_dev_attr_ss.dev_attr.attr, > + &iio_const_attr_sampling_frequency_available.dev_attr.attr, > + NULL, > +}; > + > +static struct attribute_group as6200_attr_group = { > + .attrs = as6200_attrs, > +}; > + > +static const struct iio_chan_spec as6200_channels[] = { > + { > + .type = IIO_TEMP, > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | > + BIT(IIO_CHAN_INFO_PROCESSED) | > + BIT(IIO_CHAN_INFO_SCALE), > + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), > + } > +}; > + > +static const struct iio_info as6200_info = { > + .read_raw = as6200_read_raw, > + .write_raw = as6200_write_raw, > + .attrs = &as6200_attr_group, > + .driver_module = THIS_MODULE, > +}; > + > +static int as6200_i2c_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + struct iio_dev *indio_dev; > + struct as6200_data *data; > + int ret; > + > + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); > + if (!indio_dev) > + return -E
Re: [PATCH v2] leds: Introduce userspace leds driver
0) > + goto out; > + > + udev->state = ULEDS_STATE_REGISTERED; > + ret = count; > + > +out: > + mutex_unlock(&udev->mutex); > + > + return ret; > +} > + > +static ssize_t uleds_read(struct file *file, char __user *buffer, size_t > count, > + loff_t *ppos) > +{ > + struct uleds_device *udev = file->private_data; > + ssize_t retval; > + > + if (count == 0) > + return 0; > + > + if (count != 1) > + return -EINVAL; > + > + do { > + retval = mutex_lock_interruptible(&udev->mutex); > + if (retval) > + return retval; > + > + if (udev->state != ULEDS_STATE_REGISTERED) { > + retval = -ENODEV; > + } else if (!udev->new_data && (file->f_flags & O_NONBLOCK)) { > + retval = -EAGAIN; > + } else { > + retval = copy_to_user(buffer, &udev->brightness, 1); > + udev->new_data = 0; > + retval = 1; > + } > + > + mutex_unlock(&udev->mutex); > + > + if (retval || count == 0) > + break; > + > + if (!(file->f_flags & O_NONBLOCK)) > + retval = wait_event_interruptible(udev->waitq, > + udev->new_data || > + udev->state != ULEDS_STATE_REGISTERED); > + } while (retval == 0); > + > + return retval; > +} > + > +static unsigned int uleds_poll(struct file *file, poll_table *wait) > +{ > + struct uleds_device *udev = file->private_data; > + > + poll_wait(file, &udev->waitq, wait); > + > + if (udev->new_data) > + return POLLIN | POLLRDNORM; > + > + return 0; > +} > + > +static int uleds_release(struct inode *inode, struct file *file) > +{ > + struct uleds_device *udev = file->private_data; > + > + if (udev->state == ULEDS_STATE_REGISTERED) { > + udev->state = ULEDS_STATE_UNKNOWN; > + led_classdev_unregister(&udev->led_cdev); > + } > + kfree(udev); > + > + return 0; > +} > + > +static const struct file_operations uleds_fops = { > + .owner = THIS_MODULE, > + .open = uleds_open, > + .release= uleds_release, > + .read = uleds_read, > + .write = uleds_write, > + .poll = uleds_poll, > + .llseek = no_llseek, > +}; > + > +static struct miscdevice uleds_misc = { > + .fops = &uleds_fops, > + .minor = MISC_DYNAMIC_MINOR, > + .name = ULEDS_NAME, > +}; > + > +static int __init uleds_init(void) > +{ > + return misc_register(&uleds_misc); > +} > +module_init(uleds_init); > + > +static void __exit uleds_exit(void) > +{ > + misc_deregister(&uleds_misc); > +} > +module_exit(uleds_exit); > + > +MODULE_AUTHOR("David Lechner "); > +MODULE_DESCRIPTION("Userspace driver for leds subsystem"); > +MODULE_LICENSE("GPL"); > diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild > index 185f8ea..416f5e6 100644 > --- a/include/uapi/linux/Kbuild > +++ b/include/uapi/linux/Kbuild > @@ -421,6 +421,7 @@ header-y += udp.h > header-y += uhid.h > header-y += uinput.h > header-y += uio.h > +header-y += uleds.h > header-y += ultrasound.h > header-y += un.h > header-y += unistd.h > diff --git a/include/uapi/linux/uleds.h b/include/uapi/linux/uleds.h > new file mode 100644 > index 000..e78ed46 > --- /dev/null > +++ b/include/uapi/linux/uleds.h > @@ -0,0 +1,23 @@ > +/* > + * Userspace driver support for leds subsystem > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + */ > +#ifndef _UAPI__ULEDS_H_ > +#define _UAPI__ULEDS_H_ > + > +#define ULEDS_MAX_NAME_SIZE 80 > + > +struct uleds_user_dev { > + char name[ULEDS_MAX_NAME_SIZE]; > +}; > + > +#endif /* _UAPI__ULEDS_H_ */ > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [alsa-devel] [PATCH 4/4] ASoC: da7213: Improve 32KHz mode PLL locking
> To aid PLL in locking on to a 32KHz MCLK, some register mods > are made during PLL configuration, and when enabling the DAI, > to achieve the full range of sample rates. thanks for the patch series; we are about to test... some comments below > Signed-off-by: Adam Thomson > --- > sound/soc/codecs/da7213.c | 25 - > 1 file changed, 24 insertions(+), 1 deletion(-) > > diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c > index 79b8324..095fe40 100644 > --- a/sound/soc/codecs/da7213.c > +++ b/sound/soc/codecs/da7213.c > @@ -750,11 +750,18 @@ static int da7213_dai_event(struct snd_soc_dapm_widget > *w, > snd_soc_update_bits(codec, DA7213_PC_COUNT, > DA7213_PC_FREERUN_MASK, 0); > > - /* Slave mode, if SRM not enabled no need for status checks */ > + /* If SRM not enabled then nothing more to do */ > pll_ctrl = snd_soc_read(codec, DA7213_PLL_CTRL); > if (!(pll_ctrl & DA7213_PLL_SRM_EN)) > return 0; > > + /* Assist 32KHz mode PLL lock */ > + if (pll_ctrl & DA7213_PLL_32K_MODE) { these registers cannot not found in the datasheet; maybe add descriptive #defines in da7213.h > + snd_soc_write(codec, 0xF0, 0x8B); > + snd_soc_write(codec, 0xF2, 0x03); > + snd_soc_write(codec, 0xF0, 0x00); > + } > + > /* Check SRM has locked */ > do { > pll_status = snd_soc_read(codec, DA7213_PLL_STATUS); > @@ -771,6 +778,14 @@ static int da7213_dai_event(struct snd_soc_dapm_widget > *w, > > return 0; > case SND_SOC_DAPM_POST_PMD: > + /* Revert 32KHz PLL lock udpates if applied previously */ > + pll_ctrl = snd_soc_read(codec, DA7213_PLL_CTRL); > + if (pll_ctrl & DA7213_PLL_32K_MODE) { > + snd_soc_write(codec, 0xF0, 0x8B); > + snd_soc_write(codec, 0xF2, 0x01); > + snd_soc_write(codec, 0xF0, 0x00); > + } > + > /* PC free-running */ > snd_soc_update_bits(codec, DA7213_PC_COUNT, > DA7213_PC_FREERUN_MASK, > @@ -1428,6 +1443,14 @@ static int da7213_set_dai_pll(struct snd_soc_dai > *codec_dai, int pll_id, > DA7213_PLL_INDIV_MASK | DA7213_PLL_MODE_MASK, > pll_ctrl); > > + /* Assist 32KHz mode PLL lock */ > + if (source == DA7213_SYSCLK_PLL_32KHZ) { > + snd_soc_write(codec, 0xF0, 0x8B); > + snd_soc_write(codec, 0xF1, 0x03); > + snd_soc_write(codec, 0xF1, 0x01); > + snd_soc_write(codec, 0xF0, 0x00); > + } > + > return 0; > } -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [alsa-devel] [PATCH 3/4] ASoC: da7213: Refactor sysclk(), pll() functions to improve handling
> Currently the handling of the PLL in the driver is a little clunky, > and not ideal for all modes. This patch updates the code to make it > cleaner and more sensible for the various PLL states. > > Key items of note are: > - MCLK squaring is now handled directly as part of the sysclk() >function, removing the need for a private flag to set this feature. > - All PLL modes are defined as an enum, and are handled as a case >statement in pll() function to clean up configuration. This also >removes any need for a private flag for SRM. > - For 32KHz mode, checks are made on codec master mode and correct >MCLK rates, to avoid incorrect usage of PLL for this operation. > - For 32KHz mode, SRM flag now correctly enabled and fout set to >sensible value to achieve appropriate PLL dividers. thanks, looks good Tested-by: Peter Meerwald-Stadler nitpick: add extra newline at the end of if (da7213->mclk_rate == 32768) block > Signed-off-by: Adam Thomson > --- > sound/soc/codecs/da7213.c | 85 > +++ > sound/soc/codecs/da7213.h | 12 --- > 2 files changed, 57 insertions(+), 40 deletions(-) > > diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c > index 7701f4e..79b8324 100644 > --- a/sound/soc/codecs/da7213.c > +++ b/sound/soc/codecs/da7213.c > @@ -1297,10 +1297,13 @@ static int da7213_set_dai_sysclk(struct snd_soc_dai > *codec_dai, > > switch (clk_id) { > case DA7213_CLKSRC_MCLK: > - da7213->mclk_squarer_en = false; > + snd_soc_update_bits(codec, DA7213_PLL_CTRL, > + DA7213_PLL_MCLK_SQR_EN, 0); > break; > case DA7213_CLKSRC_MCLK_SQR: > - da7213->mclk_squarer_en = true; > + snd_soc_update_bits(codec, DA7213_PLL_CTRL, > + DA7213_PLL_MCLK_SQR_EN, > + DA7213_PLL_MCLK_SQR_EN); > break; > default: > dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id); > @@ -1324,7 +1327,7 @@ static int da7213_set_dai_sysclk(struct snd_soc_dai > *codec_dai, > return 0; > } > > -/* Supported PLL input frequencies are 5MHz - 54MHz. */ > +/* Supported PLL input frequencies are 32KHz, 5MHz - 54MHz. */ > static int da7213_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, > int source, unsigned int fref, unsigned int fout) > { > @@ -1336,22 +1339,26 @@ static int da7213_set_dai_pll(struct snd_soc_dai > *codec_dai, int pll_id, > u32 freq_ref; > u64 frac_div; > > - /* Reset PLL configuration */ > - snd_soc_write(codec, DA7213_PLL_CTRL, 0); > - > - pll_ctrl = 0; > - > /* Workout input divider based on MCLK rate */ > if (da7213->mclk_rate == 32768) { > + if (!da7213->master) { > + dev_err(codec->dev, > + "32KHz only valid if codec is clock master\n"); > + return -EINVAL; > + } > + > /* 32KHz PLL Mode */ > indiv_bits = DA7213_PLL_INDIV_9_TO_18_MHZ; > indiv = DA7213_PLL_INDIV_9_TO_18_MHZ_VAL; > + source = DA7213_SYSCLK_PLL_32KHZ; > freq_ref = 375; > - pll_ctrl |= DA7213_PLL_32K_MODE; > + > } else { > - /* 5 - 54MHz MCLK */ > if (da7213->mclk_rate < 500) { > - goto pll_err; > + dev_err(codec->dev, > + "PLL input clock %d below valid range\n", > + da7213->mclk_rate); > + return -EINVAL; > } else if (da7213->mclk_rate <= 900) { > indiv_bits = DA7213_PLL_INDIV_5_TO_9_MHZ; > indiv = DA7213_PLL_INDIV_5_TO_9_MHZ_VAL; > @@ -1365,32 +1372,44 @@ static int da7213_set_dai_pll(struct snd_soc_dai > *codec_dai, int pll_id, > indiv_bits = DA7213_PLL_INDIV_36_TO_54_MHZ; > indiv = DA7213_PLL_INDIV_36_TO_54_MHZ_VAL; > } else { > - goto pll_err; > + dev_err(codec->dev, > + "PLL input clock %d above valid range\n", > + da7213->mclk_rate); > + return -EINVAL; > } > freq_ref = (da7213->mclk_rate / indiv); > } > > - pll_ctrl |= indiv_bits; > + pll_ctrl = indiv_bits; > > -
Re: [alsa-devel] [PATCH 2/4] ASoC: da7213: Improve driver efficiency with regards to MCLK usage
> Currently MCLK remains enabled during bias STANDBY state, and this > is not necessary. This patch updates the code to handle enabling > and disabling of MCLK, if provided, when moving between STANDBY > and PREPARE states, therefore saving power when no active streams > present. > > Signed-off-by: Adam Thomson Tested-by: Peter Meerwald-Stadler > --- > sound/soc/codecs/da7213.c | 20 +++- > 1 file changed, 11 insertions(+), 9 deletions(-) > > diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c > index bcf1834..7701f4e 100644 > --- a/sound/soc/codecs/da7213.c > +++ b/sound/soc/codecs/da7213.c > @@ -1454,11 +1454,10 @@ static int da7213_set_bias_level(struct snd_soc_codec > *codec, > > switch (level) { > case SND_SOC_BIAS_ON: > - case SND_SOC_BIAS_PREPARE: > break; > - case SND_SOC_BIAS_STANDBY: > - if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { > - /* MCLK */ > + case SND_SOC_BIAS_PREPARE: > + /* Enable MCLK for transition to ON state */ > + if (snd_soc_codec_get_bias_level(codec) == > SND_SOC_BIAS_STANDBY) { > if (da7213->mclk) { > ret = clk_prepare_enable(da7213->mclk); > if (ret) { > @@ -1467,21 +1466,24 @@ static int da7213_set_bias_level(struct snd_soc_codec > *codec, > return ret; > } > } > - > + } > + break; > + case SND_SOC_BIAS_STANDBY: > + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { > /* Enable VMID reference & master bias */ > snd_soc_update_bits(codec, DA7213_REFERENCES, > DA7213_VMID_EN | DA7213_BIAS_EN, > DA7213_VMID_EN | DA7213_BIAS_EN); > + } else { > + /* Remove MCLK */ > + if (da7213->mclk) > + clk_disable_unprepare(da7213->mclk); > } > break; > case SND_SOC_BIAS_OFF: > /* Disable VMID reference & master bias */ > snd_soc_update_bits(codec, DA7213_REFERENCES, > DA7213_VMID_EN | DA7213_BIAS_EN, 0); > - > - /* MCLK */ > - if (da7213->mclk) > - clk_disable_unprepare(da7213->mclk); > break; > } > return 0; > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [alsa-devel] [PATCH 1/4] ASoC: da7213: Default to 64 BCLKs per WCLK to support all formats
> Previously code defaulted to 32 BCLKS per WCLK which meant 24 and > 32 bit DAI formats would not work properly. This patch fixes the > issue by defaulting to 64 BCLKs per WCLK. Tested-by: Peter Meerwald-Stadler > Signed-off-by: Adam Thomson > --- > sound/soc/codecs/da7213.c | 4 ++-- > 1 file changed, 2 insertions(+), 2 deletions(-) > > diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c > index e5527bc..bcf1834 100644 > --- a/sound/soc/codecs/da7213.c > +++ b/sound/soc/codecs/da7213.c > @@ -1247,8 +1247,8 @@ static int da7213_set_dai_fmt(struct snd_soc_dai > *codec_dai, unsigned int fmt) > return -EINVAL; > } > > - /* By default only 32 BCLK per WCLK is supported */ > - dai_clk_mode |= DA7213_DAI_BCLKS_PER_WCLK_32; > + /* By default only 64 BCLK per WCLK is supported */ > + dai_clk_mode |= DA7213_DAI_BCLKS_PER_WCLK_64; > > snd_soc_write(codec, DA7213_DAI_CLK_MODE, dai_clk_mode); > snd_soc_update_bits(codec, DA7213_DAI_CTRL, DA7213_DAI_FORMAT_MASK, > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH] iio: accel: mma8452: Bugfix to enbale and allow different events to work parallely.
turn IRQ_NONE; > + > if (src & MMA8452_INT_DRDY) { > iio_trigger_poll_chained(indio_dev->trig); > ret = IRQ_HANDLED; > } > > - if ((src & MMA8452_INT_TRANS && > - chip->ev_src == MMA8452_TRANSIENT_SRC) || > - (src & MMA8452_INT_FF_MT && > - chip->ev_src == MMA8452_FF_MT_SRC)) { > + if (src & MMA8452_INT_FF_MT) { > + if (mma8452_freefall_mode_enabled(data)) { > + s64 ts = iio_get_time_ns(indio_dev); > + > + iio_push_event(indio_dev, > +IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, > + IIO_MOD_X_AND_Y_AND_Z, > + IIO_EV_TYPE_MAG, > + IIO_EV_DIR_FALLING), > +ts); > + } > + ret = IRQ_HANDLED; > + } > + > + if (src & MMA8452_INT_TRANS) { > mma8452_transient_interrupt(indio_dev); > ret = IRQ_HANDLED; > } > @@ -1222,96 +1255,36 @@ static const struct mma_chip_info > mma_chip_info_table[] = { >* g * N * 100 / 2048 for N = 2, 4, 8 and g=9.80665 >*/ > .mma_scales = { {0, 2394}, {0, 4788}, {0, 9577} }, > - .ev_cfg = MMA8452_TRANSIENT_CFG, > - .ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE, > - .ev_cfg_chan_shift = 1, > - .ev_src = MMA8452_TRANSIENT_SRC, > - .ev_src_xe = MMA8452_TRANSIENT_SRC_XTRANSE, > - .ev_src_ye = MMA8452_TRANSIENT_SRC_YTRANSE, > - .ev_src_ze = MMA8452_TRANSIENT_SRC_ZTRANSE, > - .ev_ths = MMA8452_TRANSIENT_THS, > - .ev_ths_mask = MMA8452_TRANSIENT_THS_MASK, > - .ev_count = MMA8452_TRANSIENT_COUNT, > }, > [mma8452] = { > .chip_id = MMA8452_DEVICE_ID, > .channels = mma8452_channels, > .num_channels = ARRAY_SIZE(mma8452_channels), > .mma_scales = { {0, 9577}, {0, 19154}, {0, 38307} }, > - .ev_cfg = MMA8452_TRANSIENT_CFG, > - .ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE, > - .ev_cfg_chan_shift = 1, > - .ev_src = MMA8452_TRANSIENT_SRC, > - .ev_src_xe = MMA8452_TRANSIENT_SRC_XTRANSE, > - .ev_src_ye = MMA8452_TRANSIENT_SRC_YTRANSE, > - .ev_src_ze = MMA8452_TRANSIENT_SRC_ZTRANSE, > - .ev_ths = MMA8452_TRANSIENT_THS, > - .ev_ths_mask = MMA8452_TRANSIENT_THS_MASK, > - .ev_count = MMA8452_TRANSIENT_COUNT, > }, > [mma8453] = { > .chip_id = MMA8453_DEVICE_ID, > .channels = mma8453_channels, > .num_channels = ARRAY_SIZE(mma8453_channels), > .mma_scales = { {0, 38307}, {0, 76614}, {0, 153228} }, > - .ev_cfg = MMA8452_TRANSIENT_CFG, > - .ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE, > - .ev_cfg_chan_shift = 1, > - .ev_src = MMA8452_TRANSIENT_SRC, > - .ev_src_xe = MMA8452_TRANSIENT_SRC_XTRANSE, > - .ev_src_ye = MMA8452_TRANSIENT_SRC_YTRANSE, > - .ev_src_ze = MMA8452_TRANSIENT_SRC_ZTRANSE, > - .ev_ths = MMA8452_TRANSIENT_THS, > - .ev_ths_mask = MMA8452_TRANSIENT_THS_MASK, > - .ev_count = MMA8452_TRANSIENT_COUNT, > }, > [mma8652] = { > .chip_id = MMA8652_DEVICE_ID, > .channels = mma8652_channels, > .num_channels = ARRAY_SIZE(mma8652_channels), > .mma_scales = { {0, 9577}, {0, 19154}, {0, 38307} }, > - .ev_cfg = MMA8452_FF_MT_CFG, > - .ev_cfg_ele = MMA8452_FF_MT_CFG_ELE, > - .ev_cfg_chan_shift = 3, > - .ev_src = MMA8452_FF_MT_SRC, > - .ev_src_xe = MMA8452_FF_MT_SRC_XHE, > - .ev_src_ye = MMA8452_FF_MT_SRC_YHE, > - .ev_src_ze = MMA8452_FF_MT_SRC_ZHE, > - .ev_ths = MMA8452_FF_MT_THS, > - .ev_ths_mask = MMA8452_FF_MT_THS_MASK, > - .ev_count = MMA8452_FF_MT_COUNT, > }, > [mma8653] = { > .chip_id = MMA8653_DEVICE_ID, > .channels = mma8653_channels, > .num_channels = ARRAY_SIZE(mma8653_channels), > .mma_scales = { {0, 38307}, {0, 76614}, {0, 153228} }, > - .ev_cfg = MMA8452_FF_MT_CFG, > - .ev_cfg_ele = MMA8452_FF_MT_CFG_ELE, > - .ev_cfg_chan_shift = 3, > - .ev_src = MMA8452_FF_MT_SRC, > - .ev_src_xe = MMA8452_FF_MT_SRC_XHE, > - .ev_src_ye = MMA8452_FF_MT_SRC_YHE, > - .ev_src_ze = MMA8452_FF_MT_SRC_ZHE, > - .ev_ths = MMA8452_FF_MT_THS, > - .ev_ths_mask = MMA8452_FF_MT_THS_MASK, > - .ev_count = MMA8452_FF_MT_COUNT, > }, > [fxls8471] = { > .chip_id = FXLS8471_DEVICE_ID, > .channels = mma8451_channels, > .num_channels = ARRAY_SIZE(mma8451_channels), > .mma_scales = { {0, 2394}, {0, 4788}, {0, 9577} }, > - .ev_cfg = MMA8452_TRANSIENT_CFG, > - .ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE, > - .ev_cfg_chan_shift = 1, > - .ev_src = MMA8452_TRANSIENT_SRC, > - .ev_src_xe = MMA8452_TRANSIENT_SRC_XTRANSE, > - .ev_src_ye = MMA8452_TRANSIENT_SRC_YTRANSE, > - .ev_src_ze = MMA8452_TRANSIENT_SRC_ZTRANSE, > - .ev_ths = MMA8452_TRANSIENT_THS, > - .ev_ths_mask = MMA8452_TRANSIENT_THS_MASK, > - .ev_count = MMA8452_TRANSIENT_COUNT, > }, > }; > > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH v2 5/5] iio: srf08: add support for srf02 in i2c mode
ls); > > @@ -447,24 +470,39 @@ static int srf08_probe(struct i2c_client *client, > return ret; > } > > - /* > - * set default values of device here > - * these register values cannot be read from the hardware > - * therefore set driver specific default values > - */ > - ret = srf08_write_range_mm(data, SRF08_DEFAULT_RANGE); > - if (ret < 0) > - return ret; > + if (srf08_default_range[id->driver_data]) { it would be nice to have a chip_info struct with chip-specific information, so we can point to the relevant struct once instead of picking the correct entry from srf08_default_sensitivity and srf08_default_range separately so far, we have only two defaults tables... no big deal > + /* > + * set default range of device here > + * these register values cannot be read from he hardware > + * therefore set driver specific default values > + * > + * srf02 don't have a default value so it'll be omitted > + */ > + ret = srf08_write_range_mm(data, > + srf08_default_range[id->driver_data]); > + if (ret < 0) > + return ret; > + } > > - ret = srf08_write_sensitivity(data, > - srf08_default_sensitivity[id->driver_data]); > - if (ret < 0) > - return ret; > + if (srf08_default_sensitivity[id->driver_data]) { > + /* > + * set default sensitivity of device here > + * these register values cannot be read from the hardware > + * therefore set driver specific default values > + * > + * srf02 don't have a default value so it'll be omitted > + */ > + ret = srf08_write_sensitivity(data, > + srf08_default_sensitivity[id->driver_data]); > + if (ret < 0) > + return ret; > + } > > return devm_iio_device_register(&client->dev, indio_dev); > } > > static const struct of_device_id of_srf08_match[] = { > + { .compatible = "devantech,srf02", (void *)SRF02}, > { .compatible = "devantech,srf08", (void *)SRF08}, > { .compatible = "devantech,srf10", (void *)SRF10}, > {}, > @@ -473,6 +511,7 @@ static const struct of_device_id of_srf08_match[] = { > MODULE_DEVICE_TABLE(of, of_srf08_match); > > static const struct i2c_device_id srf08_id[] = { > + { "srf02", SRF02 }, > { "srf08", SRF08 }, > { "srf10", SRF10 }, > { } > @@ -490,5 +529,5 @@ static struct i2c_driver srf08_driver = { > module_i2c_driver(srf08_driver); > > MODULE_AUTHOR("Andreas Klinger "); > -MODULE_DESCRIPTION("Devantech SRF08/SRF10 ultrasonic ranger driver"); > +MODULE_DESCRIPTION("Devantech SRF02/SRF08/SRF10 i2c ultrasonic ranger > driver"); > MODULE_LICENSE("GPL"); > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH v2 3/5] iio: srf08: add triggered buffer support
> Add support for triggered buffers. comments below > Data format is quite simple: > distance 16 Bit > alignment48 Bit > timestamp64 Bit > > Signed-off-by: Andreas Klinger > --- > drivers/iio/proximity/srf08.c | 59 > --- > 1 file changed, 56 insertions(+), 3 deletions(-) > > diff --git a/drivers/iio/proximity/srf08.c b/drivers/iio/proximity/srf08.c > index 3f19536f215f..8018bb90b7b2 100644 > --- a/drivers/iio/proximity/srf08.c > +++ b/drivers/iio/proximity/srf08.c > @@ -18,6 +18,9 @@ > #include > #include > #include > +#include > +#include > +#include > > /* registers of SRF08 device */ > #define SRF08_WRITE_COMMAND 0x00/* Command Register */ > @@ -35,9 +38,22 @@ > > struct srf08_data { > struct i2c_client *client; > - int sensitivity;/* Gain */ > - int range_mm; /* max. Range in mm */ > + > + /* > + * Gain in the datasheet is called sensitivity here to distinct it > + * from the gain used with amplifiers of adc's > + */ > + int sensitivity; > + > + /* max. Range in mm */ > + int range_mm; > struct mutexlock; > + > + /* > + * triggered buffer > + * 1x16-bit channel + 3x16 padding + 4x16 timestamp > + */ > + s16 buffer[8]; > }; > > /* > @@ -110,6 +126,28 @@ static int srf08_read_ranging(struct srf08_data *data) > return ret; > } > > +static irqreturn_t srf08_trigger_handler(int irq, void *p) > +{ > + struct iio_poll_func *pf = p; > + struct iio_dev *indio_dev = pf->indio_dev; > + struct srf08_data *data = iio_priv(indio_dev); > + s16 sensor_data; > + > + sensor_data = srf08_read_ranging(data); > + if (sensor_data < 0) > + goto err; > + > + mutex_lock(&data->lock); > + data->buffer[0] = sensor_data; > + mutex_unlock(&data->lock); what is this lock protecting? this is still racy... data->buffer could still be overwritten here, between unlock() and iio_push_...() > + > + iio_push_to_buffers_with_timestamp(indio_dev, > + data->buffer, pf->timestamp); > +err: > + iio_trigger_notify_done(indio_dev->trig); > + return IRQ_HANDLED; > +} > + > static int srf08_read_raw(struct iio_dev *indio_dev, > struct iio_chan_spec const *channel, int *val, > int *val2, long mask) > @@ -323,7 +361,15 @@ static const struct iio_chan_spec srf08_channels[] = { > .info_mask_separate = > BIT(IIO_CHAN_INFO_RAW) | > BIT(IIO_CHAN_INFO_SCALE), > + .scan_index = 0, > + .scan_type = { > + .sign = 's', > + .realbits = 16, > + .storagebits = 16, > + .endianness = IIO_CPU, > + }, > }, > + IIO_CHAN_SOFT_TIMESTAMP(1), > }; > > static const struct iio_info srf08_info = { > @@ -362,6 +408,13 @@ static int srf08_probe(struct i2c_client *client, > > mutex_init(&data->lock); > > + ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, > + iio_pollfunc_store_time, srf08_trigger_handler, NULL); > + if (ret < 0) { > + dev_err(&client->dev, "setup of iio triggered buffer failed\n"); > + return ret; > + } > + > /* >* set default values of device here >* these register values cannot be read from the hardware > @@ -402,5 +455,5 @@ static struct i2c_driver srf08_driver = { > module_i2c_driver(srf08_driver); > > MODULE_AUTHOR("Andreas Klinger "); > -MODULE_DESCRIPTION("Devantech SRF08 ultrasonic ranger driver"); > +MODULE_DESCRIPTION("Devantech SRF08/SRF10 ultrasonic ranger driver"); > MODULE_LICENSE("GPL"); > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH] iio: adc: Add support for DLN2 ADC
"samplerate"); > + > + if (!indio_dev->pollfunc) { > + ret = -ENOMEM; > + goto dealloc_kfifo; > + } > + > + ret = dln2_register_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV, > + dln2_adc_event); > + if (ret) { > + dev_err(dev, "failed to register event cb: %d\n", ret); > + goto dealloc_pollfunc; > + } > + > + ret = iio_device_register(indio_dev); > + if (ret) { > + dev_err(dev, "failed to register iio device: %d\n", ret); > + goto dealloc_pollfunc; > + } > + > + dev_info(dev, "DLN2 ADC driver loaded\n"); avoid this kind of logging > + > + return 0; > + > +dealloc_pollfunc: > + iio_dealloc_pollfunc(indio_dev->pollfunc); > +dealloc_kfifo: > +dealloc_trigger: > + iio_trigger_unregister(dln2->trig); > +dealloc_dev: > + > + return ret; > +} > + > +static int dln2_adc_remove(struct platform_device *pdev) > +{ > + struct iio_dev *indio_dev = platform_get_drvdata(pdev); > + struct dln2_adc *dln2 = iio_priv(indio_dev); > + > + dev_info(&indio_dev->dev, "DLN2 ADC driver unloaded\n"); no such logging please > + > + dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV); should be after device_unregister > + iio_device_unregister(indio_dev); > + iio_trigger_unregister(dln2->trig); > + iio_dealloc_pollfunc(indio_dev->pollfunc); > + > + return 0; > +} > + > +static struct platform_driver dln2_adc_driver = { > + .driver.name= DLN2_ADC_MOD_NAME, > + .probe = dln2_adc_probe, > + .remove = dln2_adc_remove, > +}; > + > +module_platform_driver(dln2_adc_driver); > + > +MODULE_AUTHOR("Jack Andersen +MODULE_DESCRIPTION("Driver for the Diolan DLN2 ADC interface"); > +MODULE_LICENSE("GPL v2"); > +MODULE_ALIAS("platform:dln2-adc"); > diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c > index 704e189..a22ab8c 100644 > --- a/drivers/mfd/dln2.c > +++ b/drivers/mfd/dln2.c > @@ -53,6 +53,7 @@ enum dln2_handle { > DLN2_HANDLE_GPIO, > DLN2_HANDLE_I2C, > DLN2_HANDLE_SPI, > + DLN2_HANDLE_ADC, > DLN2_HANDLES > }; > > @@ -663,6 +664,12 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, > gfp_t gfp) > .port = 0, > }; > > +/* Only one ADC port supported */ > +static struct dln2_platform_data dln2_pdata_adc = { > + .handle = DLN2_HANDLE_ADC, > + .port = 0, > +}; > + > static const struct mfd_cell dln2_devs[] = { > { > .name = "dln2-gpio", > @@ -679,6 +686,11 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, > gfp_t gfp) > .platform_data = &dln2_pdata_spi, > .pdata_size = sizeof(struct dln2_platform_data), > }, > + { > + .name = "dln2-adc", > + .platform_data = &dln2_pdata_adc, > + .pdata_size = sizeof(struct dln2_platform_data), > + }, > }; > > static void dln2_stop(struct dln2_dev *dln2) > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH v2] iio : Add cm3218 smbus ara and acpi support
&ares, > + cm3218_get_i2c_address, > + client); > + acpi_dev_free_resource_list(&ares); > + if (ret < 0) > + return -ENODEV; > + > + return 0; > +} > +#else > +static inline int cm3218_acpi_get_address(struct i2c_client *client) why inline? > +{ > + return -ENODEV; > +} > +#endif > + > static int cm32181_probe(struct i2c_client *client, > const struct i2c_device_id *id) > { > struct cm32181_chip *cm32181; > struct iio_dev *indio_dev; > int ret; > + struct i2c_smbus_alert_setup ara_setup; > + const struct of_device_id *of_id; > + const struct acpi_device_id *acpi_id; > > indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*cm32181)); > if (!indio_dev) { > @@ -323,11 +402,65 @@ static int cm32181_probe(struct i2c_client *client, > indio_dev->name = id->name; > indio_dev->modes = INDIO_DIRECT_MODE; > > + /* Lookup for chip ID from platform, acpi or of device table */ > + if (id) { > + cm32181->chip_id = id->driver_data; > + } else if (ACPI_COMPANION(&client->dev)) { > + acpi_id = > acpi_match_device(client->dev.driver->acpi_match_table, > + &client->dev); > + if (!acpi_id) > + return -ENODEV; > + > + cm32181->chip_id = (kernel_ulong_t)acpi_id->driver_data; > + } else if (client->dev.of_node) { > + of_id = of_match_device(clients->dev.driver->of_match_table, > + &client->dev); > + if (!of_id) > + return -ENODEV; > + > + cm32181->chip_id = (kernel_ulong_t)of_id->data; > + } else { > + return -ENODEV; > + } > + > + if (cm32181->chip_id == CM3218_ID) { > + if (client->addr == CM3218_ARA_ADDR) { > + /* In case first address is the ARA device > + * lookup for a second one in acpi resources if > + * this client is enumerated on acpi > + */ > + ret = cm3218_acpi_get_address(client); > + if (ret < 0) > + return -ENODEV; > + } > + > +#ifdef CONFIG_I2C_SMBUS > + if (client->irq > 0) { > + /* setup smb alert device single line comment > + */ > + ara_setup.irq = client->irq; > + ara_setup.alert_edge_triggered = 0; > + cm32181->ara = i2c_setup_smbus_alert(client->adapter, > + &ara_setup); > + if (!cm32181->ara) > + return -ENODEV; > + } else { > + return -ENODEV; > + } > +#else > + return -ENODEV; > +#endif > + } > + > ret = cm32181_reg_init(cm32181); > if (ret) { > dev_err(&client->dev, > "%s: register init failed\n", > __func__); > + > + if (cm32181->ara) > + i2c_unregister_device(cm32181->ara); > + > return ret; > } > > @@ -336,32 +469,58 @@ static int cm32181_probe(struct i2c_client *client, > dev_err(&client->dev, > "%s: regist device failed\n", > __func__); > + > + if (cm32181->ara) > + i2c_unregister_device(cm32181->ara); > + > return ret; > } > > return 0; > } > > +static int cm32181_remove(struct i2c_client *client) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(client); > + struct cm32181_chip *cm32181 = iio_priv(indio_dev); > + > + if (cm32181->ara) > + i2c_unregister_device(cm32181->ara); > + > + return 0; > +}; > + > static const struct i2c_device_id cm32181_id[] = { > - { "cm32181", 0 }, > + { "cm32181", CM32181_ID }, > + { "cm3218", CM3218_ID }, > { } > }; > > MODULE_DEVICE_TABLE(i2c, cm32181_id); > > static const struct of_device_id cm32181_of_match[] = { > - { .compatible = "capella,cm32181" }, > + { .compatible = "capella,cm32181", (void *)CM32181_ID }, > + { .compatible = "capella,cm3218", (void *)CM3218_ID }, > { } > }; > MODULE_DEVICE_TABLE(of, cm32181_of_match); > > +static const struct acpi_device_id cm32181_acpi_match[] = { > + { "CPLM3218", CM3218_ID }, > + { } > +}; > + > +MODULE_DEVICE_TABLE(acpi, cm32181_acpi_match); > + > static struct i2c_driver cm32181_driver = { > .driver = { > .name = "cm32181", > .of_match_table = of_match_ptr(cm32181_of_match), > + .acpi_match_table = ACPI_PTR(cm32181_acpi_match), > }, > .id_table = cm32181_id, > .probe = cm32181_probe, > + .remove = cm32181_remove, > }; > > module_i2c_driver(cm32181_driver); > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: iio/accel/stk8312: Improve unlocking of a mutex in two functions
Hello, > Maybe … > But I proposed an other source code layout for useful reasons. I think there is a (hidden) cost of having pure cleanup patches: they make backporting fixes harder (across the cleanup) stylistic changes must have a clear benefit, readability is subjective, consistency per se doesn't buy anything the discussion how code should be written in the first place is separate from the discussion what is worth fixing up lateron (IMHO) > > There is no firm rule about error handling in one place. > > There are some design options available. > > > > If it leads to more complex flow as here, don't do it. > > I would appreciate to clarify such a view a bit more. > How would you like to achieve a complete and efficient > exception handling in shown places? regards, p. -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH v4] iio : Add cm3218 smbus ara and acpi support
/* Lookup for chip ID from platform, acpi or of device table */ > + if (id) { > + cm32181->chip_id = id->driver_data; > + } else if (ACPI_COMPANION(&client->dev)) { > + acpi_id = > acpi_match_device(client->dev.driver->acpi_match_table, > + &client->dev); > + if (!acpi_id) > + return -ENODEV; > + > + cm32181->chip_id = (kernel_ulong_t)acpi_id->driver_data; > + } else if (client->dev.of_node) { > + of_id = of_match_device(clients->dev.driver->of_match_table, > + &client->dev); > + if (!of_id) > + return -ENODEV; > + > + cm32181->chip_id = (kernel_ulong_t)of_id->data; > + } else { > + return -ENODEV; > + } > + > + if (cm32181->chip_id == CM3218_ID) { > + if (client->addr == CM3218_ARA_ADDR) { > + /* > + * In case first address is the ARA device > + * lookup for a second one in acpi resources if > + * this client is enumerated on acpi > + */ > + ret = cm3218_acpi_get_address(client); > + if (ret < 0) > + return -ENODEV; > + } > + > +#ifdef CONFIG_I2C_SMBUS > + if (client->irq > 0) { > + /* setup smb alert device */ > + ara_setup.irq = client->irq; > + ara_setup.alert_edge_triggered = 0; > + cm32181->ara = i2c_setup_smbus_alert(client->adapter, > + &ara_setup); > + if (!cm32181->ara) > + return -ENODEV; > + } else { > + return -ENODEV; > + } > +#else > + return -ENODEV; > +#endif > + } > + > ret = cm32181_reg_init(cm32181); > if (ret) { > dev_err(&client->dev, > "%s: register init failed\n", > __func__); > + > + if (cm32181->ara) > + i2c_unregister_device(cm32181->ara); > + > return ret; > } > > @@ -336,32 +478,58 @@ static int cm32181_probe(struct i2c_client *client, > dev_err(&client->dev, > "%s: regist device failed\n", > __func__); > + > + if (cm32181->ara) > + i2c_unregister_device(cm32181->ara); > + > return ret; > } > > return 0; > } > > +static int cm32181_remove(struct i2c_client *client) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(client); > + struct cm32181_chip *cm32181 = iio_priv(indio_dev); > + > + if (cm32181->ara) > + i2c_unregister_device(cm32181->ara); > + > + return 0; > +}; > + > static const struct i2c_device_id cm32181_id[] = { > - { "cm32181", 0 }, > + { "cm32181", CM32181_ID }, > + { "cm3218", CM3218_ID }, > { } > }; > > MODULE_DEVICE_TABLE(i2c, cm32181_id); > > static const struct of_device_id cm32181_of_match[] = { > - { .compatible = "capella,cm32181" }, > + { .compatible = "capella,cm32181", (void *)CM32181_ID }, > + { .compatible = "capella,cm3218", (void *)CM3218_ID }, > { } > }; > MODULE_DEVICE_TABLE(of, cm32181_of_match); > > +static const struct acpi_device_id cm32181_acpi_match[] = { > + { "CPLM3218", CM3218_ID }, > + { } > +}; > + > +MODULE_DEVICE_TABLE(acpi, cm32181_acpi_match); > + > static struct i2c_driver cm32181_driver = { > .driver = { > .name = "cm32181", > .of_match_table = of_match_ptr(cm32181_of_match), > + .acpi_match_table = ACPI_PTR(cm32181_acpi_match), > }, > .id_table = cm32181_id, > .probe = cm32181_probe, > + .remove = cm32181_remove, > }; > > module_i2c_driver(cm32181_driver); > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH v4 1/3] iio: Add modifier for white light
Hello, it is not clear to me why 'white' is needed; isn't that the default, i.e. unfiltered light? thanks, regards, p. > Signed-off-by: Parthiban Nallathambi > --- > Documentation/ABI/testing/sysfs-bus-iio | 7 +++ > drivers/iio/industrialio-core.c | 1 + > include/uapi/linux/iio/types.h | 1 + > tools/iio/iio_event_monitor.c | 2 ++ > 4 files changed, 11 insertions(+) > > diff --git a/Documentation/ABI/testing/sysfs-bus-iio > b/Documentation/ABI/testing/sysfs-bus-iio > index 731146c3b138..43e481aed5b2 100644 > --- a/Documentation/ABI/testing/sysfs-bus-iio > +++ b/Documentation/ABI/testing/sysfs-bus-iio > @@ -1312,6 +1312,13 @@ Description: > standardised CIE Erythemal Action Spectrum. UV index values > range > from 0 (low) to >=11 (extreme). > > +What:/sys/.../iio:deviceX/in_intensityY_white_raw > +KernelVersion: 4.18 > +Contact: linux-...@vger.kernel.org > +Description: > + Modifier white indicates that measurements contain white LED > + component. > + > What:/sys/.../iio:deviceX/in_intensity_red_integration_time > What:/sys/.../iio:deviceX/in_intensity_green_integration_time > What:/sys/.../iio:deviceX/in_intensity_blue_integration_time > diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c > index 19bdf3d2962a..cb939b9fad16 100644 > --- a/drivers/iio/industrialio-core.c > +++ b/drivers/iio/industrialio-core.c > @@ -108,6 +108,7 @@ static const char * const iio_modifier_names[] = { > [IIO_MOD_LIGHT_GREEN] = "green", > [IIO_MOD_LIGHT_BLUE] = "blue", > [IIO_MOD_LIGHT_UV] = "uv", > + [IIO_MOD_LIGHT_WHITE] = "white", > [IIO_MOD_QUATERNION] = "quaternion", > [IIO_MOD_TEMP_AMBIENT] = "ambient", > [IIO_MOD_TEMP_OBJECT] = "object", > diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h > index 4213cdf88e3c..de87a6c7e6de 100644 > --- a/include/uapi/linux/iio/types.h > +++ b/include/uapi/linux/iio/types.h > @@ -84,6 +84,7 @@ enum iio_modifier { > IIO_MOD_CO2, > IIO_MOD_VOC, > IIO_MOD_LIGHT_UV, > + IIO_MOD_LIGHT_WHITE, > }; > > enum iio_event_type { > diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c > index b61245e1181d..a2f9c62a79dd 100644 > --- a/tools/iio/iio_event_monitor.c > +++ b/tools/iio/iio_event_monitor.c > @@ -96,6 +96,7 @@ static const char * const iio_modifier_names[] = { > [IIO_MOD_LIGHT_GREEN] = "green", > [IIO_MOD_LIGHT_BLUE] = "blue", > [IIO_MOD_LIGHT_UV] = "uv", > + [IIO_MOD_LIGHT_WHITE] = "white", > [IIO_MOD_QUATERNION] = "quaternion", > [IIO_MOD_TEMP_AMBIENT] = "ambient", > [IIO_MOD_TEMP_OBJECT] = "object", > @@ -178,6 +179,7 @@ static bool event_is_known(struct iio_event_data *event) > case IIO_MOD_LIGHT_GREEN: > case IIO_MOD_LIGHT_BLUE: > case IIO_MOD_LIGHT_UV: > + case IIO_MOD_LIGHT_WHITE: > case IIO_MOD_QUATERNION: > case IIO_MOD_TEMP_AMBIENT: > case IIO_MOD_TEMP_OBJECT: > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH v4 2/3] iio: light: Add support for vishay vcnl4035
> + ret = iio_trigger_register(data->drdy_trigger0); > + if (ret) { > + dev_err(&client->dev, "iio trigger register failed\n"); > + goto fail_pm_disable; > + } > + > + /* Trigger setup */ > + ret = iio_triggered_buffer_setup(indio_dev, NULL, > + vcnl4035_trigger_consumer_handler, > + NULL); > + if (ret < 0) { > + dev_err(&client->dev, "iio triggered buffer setup > failed\n"); > + goto fail_trigger_unregister; > + } > + > + /* IRQ to trigger mapping */ > + ret = devm_request_threaded_irq(&client->dev, client->irq, > + NULL, vcnl4035_drdy_irq_thread, > + IRQF_TRIGGER_LOW | IRQF_ONESHOT, > + VCNL4035_IRQ_NAME, indio_dev); > + if (ret < 0) { > + dev_err(&client->dev, "request irq %d for trigger0 > failed\n", > + client->irq); > + goto fail_buffer_clean; > + } the closing bracket seems to be mis-indented > + } > + > + ret = iio_device_register(indio_dev); > + if (ret) > + goto fail_buffer_clean; > + return 0; > + > +fail_buffer_clean: > + iio_triggered_buffer_cleanup(indio_dev); > +fail_trigger_unregister: > + iio_trigger_unregister(data->drdy_trigger0); > +fail_pm_disable: > + pm_runtime_disable(&client->dev); > + pm_runtime_set_suspended(&client->dev); > + pm_runtime_put_noidle(&client->dev); > +fail_poweroff: > + vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_DISABLE); > + return ret; > +} > + > +static int vcnl4035_remove(struct i2c_client *client) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(client); > + struct vcnl4035_data *data = iio_priv(indio_dev); > + > + iio_triggered_buffer_cleanup(indio_dev); > + iio_trigger_unregister(data->drdy_trigger0); > + iio_device_unregister(indio_dev); > + > + pm_runtime_disable(&client->dev); > + pm_runtime_set_suspended(&client->dev); > + pm_runtime_put_noidle(&client->dev); > + > + return vcnl4035_set_als_power_state(iio_priv(indio_dev), > + VCNL4035_MODE_ALS_DISABLE); > +} > + > +#ifdef CONFIG_PM > +static int vcnl4035_runtime_suspend(struct device *dev) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); > + struct vcnl4035_data *data = iio_priv(indio_dev); > + int ret; > + > + ret = vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_DISABLE); > + regcache_mark_dirty(data->regmap); > + > + return ret; > +} > + > +static int vcnl4035_runtime_resume(struct device *dev) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); > + struct vcnl4035_data *data = iio_priv(indio_dev); > + int ret; > + > + regcache_sync(data->regmap); > + ret = vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_ENABLE); > + if (ret < 0) > + return ret; > + /* wait for 1 ALS integration cycle */ > + msleep(data->als_it_val * 100); > + > + return 0; > +} > +#endif > + > +static const struct dev_pm_ops vcnl4035_pm_ops = { > + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, > + pm_runtime_force_resume) > + SET_RUNTIME_PM_OPS(vcnl4035_runtime_suspend, > +vcnl4035_runtime_resume, NULL) > +}; > + > +static const struct of_device_id vcnl4035_of_match[] = { > + { .compatible = "vishay,vcnl4035", }, > + { }, > +}; > +MODULE_DEVICE_TABLE(of, vcnl4035_of_match); > + > +static const struct i2c_device_id vcnl4035_id[] = { > + { "vcnl4035", 0 }, > + { } > +}; > +MODULE_DEVICE_TABLE(i2c, vcnl4035_id); > + > +static struct i2c_driver vcnl4035_driver = { > + .driver = { > + .name = VCNL4035_DRV_NAME, > + .pm = &vcnl4035_pm_ops, > + .of_match_table = of_match_ptr(vcnl4035_of_match), > + }, > + .probe = vcnl4035_probe, > + .remove = vcnl4035_remove, > + .id_table = vcnl4035_id, > +}; > + > +module_i2c_driver(vcnl4035_driver); > + > +MODULE_AUTHOR("Parthiban Nallathambi "); > +MODULE_DESCRIPTION("VCNL4035 Ambient Light Sensor driver"); > +MODULE_LICENSE("GPL v2"); > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH v4 1/3] iio: Add modifier for white light
Hello, > > it is not clear to me why 'white' is needed; > > isn't that the default, i.e. unfiltered light? > > Yes, it is. But devices like vcnl4035 veml7700, White LED data one > register and all other sources of light (like fluorescent, > incandescent ,sunlight) in separate register. > > So in such cases this WHITE modifier is needed. Should it needs to > come under IIO_MOD_LIGHT_CLEAR? it is a mess already :), there is _intensity _intensity_ir _intensity_both _intensity_uv _intensity_red _intensity_green _intensity_blue _intensity_clear I think that ir, uv, red, green, blue are filters for particular ranges of the spectrum _intensity_clear might be unfiltered and _intensity might relate to the human eye photopic curve, I think the proposed white might be the same as clear? 'both' means ir + _intensity (not clearly specified) regards, p. > > > +KernelVersion: 4.18 > > > +Contact: linux-...@vger.kernel.org > > > +Description: > > > + Modifier white indicates that measurements contain white LED > > > + component. > > > + > > > What: /sys/.../iio:deviceX/in_intensity_red_integration_time > > > What: > > > /sys/.../iio:deviceX/in_intensity_green_integration_time > > > What: > > > /sys/.../iio:deviceX/in_intensity_blue_integration_time > > > diff --git a/drivers/iio/industrialio-core.c > > > b/drivers/iio/industrialio-core.c > > > index 19bdf3d2962a..cb939b9fad16 100644 > > > --- a/drivers/iio/industrialio-core.c > > > +++ b/drivers/iio/industrialio-core.c > > > @@ -108,6 +108,7 @@ static const char * const iio_modifier_names[] = { > > > [IIO_MOD_LIGHT_GREEN] = "green", > > > [IIO_MOD_LIGHT_BLUE] = "blue", > > > [IIO_MOD_LIGHT_UV] = "uv", > > > + [IIO_MOD_LIGHT_WHITE] = "white", > > > [IIO_MOD_QUATERNION] = "quaternion", > > > [IIO_MOD_TEMP_AMBIENT] = "ambient", > > > [IIO_MOD_TEMP_OBJECT] = "object", > > > diff --git a/include/uapi/linux/iio/types.h > > > b/include/uapi/linux/iio/types.h > > > index 4213cdf88e3c..de87a6c7e6de 100644 > > > --- a/include/uapi/linux/iio/types.h > > > +++ b/include/uapi/linux/iio/types.h > > > @@ -84,6 +84,7 @@ enum iio_modifier { > > > IIO_MOD_CO2, > > > IIO_MOD_VOC, > > > IIO_MOD_LIGHT_UV, > > > + IIO_MOD_LIGHT_WHITE, > > > }; > > > enum iio_event_type { > > > diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c > > > index b61245e1181d..a2f9c62a79dd 100644 > > > --- a/tools/iio/iio_event_monitor.c > > > +++ b/tools/iio/iio_event_monitor.c > > > @@ -96,6 +96,7 @@ static const char * const iio_modifier_names[] = { > > > [IIO_MOD_LIGHT_GREEN] = "green", > > > [IIO_MOD_LIGHT_BLUE] = "blue", > > > [IIO_MOD_LIGHT_UV] = "uv", > > > + [IIO_MOD_LIGHT_WHITE] = "white", > > > [IIO_MOD_QUATERNION] = "quaternion", > > > [IIO_MOD_TEMP_AMBIENT] = "ambient", > > > [IIO_MOD_TEMP_OBJECT] = "object", > > > @@ -178,6 +179,7 @@ static bool event_is_known(struct iio_event_data > > > *event) > > > case IIO_MOD_LIGHT_GREEN: > > > case IIO_MOD_LIGHT_BLUE: > > > case IIO_MOD_LIGHT_UV: > > > + case IIO_MOD_LIGHT_WHITE: > > > case IIO_MOD_QUATERNION: > > > case IIO_MOD_TEMP_AMBIENT: > > > case IIO_MOD_TEMP_OBJECT: > > > > > > > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH 1/3] iio: adc: add support for mcp3911
g(&adc->spi->dev, "set channel 1 into 24bit mode\n"); > + break; > + case 16: > + statuscomreg |= MCP3911_STATUSCOM_CH1_24WIDTH; > + dev_dbg(&adc->spi->dev, "set channel 1 into 16bit mode\n"); > + break; > + default: > + adc->width[1] = 24; > + dev_info(&adc->spi->dev, "invalid width for channel 1. Use > 24bit.\n"); > + break; > + } > + > + return mcp3911_write(adc, MCP3911_REG_STATUSCOM, statuscomreg, 2); > +} > + > +static int mcp3911_probe(struct spi_device *spi) > +{ > + struct iio_dev *indio_dev; > + struct mcp3911 *adc; > + int ret; > + > + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); > + if (!indio_dev) > + return -ENOMEM; > + > + adc = iio_priv(indio_dev); > + adc->spi = spi; > + adc->np = spi->dev.of_node; > + > + ret = mcp3911_config_of(adc); > + if (ret) > + return ret; > + > + if (adc->vrefext) { > + adc->vref = devm_regulator_get(&adc->spi->dev, "vref"); > + if (IS_ERR(adc->vref)) > + return PTR_ERR(adc->vref); > + > + ret = regulator_enable(adc->vref); > + if (ret < 0) > + return ret; > + } > + > + /* Store gain values to better calculate scale values */ > + mcp3911_get_hwgain(adc, 0, &adc->gain[0]); > + mcp3911_get_hwgain(adc, 1, &adc->gain[1]); > + > + indio_dev->dev.parent = &spi->dev; > + indio_dev->dev.of_node = spi->dev.of_node; > + indio_dev->name = spi_get_device_id(spi)->name; > + indio_dev->modes = INDIO_DIRECT_MODE; > + indio_dev->info = &mcp3911_info; > + spi_set_drvdata(spi, indio_dev); > + > + indio_dev->channels = mcp3911_channels; > + indio_dev->num_channels = ARRAY_SIZE(mcp3911_channels); > + > + mutex_init(&adc->lock); > + > + ret = iio_device_register(indio_dev); > + if (ret) > + goto reg_disable; > + > + return ret; > + > +reg_disable: > + if (adc->vref) > + regulator_disable(adc->vref); > + > + return ret; > +} > + > +static int mcp3911_remove(struct spi_device *spi) > +{ > + struct iio_dev *indio_dev = spi_get_drvdata(spi); > + struct mcp3911 *adc = iio_priv(indio_dev); > + > + iio_device_unregister(indio_dev); > + > + if (adc->vref) > + regulator_disable(adc->vref); > + > + return 0; > +} > + > +#if defined(CONFIG_OF) > +static const struct of_device_id mcp3911_dt_ids[] = { > + { .compatible = "microchip,mcp3911" }, > + { } > +}; > +MODULE_DEVICE_TABLE(of, mcp3911_dt_ids); > +#endif > + > +static const struct spi_device_id mcp3911_id[] = { > + { "mcp3911", 0 }, > + { } > +}; > +MODULE_DEVICE_TABLE(spi, mcp3911_id); > + > +static struct spi_driver mcp3911_driver = { > + .driver = { > + .name = "mcp3911", > + .of_match_table = of_match_ptr(mcp3911_dt_ids), > + }, > + .probe = mcp3911_probe, > + .remove = mcp3911_remove, > + .id_table = mcp3911_id, > +}; > +module_spi_driver(mcp3911_driver); > + > +MODULE_AUTHOR("Marcus Folkesson "); > +MODULE_AUTHOR("Kent Gustavsson "); > +MODULE_DESCRIPTION("Microchip Technology MCP3911"); > +MODULE_LICENSE("GPL v2"); > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH v2 4/6] iio:adxl372: Add FIFO and interrupts support
e) > + * 512 sample sets of single-axis data > + */ > + if ((st->watermark * st->fifo_set_size) > ADXL372_FIFO_SIZE) > + st->watermark = (ADXL372_FIFO_SIZE / st->fifo_set_size); > + > + st->fifo_mode = ADXL372_FIFO_STREAMED; > + > + ret = adxl372_configure_fifo(st); > + if (ret < 0) { > + st->fifo_mode = ADXL372_FIFO_BYPASSED; > + adxl372_set_interrupts(st, 0, 0); > + return ret; > + } > + > + return iio_triggered_buffer_postenable(indio_dev); > +} > + > +static int adxl372_buffer_predisable(struct iio_dev *indio_dev) > +{ > + struct adxl372_state *st = iio_priv(indio_dev); > + > + adxl372_set_interrupts(st, 0, 0); > + st->fifo_mode = ADXL372_FIFO_BYPASSED; > + adxl372_configure_fifo(st); > + > + return iio_triggered_buffer_predisable(indio_dev); > +} > + > +static const struct iio_buffer_setup_ops adxl372_buffer_ops = { > + .postenable = adxl372_buffer_postenable, > + .predisable = adxl372_buffer_predisable, > +}; > + > +static int adxl372_dready_trig_set_state(struct iio_trigger *trig, > + bool state) > +{ > + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); > + struct adxl372_state *st = iio_priv(indio_dev); > + unsigned long int mask = 0; > + > + if (state) > + mask = ADXL372_INT1_MAP_FIFO_FULL_MSK; > + > + return adxl372_set_interrupts(st, mask, 0); > +} > + > +static const struct iio_trigger_ops adxl372_trigger_ops = { > + .set_trigger_state = adxl372_dready_trig_set_state, > +}; > + > static const struct iio_info adxl372_info = { > .read_raw = adxl372_read_raw, > .debugfs_reg_access = &adxl372_reg_access, > + .hwfifo_set_watermark = adxl372_set_watermark, > }; > > static const struct regmap_config adxl372_spi_regmap_config = { > @@ -495,10 +819,11 @@ static int adxl372_probe(struct spi_device *spi) > > indio_dev->channels = adxl372_channels; > indio_dev->num_channels = ARRAY_SIZE(adxl372_channels); > + indio_dev->available_scan_masks = adxl372_channel_masks; > indio_dev->dev.parent = &spi->dev; > indio_dev->name = spi_get_device_id(spi)->name; > indio_dev->info = &adxl372_info; > - indio_dev->modes = INDIO_DIRECT_MODE; > + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; > > ret = adxl372_setup(st); > if (ret < 0) { > @@ -506,6 +831,41 @@ static int adxl372_probe(struct spi_device *spi) > return ret; > } > > + ret = devm_iio_triggered_buffer_setup(&st->spi->dev, > + indio_dev, NULL, > + adxl372_trigger_handler, > + &adxl372_buffer_ops); > + if (ret < 0) > + return ret; > + > + iio_buffer_set_attrs(indio_dev->buffer, adxl372_fifo_attributes); > + > + if (st->spi->irq) { > + st->dready_trig = devm_iio_trigger_alloc(&st->spi->dev, > + "%s-dev%d", > + indio_dev->name, > + indio_dev->id); > + if (st->dready_trig == NULL) > + return -ENOMEM; > + > + st->dready_trig->ops = &adxl372_trigger_ops; > + st->dready_trig->dev.parent = &st->spi->dev; > + iio_trigger_set_drvdata(st->dready_trig, indio_dev); > + ret = devm_iio_trigger_register(&st->spi->dev, st->dready_trig); > + if (ret < 0) > + return ret; > + > + indio_dev->trig = iio_trigger_get(st->dready_trig); > + > + ret = devm_request_threaded_irq(&st->spi->dev, st->spi->irq, > + iio_trigger_generic_data_rdy_poll, > + NULL, > + IRQF_TRIGGER_RISING | IRQF_ONESHOT, > + indio_dev->name, st->dready_trig); > + if (ret < 0) > + return ret; > + } > + > return devm_iio_device_register(&st->spi->dev, indio_dev); > } > > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH RFC] iio: pressure: zpa2326: report interrupted case as failure
> If the timeout-case prints a warning message then probably the interrupted > case should also. Further, wait_for_completion_interruptible_timeout() > returns long not int. > > Fixes: commit 03b262f2bbf4 ("iio:pressure: initial zpa2326 barometer support") > Signed-off-by: Nicholas Mc Guire this is actually a v2, looks good to me > --- > > The original control-flow was technically not wrong just confusing and a bit > complicated. Not clear if reporting the interrupted case actually is useful, > but given that the timeout is relatively long (200ms) it is not that unlikely > so differentiating the cases seems helpful. > > Patch was compile-tested with: x86_64_defconfig + CONFIG_IIO=m, > CONFIG_ZPA2326=m > > Patch is against v4.11 (localversion-next is next-20170512) > > drivers/iio/pressure/zpa2326.c | 17 ++--- > 1 file changed, 10 insertions(+), 7 deletions(-) > > diff --git a/drivers/iio/pressure/zpa2326.c b/drivers/iio/pressure/zpa2326.c > index e58a0ad..617926f 100644 > --- a/drivers/iio/pressure/zpa2326.c > +++ b/drivers/iio/pressure/zpa2326.c > @@ -867,12 +867,13 @@ static int zpa2326_wait_oneshot_completion(const struct > iio_dev *indio_dev, > { > int ret; > unsigned int val; > + long timeout; > > zpa2326_dbg(indio_dev, "waiting for one shot completion interrupt"); > > - ret = wait_for_completion_interruptible_timeout( > + timeout = wait_for_completion_interruptible_timeout( > &private->data_ready, ZPA2326_CONVERSION_JIFFIES); > - if (ret > 0) > + if (timeout > 0) > /* >* Interrupt handler completed before timeout: return operation >* status. > @@ -882,13 +883,15 @@ static int zpa2326_wait_oneshot_completion(const struct > iio_dev *indio_dev, > /* Clear all interrupts just to be sure. */ > regmap_read(private->regmap, ZPA2326_INT_SOURCE_REG, &val); > > - if (!ret) > + if (!timeout) { > /* Timed out. */ > + zpa2326_warn(indio_dev, "no one shot interrupt occurred (%ld)", > + timeout); > ret = -ETIME; > - > - if (ret != -ERESTARTSYS) > - zpa2326_warn(indio_dev, "no one shot interrupt occurred (%d)", > - ret); > + } else if (timeout < 0) { > + zpa2326_warn(indio_dev, "wait for one shot interrupt canceled"); > + ret = -ERESTARTSYS; > + } > > return ret; > } > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH v2 1/2] iio: light: Add support for vishay vcnl4035
me, indio_dev->id); > + if (!data->drdy_trigger0) { > + ret = -ENOMEM; > + goto fail_pm_disable; > + } > + data->drdy_trigger0->dev.parent = indio_dev->dev.parent; > + data->drdy_trigger0->ops = &vcnl4035_trigger_ops; > + indio_dev->available_scan_masks = vcnl4035_available_scan_masks; > + iio_trigger_set_drvdata(data->drdy_trigger0, indio_dev); > + > + /* IRQ to trigger mapping */ > + ret = devm_request_threaded_irq(&client->dev, client->irq, > + vcnl4035_drdy_irq_handler, vcnl4035_drdy_irq_thread, > + IRQF_TRIGGER_LOW | IRQF_ONESHOT, > + VCNL4035_IRQ_NAME, indio_dev); > + if (ret < 0) { > + dev_err(&client->dev, "request irq %d for trigger0 > failed\n", > + client->irq); > + goto fail_pm_disable; > + } > + > + ret = devm_iio_trigger_register(indio_dev->dev.parent, > + data->drdy_trigger0); > + if (ret) { > + dev_err(&client->dev, "iio trigger register failed\n"); > + goto fail_pm_disable; > + } > + > + /* Trigger setup */ > + ret = devm_iio_triggered_buffer_setup(indio_dev->dev.parent, > + indio_dev, > + vcnl4035_trigger_consumer_store_time, > + vcnl4035_trigger_consumer_handler, > + NULL); > + if (ret < 0) { > + dev_err(&client->dev, "iio triggered buffer setup > failed\n"); > + goto fail_pm_disable; > + } > + } > + > + ret = iio_device_register(indio_dev); > + if (ret) > + goto fail_pm_disable; > + dev_info(&client->dev, "%s Ambient light/proximity sensor\n", no need for this logging > + VCNL4035_DRV_NAME); > + return 0; > + > +fail_pm_disable: > + pm_runtime_disable(&client->dev); > + pm_runtime_set_suspended(&client->dev); > + pm_runtime_put_noidle(&client->dev); > +fail_poweroff: > + return vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_DISABLE); I think we want to return the 'ret' value indicating the original failure, not the return value of vcnl4035_set_als_power_state() which is likely success > +} > + > +static int vcnl4035_remove(struct i2c_client *client) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(client); > + > + iio_device_unregister(indio_dev); > + > + pm_runtime_disable(&client->dev); > + pm_runtime_set_suspended(&client->dev); > + pm_runtime_put_noidle(&client->dev); > + > + return vcnl4035_set_als_power_state(iio_priv(indio_dev), > + VCNL4035_MODE_ALS_DISABLE); > +} > + > +#ifdef CONFIG_PM > +static int vcnl4035_runtime_suspend(struct device *dev) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); > + struct vcnl4035_data *data = iio_priv(indio_dev); > + int ret; > + > + mutex_lock(&data->lock); > + ret = vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_DISABLE); > + regcache_mark_dirty(data->regmap); > + mutex_unlock(&data->lock); > + > + return ret; > +} > + > +static int vcnl4035_runtime_resume(struct device *dev) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); > + struct vcnl4035_data *data = iio_priv(indio_dev); > + int ret; > + > + regcache_sync(data->regmap); > + ret = vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_ENABLE); > + if (ret < 0) > + return ret; > + /* wait for 1 ALS integration cycle */ > + msleep(data->als_it_val * 100); > + > + return 0; > +} > +#endif > + > +static const struct dev_pm_ops vcnl4035_pm_ops = { > + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, > + pm_runtime_force_resume) > + SET_RUNTIME_PM_OPS(vcnl4035_runtime_suspend, > +vcnl4035_runtime_resume, NULL) > +}; > + > +static const struct of_device_id vcnl4035_of_match[] = { > + { .compatible = "vishay,vcnl4035", }, > + { }, > +}; > +MODULE_DEVICE_TABLE(of, vcnl4035_of_match); > + > +static const struct i2c_device_id vcnl4035_id[] = { > + { "vcnl4035", 0 }, > + { } > +}; > +MODULE_DEVICE_TABLE(i2c, vcnl4035_id); > + > +static struct i2c_driver vcnl4035_driver = { > + .driver = { > + .name = VCNL4035_DRV_NAME, > + .pm = &vcnl4035_pm_ops, > + .of_match_table = of_match_ptr(vcnl4035_of_match), > + }, > + .probe = vcnl4035_probe, > + .remove = vcnl4035_remove, > + .id_table = vcnl4035_id, > +}; > + > +module_i2c_driver(vcnl4035_driver); > + > +MODULE_AUTHOR("Parthiban Nallathambi "); > +MODULE_DESCRIPTION("VCNL4035 Ambient Light Sensor driver"); > +MODULE_LICENSE("GPL v2"); > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH 2/2] iio: adc: Add Spreadtrum SC27XX PMICs ADC support
t sc27xx_adc_data *sc27xx_data; > + struct iio_dev *indio_dev; > + const void *data; > + int ret; > + > + data = of_device_get_match_data(&pdev->dev); > + if (!data) { > + dev_err(&pdev->dev, "failed to get match data\n"); > + return -EINVAL; > + } > + > + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*sc27xx_data)); > + if (!indio_dev) > + return -ENOMEM; > + > + sc27xx_data = iio_priv(indio_dev); > + > + sc27xx_data->regmap = dev_get_regmap(pdev->dev.parent, NULL); > + if (!sc27xx_data->regmap) { > + dev_err(&pdev->dev, "failed to get ADC regmap\n"); > + return -ENODEV; > + } > + > + ret = of_property_read_u32(np, "reg", &sc27xx_data->base); > + if (ret) { > + dev_err(&pdev->dev, "failed to get ADC base address\n"); > + return ret; > + } > + > + sc27xx_data->irq = platform_get_irq(pdev, 0); > + if (sc27xx_data->irq < 0) { > + dev_err(&pdev->dev, "failed to get ADC irq number\n"); > + return sc27xx_data->irq; > + } > + > + ret = of_hwspin_lock_get_id(np, 0); > + if (ret < 0) { > + dev_err(&pdev->dev, "failed to get hwspinlock id\n"); > + return ret; > + } > + > + sc27xx_data->hwlock = hwspin_lock_request_specific(ret); > + if (!sc27xx_data->hwlock) { > + dev_err(&pdev->dev, "failed to request hwspinlock\n"); > + return -ENXIO; > + } > + > + init_completion(&sc27xx_data->completion); > + > + /* > + * Different PMIC ADC controllers can have differnt channel voltage different > + * ratios, so we should save the implementation of getting voltage > + * ratio for corresponding PMIC ADC in the device data structure. > + */ > + sc27xx_data->get_volt_ratio = data; > + sc27xx_data->dev = &pdev->dev; > + > + ret = sc27xx_adc_enable(sc27xx_data); > + if (ret) { > + dev_err(&pdev->dev, "failed to enable ADC module\n"); > + goto free_hwlock; > + } > + > + ret = devm_request_threaded_irq(&pdev->dev, sc27xx_data->irq, NULL, > + sc27xx_adc_isr, IRQF_ONESHOT, > + pdev->name, sc27xx_data); > + if (ret) { > + dev_err(&pdev->dev, "failed to request ADC irq\n"); > + goto disable_adc; > + } > + > + indio_dev->dev.parent = &pdev->dev; > + indio_dev->name = dev_name(&pdev->dev); > + indio_dev->modes = INDIO_DIRECT_MODE; > + indio_dev->info = &sc27xx_info; > + indio_dev->channels = sc27xx_channels; > + indio_dev->num_channels = ARRAY_SIZE(sc27xx_channels); > + ret = devm_iio_device_register(&pdev->dev, indio_dev); > + if (ret) { > + dev_err(&pdev->dev, "could not register iio (ADC)"); > + goto disable_adc; > + } > + > + platform_set_drvdata(pdev, indio_dev); > + return 0; > + > +disable_adc: > + sc27xx_adc_disable(sc27xx_data); > +free_hwlock: > + hwspin_lock_free(sc27xx_data->hwlock); > + return ret; > +} > + > +static int sc27xx_adc_remove(struct platform_device *pdev) > +{ > + struct iio_dev *indio_dev = platform_get_drvdata(pdev); > + struct sc27xx_adc_data *sc27xx_data = iio_priv(indio_dev); > + > + sc27xx_adc_disable(sc27xx_data); > + hwspin_lock_free(sc27xx_data->hwlock); > + return 0; > +} > + > +static const struct of_device_id sc27xx_adc_of_match[] = { > + { > + .compatible = "sprd,sc2731-adc", > + .data = (void *)&sc27xx_adc_2731_ratio, > + }, > + { } > +}; > + > +static struct platform_driver sc27xx_adc_driver = { > + .probe = sc27xx_adc_probe, > + .remove = sc27xx_adc_remove, > + .driver = { > + .name = "sc27xx-adc", > + .of_match_table = sc27xx_adc_of_match, > + }, > +}; > + > +module_platform_driver(sc27xx_adc_driver); > + > +MODULE_AUTHOR("Freeman Liu "); > +MODULE_DESCRIPTION("Spreadtrum SC27XX ADC Driver"); > +MODULE_LICENSE("GPL v2"); > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH 1/2] iio: dac: Add AD5758 support
out_range = i; > + break; > + } > + } > + > + if (i == ARRAY_SIZE(ad5758_min_max_table)) > + dev_warn(&st->spi->dev, "range invalid, using default"); \n missing > + } else { > + dev_dbg(&st->spi->dev, > + "Missing \"range\" property, using default\n"); > + } > + > + st->sr_config[0] = 1; > + st->sr_config[1] = 16000; > + st->sr_config[2] = 4; > + if (!device_property_read_u32_array(&st->spi->dev, "adi,slew", > + tmparray, 3)) { > + st->sr_config[0] = tmparray[0]; > + > + index = ad5758_get_array_index(ad5758_slew_rate_clk, > +ARRAY_SIZE(ad5758_slew_rate_clk), > +tmparray[1]); > + > + if (index < 0) > + dev_warn(&st->spi->dev, > + "slew rate clock out of range, using default"); \n missing > + else > + st->sr_config[1] = index; > + > + index = ad5758_get_array_index(ad5758_slew_rate_step, > + ARRAY_SIZE(ad5758_slew_rate_step), > + tmparray[2]); > + > + if (index < 0) > + dev_warn(&st->spi->dev, > + "slew rate step out of range, using default"); \n missing > + else > + st->sr_config[2] = index; > + } else { > + dev_dbg(&st->spi->dev, > + "Missing \"slew\" property, using default\n"); > + } > +} > + > +static int ad5758_init(struct ad5758_state *st) > +{ > + int regval, ret; > + > + ad5758_parse_dt(st); > + > + /* Disable CRC checks */ > + ret = ad5758_crc_disable(st); > + if (ret < 0) > + return ret; > + > + /* Perform a software reset */ > + ret = ad5758_soft_reset(st); > + if (ret < 0) > + return ret; > + > + /* Disable CRC checks */ > + ret = ad5758_crc_disable(st); > + if (ret < 0) > + return ret; > + > + /* Perform a calibration memory refresh */ > + ret = ad5758_calib_mem_refresh(st); > + if (ret < 0) > + return ret; > + > + regval = ad5758_spi_reg_read(st, AD5758_DIGITAL_DIAG_RESULTS); > + if (regval < 0) > + return regval; > + > + /* Clear all the error flags */ > + ret = ad5758_spi_reg_write(st, AD5758_DIGITAL_DIAG_RESULTS, regval); > + if (ret < 0) > + return ret; > + > + /* Set the dc-to-dc current limit */ > + ret = ad5758_set_dc_dc_ilim(st, st->dc_dc_ilim); > + if (ret < 0) > + return ret; > + > + /* Configure the dc-to-dc controller mode */ > + ret = ad5758_set_dc_dc_conv_mode(st, st->dc_dc_mode); > + if (ret < 0) > + return ret; > + > + /* Configure the output range */ > + ret = ad5758_set_out_range(st, st->out_range); > + if (ret < 0) > + return ret; > + > + /* Enable Slew Rate Control, set the slew rate clock and step */ > + ret = ad5758_slew_rate_config(st, st->sr_config[0], > + st->sr_config[1], st->sr_config[2]); > + if (ret < 0) > + return ret; > + > + /* Enable the VIOUT fault protection switch (FPS is closed) */ > + ret = ad5758_fault_prot_switch_en(st, 1); > + if (ret < 0) > + return ret; > + > + /* Power up the DAC and internal (INT) amplifiers */ > + ret = ad5758_internal_buffers_en(st, 1); > + if (ret < 0) > + return ret; > + > + /* Enable VIOUT */ > + ret = ad5758_spi_write_mask(st, AD5758_DAC_CONFIG, > + AD5758_DAC_CONFIG_OUT_EN_MSK, > + AD5758_DAC_CONFIG_OUT_EN_MODE(1)); return directly, i.e. return ad5758_spi... > + > + return ret; > +} > + > +static int ad5758_probe(struct spi_device *spi) > +{ > + struct ad5758_state *st; > + struct iio_dev *indio_dev; > + int ret; > + > + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); > + if (!indio_dev) > + return -ENOMEM; > + > + st = iio_priv(indio_dev); > + spi_set_drvdata(spi, indio_dev); > + > + st->spi = spi; > + > + indio_dev->dev.parent = &spi->dev; > + indio_dev->name = spi_get_device_id(spi)->name; > + indio_dev->info = &ad5758_info; > + indio_dev->modes = INDIO_DIRECT_MODE; > + indio_dev->channels = ad5758_channels; > + indio_dev->num_channels = ARRAY_SIZE(ad5758_channels); > + > + mutex_init(&st->lock); > + > + ret = ad5758_init(st); > + if (ret < 0) { > + dev_err(&spi->dev, "AD5758 init failed\n"); > + return ret; > + } > + > + ret = iio_device_register(indio_dev); > + if (ret) { > + dev_err(&spi->dev, "Failed to register iio device\n"); is the message really necessary/likely? > + return ret; > + } > + > + return 0; > +} > + > +static const struct spi_device_id ad5758_id[] = { > + { "ad5758", 0 }, > + {} > +}; > +MODULE_DEVICE_TABLE(spi, ad5758_id); > + > +static struct spi_driver ad5758_driver = { > + .driver = { > + .name = KBUILD_MODNAME, > + }, > + .probe = ad5758_probe, > + .id_table = ad5758_id, > +}; > + > +module_spi_driver(ad5758_driver); > + > +MODULE_AUTHOR("Stefan Popa "); > +MODULE_DESCRIPTION("Analog Devices AD5758 DAC"); > +MODULE_LICENSE("GPL v2"); > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH v3 1/4] staging: iio: ad7780: add gain & filter gpio support
elta, >unsigned int raw_sample) > { > @@ -126,10 +187,8 @@ static int ad7780_postprocess_sample(struct > ad_sigma_delta *sigma_delta, > return -EIO; > > if (chip_info->is_ad778x) { > - if (raw_sample & AD7780_GAIN) > - st->gain = 1; > - else > - st->gain = 128; > + st->gain = ad778x_gain[raw_sample & AD7780_GAIN]; > + st->odr = ad778x_odr_avail[raw_sample & AD7780_FILTER]; > } > > return 0; > @@ -173,6 +232,7 @@ static const struct ad7780_chip_info > ad7780_chip_info_tbl[] = { > > static const struct iio_info ad7780_info = { > .read_raw = ad7780_read_raw, > + .write_raw = ad7780_write_raw, > }; > > static int ad7780_probe(struct spi_device *spi) > @@ -222,6 +282,29 @@ static int ad7780_probe(struct spi_device *spi) > goto error_disable_reg; > } > > + if (st->chip_info->is_ad778x) { > + st->gain_gpio = devm_gpiod_get_optional(&spi->dev, > + "gain", > + GPIOD_OUT_HIGH); > + if (IS_ERR(st->gain_gpio)) { > + ret = PTR_ERR(st->gain_gpio); > + dev_err(&spi->dev, "Failed to request gain GPIO: > %d\n", > + ret); > + goto error_disable_reg; > + } > + > + st->filter_gpio = devm_gpiod_get_optional(&spi->dev, > + "filter", > + GPIOD_OUT_HIGH); > + if (IS_ERR(st->filter_gpio)) { > + ret = PTR_ERR(st->filter_gpio); > + dev_err(&spi->dev, > + "Failed to request filter GPIO: %d\n", > + ret); > + goto error_disable_reg; > + } > + } > + > ret = ad_sd_setup_buffer_and_trigger(indio_dev); > if (ret) > goto error_disable_reg; > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH v2 1/3] iio: chemical: add support for Plantower PMS7003 sensor
gt; + magic = get_unaligned_be16(buf); > + if (magic != PMS7003_MAGIC) > + return 2; > + > + num = get_unaligned_be16(buf + 2); 2 is sizeof(MAGIC) > + if (num <= PMS7003_MAX_DATA_LENGTH) { > + frame->expected_length = num; > + frame->length = 0; > + } > + > + return 4; > + } > + > + num = min(size, (size_t)(frame->expected_length - frame->length)); > + memcpy(frame->data + frame->length, buf, num); > + frame->length += num; > + > + if (frame->length == frame->expected_length) { > + if (pms7003_frame_is_okay(frame)) > + complete(&state->frame_ready); > + > + frame->expected_length = 0; > + } > + > + return num; > +} > + > +static const struct serdev_device_ops pms7003_serdev_ops = { > + .receive_buf = pms7003_receive_buf, > + .write_wakeup = serdev_device_write_wakeup, > +}; > + > +static void pms7003_stop(void *data) > +{ > + struct pms7003_state *state = data; > + > + pms7003_do_cmd(state, CMD_SLEEP); > +} > + > +static const unsigned long pms7003_scan_masks[] = { 0x07, 0x00 }; > + > +static int pms7003_probe(struct serdev_device *serdev) > +{ > + struct pms7003_state *state; > + struct iio_dev *indio_dev; > + int ret; > + > + indio_dev = devm_iio_device_alloc(&serdev->dev, sizeof(*state)); > + if (!indio_dev) > + return -ENOMEM; > + > + state = iio_priv(indio_dev); > + serdev_device_set_drvdata(serdev, indio_dev); > + state->serdev = serdev; > + indio_dev->dev.parent = &serdev->dev; > + indio_dev->info = &pms7003_info; > + indio_dev->name = PMS7003_DRIVER_NAME; > + indio_dev->channels = pms7003_channels, > + indio_dev->num_channels = ARRAY_SIZE(pms7003_channels); > + indio_dev->modes = INDIO_DIRECT_MODE; > + indio_dev->available_scan_masks = pms7003_scan_masks; > + > + mutex_init(&state->lock); > + init_completion(&state->frame_ready); > + > + serdev_device_set_client_ops(serdev, &pms7003_serdev_ops); > + ret = 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 = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE); > + if (ret) > + return ret; > + > + ret = pms7003_do_cmd(state, CMD_WAKEUP); > + if (ret) { > + dev_err(&serdev->dev, "failed to wakeup sensor\n"); > + return ret; > + } > + > + ret = pms7003_do_cmd(state, CMD_ENTER_PASSIVE_MODE); > + if (ret) { > + dev_err(&serdev->dev, "failed to enter passive mode\n"); > + return ret; > + } > + > + ret = devm_add_action_or_reset(&serdev->dev, pms7003_stop, state); > + if (ret) > + return ret; > + > + ret = devm_iio_triggered_buffer_setup(&serdev->dev, indio_dev, NULL, > + pms7003_trigger_handler, NULL); > + if (ret) > + return ret; > + > + return devm_iio_device_register(&serdev->dev, indio_dev); > +} > + > +static const struct of_device_id pms7003_of_match[] = { > + { .compatible = "plantower,pms7003" }, > + { } > +}; > +MODULE_DEVICE_TABLE(of, pms7003_of_match); > + > +static struct serdev_device_driver pms7003_driver = { > + .driver = { > + .name = PMS7003_DRIVER_NAME, > + .of_match_table = pms7003_of_match, > + }, > + .probe = pms7003_probe, > +}; > +module_serdev_device_driver(pms7003_driver); > + > +MODULE_AUTHOR("Tomasz Duszynski "); > +MODULE_DESCRIPTION("Plantower PMS7003 particulate matter sensor driver"); > +MODULE_LICENSE("GPL v2"); > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH 2/4] iio/proximity: add mb12x2 driver to Kconfig and Makefile
On Sun, 24 Feb 2019, Andreas Klinger wrote: > Makefile and Kconfig: add configuration for mb12x2 ultrasonic proximity > driver > > diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile > index 6d031f903c4c..c56b72a8be87 100644 > --- a/drivers/iio/proximity/Makefile > +++ b/drivers/iio/proximity/Makefile > @@ -6,6 +6,7 @@ > # When adding new entries keep the list in alphabetical order > obj-$(CONFIG_AS3935) += as3935.o > obj-$(CONFIG_ISL29501) += isl29501.o > +obj-$(CONFIG_MB12X2) += mb12x2.o alphabetic order please > obj-$(CONFIG_LIDAR_LITE_V2) += pulsedlight-lidar-lite-v2.o > obj-$(CONFIG_RFD77402) += rfd77402.o > obj-$(CONFIG_SRF04) += srf04.o > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH 3/4] mb12x2.c: add mb12x2 ultrasonic distance iio sensor
goto err; > + > + mutex_lock(&data->lock); > + > + data->buffer[0] = sensor_data; > + iio_push_to_buffers_with_timestamp(indio_dev, > + data->buffer, pf->timestamp); > + > + mutex_unlock(&data->lock); > +err: > + iio_trigger_notify_done(indio_dev->trig); > + return IRQ_HANDLED; > +} > + > +static int mb12x2_read_raw(struct iio_dev *indio_dev, > + struct iio_chan_spec const *channel, int *val, > + int *val2, long mask) > +{ > + struct mb12x2_data *data = iio_priv(indio_dev); > + int ret; > + > + if (channel->type != IIO_DISTANCE) > + return -EINVAL; > + > + switch (mask) { > + case IIO_CHAN_INFO_RAW: > + ret = mb12x2_read_distance(data); > + if (ret < 0) > + return ret; > + *val = ret; > + return IIO_VAL_INT; > + case IIO_CHAN_INFO_SCALE: > + /* 1 LSB is 1 cm */ > + *val = 0; > + *val2 = 1; > + return IIO_VAL_INT_PLUS_MICRO; > + default: > + return -EINVAL; > + } > +} > + > +static const struct iio_chan_spec mb12x2_channels[] = { > + { > + .type = IIO_DISTANCE, > + .info_mask_separate = > + BIT(IIO_CHAN_INFO_RAW) | > + BIT(IIO_CHAN_INFO_SCALE), > + .scan_index = 0, > + .scan_type = { > + .sign = 's', > + .realbits = 16, > + .storagebits = 16, > + .endianness = IIO_CPU, > + }, > + }, > + IIO_CHAN_SOFT_TIMESTAMP(1), > +}; > + > +static const struct iio_info mb12x2_info = { > + .read_raw = mb12x2_read_raw, > +}; > + > +static int mb12x2_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + struct iio_dev *indio_dev; > + struct mb12x2_data *data; > + int ret; > + struct device *dev = &client->dev; > + > + if (!i2c_check_functionality(client->adapter, > + I2C_FUNC_SMBUS_READ_BYTE | > + I2C_FUNC_SMBUS_WRITE_BYTE)) > + return -ENODEV; > + > + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); > + if (!indio_dev) > + return -ENOMEM; > + > + data = iio_priv(indio_dev); > + i2c_set_clientdata(client, indio_dev); > + data->client = client; > + > + indio_dev->info = &mb12x2_info; > + indio_dev->name = id->name; > + indio_dev->dev.parent = dev; > + indio_dev->modes = INDIO_DIRECT_MODE; > + indio_dev->channels = mb12x2_channels; > + indio_dev->num_channels = ARRAY_SIZE(mb12x2_channels); > + > + mutex_init(&data->lock); > + > + init_completion(&data->ranging); > + > + data->gpiod_status = devm_gpiod_get(dev, "status", GPIOD_IN); > + if (IS_ERR(data->gpiod_status)) { I'd rather not do a newline at the start of a block (matter of taste) > + > + if (PTR_ERR(data->gpiod_status) == -ENOENT) { > + > + dev_warn(dev, "no status gpio --> use sleep instead\n"); > + data->gpiod_status = NULL; > + } else { > + > + dev_err(dev, "cannot setup gpio; err=%ld\n", > + PTR_ERR(data->gpiod_status)); > + return PTR_ERR(data->gpiod_status); > + } > + } > + > + if (data->gpiod_status) { > + > + data->irqnr = gpiod_to_irq(data->gpiod_status); > + if (data->irqnr < 0) { > + dev_err(dev, "gpiod_to_irq: %d\n", data->irqnr); > + return data->irqnr; > + } > + > + ret = devm_request_irq(dev, data->irqnr, mb12x2_handle_irq, > + IRQF_TRIGGER_FALLING, id->name, indio_dev); > + if (ret < 0) { > + dev_err(dev, "request_irq: %d\n", ret); > + return ret; > + } > + } > + > + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, > + iio_pollfunc_store_time, mb12x2_trigger_handler, NULL); > + if (ret < 0) { > + dev_err(dev, "setup of iio triggered buffer failed\n"); > + return ret; > + } > + > + return devm_iio_device_register(dev, indio_dev); > +} > + > +static const struct of_device_id of_mb12x2_match[] = { > + { .compatible = "maxbotix,mb12x2", }, > + {}, > +}; > + > +MODULE_DEVICE_TABLE(of, of_mb12x2_match); > + > +static const struct i2c_device_id mb12x2_id[] = { > + { "maxbotix-mb12x2", }, > + { } > +}; > +MODULE_DEVICE_TABLE(i2c, mb12x2_id); > + > +static struct i2c_driver mb12x2_driver = { > + .driver = { > + .name = "maxbotix-mb12x2", > + .of_match_table = of_mb12x2_match, > + }, > + .probe = mb12x2_probe, > + .id_table = mb12x2_id, > +}; > +module_i2c_driver(mb12x2_driver); > + > +MODULE_AUTHOR("Andreas Klinger "); > +MODULE_DESCRIPTION("Maxbotix I2CXL-MB12X2-EZ i2c ultrasonic ranger driver"); > +MODULE_LICENSE("GPL"); > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH v2] Added AMS tsl2591 driver implementation
> Driver implementation for AMS/TAOS tsl2591 ambient light sensor. please see comments below > > This driver supports configuration via device tree and sysfs. > Supported channels for raw infrared light, raw visible light, > raw combined light and combined lux value. > The driver additionally supports iio events on lower and > upper thresholds. > > This is a very-high sensitivity light-to-digital converter that > transforms light intensity into a digital signal. > > Datasheet Available at: https://ams.com/tsl25911 > > Signed-off-by: Joe Sandom > --- > Changes in v2: > - Significant modifications to ensure compatibility with standard userspace > ABI > - Removed unneccesary device tree properties where configuration can be > handled either through sysfs or event properties > - Improved readability of code by inlining where appropriate and > modified naming in areas for clarity > - Cleaned up Makefile and Kconfig to follow correct ordering > - Removed noisy print outs and unnecessary use of __func__ > - All chip configuration done in probe before exposing interfaces to > userspace to avoid race conditions > - Removed having events directly from probe, now follows appropriate > event enable path > - Removed channel showing visible light only as no real use case for it. > Channels are now IR (raw), Visible + IR (raw) and Lux. > - Cleaned up binding file to reflect removal of device tree properties > > NOTE: > Apologies that v2 took a while to get submitted, making the changes to > ensure compatibility with the standard userspace ABI took a lot longer > than anticipated! mostly with the conversion of persist cycles to time > in seconds with respect to ADC integration time amongst other things. > > .../bindings/iio/light/amstaos,tsl2591.yaml | 50 + > drivers/iio/light/Kconfig | 11 + > drivers/iio/light/Makefile|1 + > drivers/iio/light/tsl2591.c | 1247 + > 4 files changed, 1309 insertions(+) > create mode 100644 > Documentation/devicetree/bindings/iio/light/amstaos,tsl2591.yaml > create mode 100644 drivers/iio/light/tsl2591.c > > diff --git a/Documentation/devicetree/bindings/iio/light/amstaos,tsl2591.yaml > b/Documentation/devicetree/bindings/iio/light/amstaos,tsl2591.yaml > new file mode 100644 > index ..030d0bbfdc7b > --- /dev/null > +++ b/Documentation/devicetree/bindings/iio/light/amstaos,tsl2591.yaml > @@ -0,0 +1,50 @@ > +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) > +%YAML 1.2 > +--- > +$id: http://devicetree.org/schemas/iio/light/tsl2591.yaml# > +$schema: http://devicetree.org/meta-schemas/core.yaml# > + > +title: AMS/TAOS TSL2591 Ambient Light Sensor (ALS) > + > +maintainers: > + - Joe Sandom > + > +description: | > + AMS/TAOS TSL2591 is a very-high sensitivity > + light-to-digital converter that transforms light intensity into a digital > + signal. > + > +properties: > + compatible: > +const: amstaos,tsl2591 > + > + reg: > +maxItems: 1 > + > + interrupts: > +maxItems: 1 > +description: > + Interrupt (INT:Pin 2) Active low. Should be set to > IRQ_TYPE_EDGE_FALLING. > + interrupt is used to detect if lux value has reached below or above > defined Interrupt is it lux always? or just intensity in some cases > + threshold values. > + > +required: > + - compatible > + - reg > + > +additionalProperties: false > + > +examples: > + - | > +#include > +i2c { > +#address-cells = <1>; > +#size-cells = <0>; > + > +tsl2591@29 { > +compatible = "amstaos,tsl2591"; > +reg = <0x29>; > +interrupts = <20 IRQ_TYPE_EDGE_FALLING>; > + }; > +}; > +... > diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig > index 33ad4dd0b5c7..0b0950a94925 100644 > --- a/drivers/iio/light/Kconfig > +++ b/drivers/iio/light/Kconfig > @@ -501,6 +501,17 @@ config TSL2583 > Provides support for the TAOS tsl2580, tsl2581 and tsl2583 devices. > Access ALS data via iio, sysfs. > > +config TSL2591 > +tristate "TAOS TSL2591 ambient light sensor" > +depends on I2C > +help > + Select Y here for support of the AMS/TAOS TSL2591 ambient light > sensor, > + featuring visible and infrared lux output. Access als data via I think lux is defined for visible light only? als -> ALS > + iio and sysfs. Supports iio_events. > + > + To compile this driver as a module, select M: the > + module will be called tsl2591. > + > config TSL2772 > tristate "TAOS TSL/TMD2x71 and TSL/TMD2x72 Family of light and > proximity sensors" > depends on I2C > diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile > index ea376deaca54..d10912faf964 100644 > --- a/drivers/iio/light/Makefile > +++ b/drivers/iio/light/Makefile > @@ -48,6 +48,7 @@ obj-$(CONFIG_ST_UVIS25_SPI) += st_uvis25_spi.o > obj-$(CONFIG
Re: [PATCH] iio: tmp006: Set correct iio name
> > It's clearly wrong. But the problem is there might be an application that > > depends on the wrong behavior, the driver has been around for 2.5 years. So > > it's difficult to fix. We might just go ahead in this case and take the > > chance that nobody will complain. But if somebody complains this will bring > > us the wrath of the Linus. > > Not if you put it into next, test it, then into a new release as early as > possible (for -rc1), clearly document that it's got a user visible change > that should not matter with instructions if anyone hits this as a > bisection for their app failing to email so you know and can revert it. is this the only driver doing it wrong? pmeerw@pmeerw:/var/git/linux/drivers/iio$ rgrep "indio_dev->name = dev_name" . ./imu/inv_mpu6050/inv_mpu_core.c: indio_dev->name = dev_name(dev); ./light/lm3533-als.c: indio_dev->name = dev_name(&pdev->dev); ./dac/vf610_dac.c: indio_dev->name = dev_name(&pdev->dev); ./dac/stx104.c: indio_dev->name = dev_name(dev); ./dac/lpc18xx_dac.c:indio_dev->name = dev_name(&pdev->dev); ./adc/mcp3422.c:indio_dev->name = dev_name(&client->dev); ./adc/at91-sama5d2_adc.c: indio_dev->name = dev_name(&pdev->dev); ./adc/vf610_adc.c: indio_dev->name = dev_name(&pdev->dev); ./adc/ti_am335x_adc.c: indio_dev->name = dev_name(&pdev->dev); ./adc/nau7802.c:indio_dev->name = dev_name(&client->dev); ./adc/da9150-gpadc.c: indio_dev->name = dev_name(dev); ./adc/lpc18xx_adc.c:indio_dev->name = dev_name(&pdev->dev); ./adc/rockchip_saradc.c:indio_dev->name = dev_name(&pdev->dev); ./adc/imx7d_adc.c: indio_dev->name = dev_name(&pdev->dev); ./adc/cc10001_adc.c:indio_dev->name = dev_name(&pdev->dev); ./adc/berlin2-adc.c:indio_dev->name = dev_name(&pdev->dev); ./adc/exynos_adc.c: indio_dev->name = dev_name(&pdev->dev); ./temperature/tmp006.c: indio_dev->name = dev_name(&client->dev); ./chemical/ams-iaq-core.c: indio_dev->name = dev_name(&client->dev); ./chemical/vz89x.c: indio_dev->name = dev_name(&client->dev); ./humidity/si7005.c:indio_dev->name = dev_name(&client->dev); ./humidity/hdc100x.c: indio_dev->name = dev_name(&client->dev); ./humidity/si7020.c:indio_dev->name = dev_name(&client->dev); regards, p. -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH v2] iio: accel: Add support for Freescale MMA7660FC
vice_unregister(indio_dev); > + > + return mma7660_set_mode(iio_priv(indio_dev), MMA7660_MODE_STANDBY); > +} > + > +#ifdef CONFIG_PM_SLEEP > +static int mma7660_suspend(struct device *dev) > +{ > + struct mma7660_data *data; > + > + data = iio_priv(i2c_get_clientdata(to_i2c_client(dev))); > + > + return mma7660_set_mode(data, MMA7660_MODE_STANDBY); > +} > + > +static int mma7660_resume(struct device *dev) > +{ > + struct mma7660_data *data; > + > + data = iio_priv(i2c_get_clientdata(to_i2c_client(dev))); > + > + return mma7660_set_mode(data, MMA7660_MODE_ACTIVE); > +} > + > +static SIMPLE_DEV_PM_OPS(mma7660_pm_ops, mma7660_suspend, mma7660_resume); > + > +#define MMA7660_PM_OPS (&mma7660_pm_ops) > +#else > +#define MMA7660_PM_OPS NULL > +#endif > + > +static const struct i2c_device_id mma7660_i2c_id[] = { > + {"MMA7660", 0}, really uppercase? > + {} > +}; > + > +static const struct acpi_device_id mma7660_acpi_id[] = { > + {"MMA7660", 0}, > + {} > +}; > + > +MODULE_DEVICE_TABLE(acpi, mma7660_acpi_id); > + > +static struct i2c_driver mma7660_driver = { > + .driver = { > + .name = "mma7660", > + .pm = MMA7660_PM_OPS, > + .acpi_match_table = ACPI_PTR(mma7660_acpi_id), > + }, > + .probe = mma7660_probe, > + .remove = mma7660_remove, > + .id_table = mma7660_i2c_id, > +}; > + > +module_i2c_driver(mma7660_driver); > + > +MODULE_AUTHOR("Constantin Musca "); > +MODULE_DESCRIPTION("Freescale MMA7660FC 3-Axis Accelerometer driver"); > +MODULE_LICENSE("GPL v2"); > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: getting mysterious (to me) EINVAL from inotify_rm_watch
Hi, > I am trying to add inotify support to my tail implementation (for -F). > This is what happens: > > inotify_init() = 4 > inotify_add_watch(4, "/tmp/foo", IN_MODIFY) = 1 > inotify_rm_watch(4, 1) = -1 EINVAL (Invalid argument) > inotify_add_watch(4, "/tmp/foo", IN_MODIFY) = 2 > > There is also some polling, some reading and some statting going on here, but > those are on other descriptors than 4 so they should not matter). > > Can somebody explain the EINVAL I'm getting from inotify_rm_watch to me? > This is a stock kernel 4.5.0. #include #include int main() { int fd, i, j; printf("init %d\n", fd=inotify_init()); // 3 printf("add %d\n", i=inotify_add_watch(fd, "/tmp/foo", IN_MODIFY)); // 1 printf("rm %d\n", inotify_rm_watch(fd, i)); // 0 printf("add %d\n", j=inotify_add_watch(fd, "/tmp/foo", IN_MODIFY)); // 2 return 0; } Ubuntu kernel x86_64 4.4.0-21, seems to work here so we have to guess what's going on between _add and _rm? regards, p. -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH 1/2] iio: generic_buffer: Cleanup when receiving signals
= size_from_channelarray(channels, num_channels); > data = malloc(scan_size * buf_len); > if (!data) { > ret = -ENOMEM; > - goto error_free_buf_dir_name; > + return ret; > } > > ret = asprintf(&buffer_access, "/dev/iio:device%d", dev_num); > if (ret < 0) { > ret = -ENOMEM; > - goto error_free_data; > + return ret; > } > > /* Attempt to open non blocking the access dev */ > @@ -498,7 +554,7 @@ int main(int argc, char **argv) > if (fp == -1) { /* TODO: If it isn't there make the node */ > ret = -errno; > fprintf(stderr, "Failed to open %s\n", buffer_access); > - goto error_free_buffer_access; > + return ret; > } > > for (j = 0; j < num_loops; j++) { > @@ -510,8 +566,7 @@ int main(int argc, char **argv) > > ret = poll(&pfd, 1, -1); > if (ret < 0) { > - ret = -errno; > - goto error_close_buffer_access; > + return -errno; > } else if (ret == 0) { > continue; > } > @@ -536,46 +591,5 @@ int main(int argc, char **argv) >num_channels); > } > > - /* Stop the buffer */ > - ret = write_sysfs_int("enable", buf_dir_name, 0); > - if (ret < 0) > - goto error_close_buffer_access; > - > - if (!notrigger) > - /* Disconnect the trigger - just write a dummy name. */ > - ret = write_sysfs_string("trigger/current_trigger", > - dev_dir_name, "NULL"); > - if (ret < 0) > - fprintf(stderr, "Failed to write to %s\n", > - dev_dir_name); > - > -error_close_buffer_access: > - if (close(fp) == -1) > - perror("Failed to close buffer"); > - > -error_free_buffer_access: > - free(buffer_access); > -error_free_data: > - free(data); > -error_free_buf_dir_name: > - free(buf_dir_name); > -error_free_channels: > - for (i = num_channels - 1; i >= 0; i--) { > - free(channels[i].name); > - free(channels[i].generic_name); > - } > - free(channels); > -error_free_triggername: > - if (datardytrigger) > - free(trigger_name); > -error_disable_channels: > - if (autochannels == AUTOCHANNELS_ACTIVE) { > - ret = enable_disable_all_channels(dev_dir_name, 0); > - if (ret) > - fprintf(stderr, "Failed to disable all channels\n"); > - } > -error_free_dev_dir_name: > - free(dev_dir_name); > - > - return ret; > + return 0; > } > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH v3] iio: max5487: Add support for Maxim digital potentiometers
int *val, int *val2, long mask) > +{ > + struct max5487_data *data = iio_priv(indio_dev); > + > + if (mask != IIO_CHAN_INFO_SCALE) > + return -EINVAL; > + > + *val = 1000 * data->kohms; > + *val2 = MAX5487_MAX_POS; > + > + return IIO_VAL_FRACTIONAL; > +} > + > +static int max5487_write_raw(struct iio_dev *indio_dev, > + struct iio_chan_spec const *chan, > + int val, int val2, long mask) > +{ > + struct max5487_data *data = iio_priv(indio_dev); > + > + if (mask != IIO_CHAN_INFO_RAW) > + return -EINVAL; > + > + if (val < 0 || val > MAX5487_MAX_POS) > + return -EINVAL; > + > + return max5487_write_cmd(data->spi, chan->address | val); > +} > + > +static const struct iio_info max5487_info = { > + .read_raw = max5487_read_raw, > + .write_raw = max5487_write_raw, > + .driver_module = THIS_MODULE, > +}; > + > +static int max5487_spi_probe(struct spi_device *spi) > +{ > + struct iio_dev *indio_dev; > + struct max5487_data *data; > + const struct spi_device_id *id = spi_get_device_id(spi); > + int ret; > + > + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data)); > + if (!indio_dev) > + return -ENOMEM; > + > + dev_set_drvdata(&spi->dev, indio_dev); > + data = iio_priv(indio_dev); > + > + data->spi = spi; > + data->kohms = id->driver_data; > + > + indio_dev->info = &max5487_info; > + indio_dev->name = id->name; > + indio_dev->dev.parent = &spi->dev; > + indio_dev->modes = INDIO_DIRECT_MODE; > + indio_dev->channels = max5487_channels; > + indio_dev->num_channels = ARRAY_SIZE(max5487_channels); > + > + /* restore both wiper regs from NV regs */ > + ret = max5487_write_cmd(data->spi, MAX5487_COPY_NV_TO_AB); > + if (ret < 0) > + return ret; > + > + return iio_device_register(indio_dev); > +} > + > +static int max5487_spi_remove(struct spi_device *spi) > +{ > + struct iio_dev *indio_dev = dev_get_drvdata(&spi->dev); > + > + iio_device_unregister(indio_dev); > + > + /* save both wiper regs to NV regs */ > + return max5487_write_cmd(spi, MAX5487_COPY_AB_TO_NV); > +} > + > +static const struct spi_device_id max5487_id[] = { > + { "MAX5487", 10 }, > + { "MAX5488", 50 }, > + { "MAX5489", 100 }, > + { } > +}; > +MODULE_DEVICE_TABLE(spi, max5487_id); > + > +static const struct acpi_device_id max5487_acpi_match[] = { > + { "MAX5487", 10 }, > + { "MAX5488", 50 }, > + { "MAX5489", 100 }, > + { }, > +}; > +MODULE_DEVICE_TABLE(acpi, max5487_acpi_match); > + > +static struct spi_driver max5487_driver = { > + .driver = { > + .name = "max5487", > + .owner = THIS_MODULE, > + .acpi_match_table = ACPI_PTR(max5487_acpi_match), > + }, > + .id_table = max5487_id, > + .probe = max5487_spi_probe, > + .remove = max5487_spi_remove > +}; > +module_spi_driver(max5487_driver); > + > +MODULE_AUTHOR("Cristina-Gabriela Moraru "); > +MODULE_DESCRIPTION("max5487 SPI driver"); > +MODULE_LICENSE("GPL v2"); > -- Peter Meerwald-Stadler +43-664-218 (mobile)
[RFC] iio: Add driver for Silabs si1132, si1141/2/3 and si1145/6/7 ambient light, uv index and proximity sensors
From: Peter Meerwald The si114x supports x=1,2,3 IR LEDs for proximity sensing together with visible and IR ambient light sensing (ALS). Newer parts (si1132, si1145/6/7) can measure UV light and compute an UV index Arranging 3 IR LEDs in a triangular shape can be used for detection of swipe gestures (the present driver only measures the intensities, it does not process the data); there is an affordable reference design (via Digikey), see http://www.silabs.com/products/sensors/Pages/HID-USB-to-IR-Reference-Design.aspx Signed-off-by: Peter Meerwald --- This is the code I am currently working on, partly tested. I've stripped some features from earlier versions and added support for newer chips. I hope this is useful to Crestez Dan Leonard. I'm happy to review stuff as the code gets forged into shape and appreciate any testing. drivers/iio/light/Kconfig | 13 + drivers/iio/light/Makefile | 1 + drivers/iio/light/si1145.c | 855 + 3 files changed, 869 insertions(+) create mode 100644 drivers/iio/light/si1145.c diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 7c566f5..c362e63 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -266,6 +266,19 @@ config PA12203001 This driver can also be built as a module. If so, the module will be called pa12203001. +config SI1145 + tristate "SI1132 and SI1141/2/3/5/6/7 combined ALS, UV index and proximity sensor" + depends on I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say Y here if you want to build a driver for the Silicon Labs SI1132 or + SI1141/2/3/5/6/7 combined ambient light, UV index and proximity sensor + chips. + + To compile this driver as a module, choose M here: the module will be + called si1145. + config STK3310 tristate "STK3310 ALS and proximity sensor" depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index 6f2a3c6..c5768df 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_OPT3001) += opt3001.o obj-$(CONFIG_PA12203001) += pa12203001.o obj-$(CONFIG_RPR0521) += rpr0521.o obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o +obj-$(CONFIG_SI1145) += si1145.o obj-$(CONFIG_STK3310) += stk3310.o obj-$(CONFIG_TCS3414) += tcs3414.o obj-$(CONFIG_TCS3472) += tcs3472.o diff --git a/drivers/iio/light/si1145.c b/drivers/iio/light/si1145.c new file mode 100644 index 000..b35e5b2 --- /dev/null +++ b/drivers/iio/light/si1145.c @@ -0,0 +1,855 @@ +/* + * si1145.c - Support for Silabs SI1132 and SI1141/2/3/5/6/7 combined ambient + * light, UV index and proximity sensors + * + * Copyright 2014 Peter Meerwald-Stadler + * + * This file is subject to the terms and conditions of version 2 of + * the GNU General Public License. See the file COPYING in the main + * directory of this archive for more details. + * + * SI1132 (7-bit I2C slave address 0x60) + * SI1141/2/3 (7-bit I2C slave address 0x5a) + * SI1145/6/6 (7-bit I2C slave address 0x60) + * + * TODO: sample freq, IRQ, power management + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define SI1145_REG_PART_ID 0x00 +#define SI1145_REG_REV_ID 0x01 +#define SI1145_REG_SEQ_ID 0x02 +#define SI1145_REG_INT_CFG 0x03 +#define SI1145_REG_IRQ_ENABLE 0x04 +#define SI1145_REG_IRQ_MODE0x05 +#define SI1145_REG_HW_KEY 0x07 +#define SI1145_REG_MEAS_RATE 0x08 +#define SI1145_REG_PS_LED210x0f +#define SI1145_REG_PS_LED3 0x10 +#define SI1145_REG_PARAM_WR0x17 +#define SI1145_REG_COMMAND 0x18 +#define SI1145_REG_RESPONSE0x20 +#define SI1145_REG_IRQ_STATUS 0x21 +#define SI1145_REG_ALSVIS_DATA 0x22 +#define SI1145_REG_ALSIR_DATA 0x24 +#define SI1145_REG_PS1_DATA0x26 +#define SI1145_REG_PS2_DATA0x28 +#define SI1145_REG_PS3_DATA0x2a +#define SI1145_REG_AUX_DATA0x2c +#define SI1145_REG_PARAM_RD0x2e +#define SI1145_REG_CHIP_STAT 0x30 + +/* Helper to figure out PS_LED register / shift per channel */ +#define SI1145_PS_LED_REG(ch) \ + (((ch) == 2) ? SI1145_REG_PS_LED3 : SI1145_REG_PS_LED21) +#define SI1145_PS_LED_SHIFT(ch) \ + (((ch) == 1) ? 4 : 0) + +/* Parameter offsets */ +#define SI1145_PARAM_CHLIST0x01 +#define SI1145_PARAM_PSLED12_SELECT0x02 +#define SI1145_PARAM_PSLED3_SELECT 0x03 +#define SI1145_PARAM_PS_ENCODING 0x05 +#define SI1145_PARAM_ALS_ENCODING 0x06 +#define SI1145_PARAM_PS1_ADC_MUX 0x07 +#define SI1145_PARAM_PS2_ADC_MUX 0x08 +#d
Re: [PATCH 1/1] iio: light: Added CM36672 Proximity Sensor Driver.
request irq failed\n", > + __func__); > + return ret; > + } > + > + /* Enable interrupt if default request*/ > + if (chip->regs[CM36672_ADDR_PRX_CONF] & CM36672_PRX_INT_MASK) { > + ret = i2c_smbus_write_word_data(chip->client, > + CM36672_ADDR_PRX_CONF, > + chip->regs[CM36672_ADDR_PRX_CONF]); > + if (ret) { > + dev_err(&client->dev, > + "%s: enable interrupt failed\n", > + __func__); > + return ret; > + } > + } > + } > + > + ret = iio_device_register(indio_dev); > + if (ret) { > + dev_err(&client->dev, > + "%s: regist device failed\n", registering > + __func__); > + if (client->irq) > + free_irq(client->irq, indio_dev); > + } > + > + return 0; > +} > + > +static int cm36672_remove(struct i2c_client *client) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(client); > + struct cm36672_chip *chip = iio_priv(indio_dev); > + > + i2c_smbus_write_word_data( > + chip->client, > + CM36672_ADDR_PRX_CONF, > + CM36672_PRX_DISABLE); > + if (client->irq) > + free_irq(client->irq, indio_dev); > + iio_device_unregister(indio_dev); > + return 0; > +} > + > +static const struct i2c_device_id cm36672_id[] = { > + { "cm36672", 0 }, > + { } > +}; > + > +MODULE_DEVICE_TABLE(i2c, cm36672_id); > + > +static const struct of_device_id cm36672_of_match[] = { > + { .compatible = "capella,cm36672" }, > + { } > +}; > + > +#ifdef CONFIG_ACPI > +static const struct acpi_device_id cm36672_acpi_match[] = { > + { "CPLM6672", 0}, > + {}, > +}; > + > +MODULE_DEVICE_TABLE(acpi, cm36672_acpi_match); > +#endif > + > +#ifdef CONFIG_PM_SLEEP > +static int cm36672_suspend(struct device *dev) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); > + struct cm36672_chip *chip = iio_priv(indio_dev); > + int ret; > + > + chip->regs[CM36672_ADDR_PRX_CONF] |= CM36672_PRX_DISABLE; > + ret = i2c_smbus_write_byte_data( > + chip->client, > + CM36672_ADDR_PRX_CONF, > + chip->regs[CM36672_ADDR_PRX_CONF]); > + return ret; > +} > + > +static int cm36672_resume(struct device *dev) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); > + struct cm36672_chip *chip = iio_priv(indio_dev); > + int i, ret; > + > + chip->regs[CM36672_ADDR_PRX_CONF] &= ~CM36672_PRX_DISABLE; > + > + /* Initialize registers*/ whitespace before */ please > + for (i = 0; i < CM36672_REGS_NUM; i++) { > + ret = i2c_smbus_write_word_data( > + chip->client, > + i, > + chip->regs[i]); > + if (ret < 0) > + return ret; > + } > + > + return ret; > +} > + > +static const struct dev_pm_ops cm36672_pm_ops = { > + SET_SYSTEM_SLEEP_PM_OPS(cm36672_suspend, cm36672_resume)}; > +#endif > + > +static struct i2c_driver cm36672_driver = { > + .driver = { > + .name = CM36672_DRIVER_NAME, > + .of_match_table = cm36672_of_match, > +#ifdef CONFIG_ACPI > + .acpi_match_table = ACPI_PTR(cm36672_acpi_match), > +#endif > + .owner = THIS_MODULE, > +#ifdef CONFIG_PM_SLEEP > + .pm = &cm36672_pm_ops, > +#endif > + }, > + .id_table = cm36672_id, > + .probe = cm36672_probe, > + .remove = cm36672_remove, > +}; > + > +module_i2c_driver(cm36672_driver); > + > +MODULE_AUTHOR("Kevin Tsai "); > +MODULE_DESCRIPTION("CM36672 proximity sensor driver"); > +MODULE_LICENSE("GPL v2"); > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH v2 1/2] iio: ina2xx: add support for TI INA2xx Power Monitors
chip->task = kthread_run(ina2xx_capture_thread, (void *)indio_dev, > + "ina2xx-%uus", sampling_us); > + > + return PTR_ERR_OR_ZERO(chip->task); > +} > + > +int ina2xx_buffer_disable(struct iio_dev *indio_dev) > +{ > + struct ina2xx_chip_info *chip = iio_priv(indio_dev); > + > + if (chip->task) { > + kthread_stop(chip->task); > + chip->task = NULL; > + } > + > + return 0; > +} > + > +static const struct iio_buffer_setup_ops ina2xx_setup_ops = { > + .postenable = &ina2xx_buffer_enable, > + .postdisable = &ina2xx_buffer_disable, > +}; > + > +static int ina2xx_debug_reg(struct iio_dev *indio_dev, > + unsigned reg, unsigned writeval, unsigned *readval) > +{ > + struct ina2xx_chip_info *chip = iio_priv(indio_dev); > + > + if (!readval) > + return regmap_write(chip->regmap, reg, writeval); > + > + return regmap_read(chip->regmap, reg, readval); > +} > + > +/* Possible integration times for vshunt and vbus */ > +static IIO_CONST_ATTR_INT_TIME_AVAIL \ > + ("0.000140 0.000204 0.000332 0.000588 0.001100 0.002116 0.004156 0.008244"); > + > +static struct attribute *ina2xx_attributes[] = { > + &iio_const_attr_integration_time_available.dev_attr.attr, > + NULL, > +}; > + > +static const struct attribute_group ina2xx_attribute_group = { > + .attrs = ina2xx_attributes, > +}; > + > +static const struct iio_info ina2xx_info = { > + .debugfs_reg_access = &ina2xx_debug_reg, > + .read_raw = &ina2xx_read_raw, > + .write_raw = &ina2xx_write_raw, > + .attrs = &ina2xx_attribute_group, > + .driver_module = THIS_MODULE, > +}; > + > +/* Initialize the configuration and calibration registers. */ > +static int ina2xx_init(struct ina2xx_chip_info *chip, unsigned int config) > +{ > + u16 regval; > + int ret = regmap_write(chip->regmap, INA2XX_CONFIG, config); > + > + if (ret < 0) > + return ret; > + /* > + * Set current LSB to 1mA, shunt is in uOhms > + * (equation 13 in datasheet). We hardcode a Current_LSB > + * of 1.0 x10-6. The only remaining parameter is RShunt. > + * There is no need to expose the CALIBRATION register > + * to the user for now. > + */ > + regval = DIV_ROUND_CLOSEST(chip->config->calibration_factor, > + chip->rshunt); > + > + return regmap_write(chip->regmap, INA2XX_CALIBRATION, regval); > +} > + > +static int ina2xx_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + struct ina2xx_chip_info *chip; > + struct iio_dev *indio_dev; > + struct iio_buffer *buffer; > + int ret; > + unsigned int val; > + > + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); > + if (!indio_dev) > + return -ENOMEM; > + > + chip = iio_priv(indio_dev); > + > + chip->config = &ina2xx_config[id->driver_data]; > + > + if (of_property_read_u32(client->dev.of_node, > + "shunt-resistor", &val) < 0) { > + struct ina2xx_platform_data *pdata = > + dev_get_platdata(&client->dev); > + > + if (pdata) > + val = pdata->shunt_uohms; > + else > + val = INA2XX_RSHUNT_DEFAULT; > + } > + > + if (val <= 0 || val > chip->config->calibration_factor) > + return -ENODEV; > + > + chip->rshunt = val; > + > + ina2xx_regmap_config.max_register = chip->config->registers; > + > + mutex_init(&chip->state_lock); > + > + /* This is only used for device removal purposes. */ > + i2c_set_clientdata(client, indio_dev); > + > + indio_dev->name = id->name; > + indio_dev->channels = ina2xx_channels; > + indio_dev->num_channels = ARRAY_SIZE(ina2xx_channels); > + > + indio_dev->dev.parent = &client->dev; > + indio_dev->info = &ina2xx_info; > + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; > + > + chip->regmap = devm_regmap_init_i2c(client, &ina2xx_regmap_config); > + if (IS_ERR(chip->regmap)) { > + dev_err(&client->dev, "failed to allocate register map\n"); > + return PTR_ERR(chip->regmap); > + } > + > + /* Patch the current config register with default. */ > + val = chip->config->config_default; > + > + if (id->driver_data == ina226) { > + ina226_set_average(chip, INA226_DEFAULT_AVG, &val); > + ina226_set_int_time_vbus(chip, INA226_DEFAULT_IT, &val); > + ina226_set_int_time_vshunt(chip, INA226_DEFAULT_IT, &val); > + } > + > + ret = ina2xx_init(chip, val); > + if (ret < 0) { > + dev_err(&client->dev, "error configuring the device: %d\n", > + ret); > + return -ENODEV; > + } > + > + buffer = devm_iio_kfifo_allocate(&indio_dev->dev); > + if (!buffer) > + return -ENOMEM; > + > + indio_dev->setup_ops = &ina2xx_setup_ops; > + > + iio_device_attach_buffer(indio_dev, buffer); > + > + return iio_device_register(indio_dev); > +} > + > + > +static int ina2xx_remove(struct i2c_client *client) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(client); > + struct ina2xx_chip_info *chip = iio_priv(indio_dev); > + > + iio_device_unregister(indio_dev); > + > + /* Powerdown */ > + return regmap_update_bits(chip->regmap, INA2XX_CONFIG, > + INA2XX_MODE_MASK, 0); > +} > + > + > +static const struct i2c_device_id ina2xx_id[] = { > + {"ina219", ina219}, > + {"ina220", ina219}, > + {"ina226", ina226}, > + {"ina230", ina226}, > + {"ina231", ina226}, > + {} > +}; > + > +MODULE_DEVICE_TABLE(i2c, ina2xx_id); > + > +static struct i2c_driver ina2xx_driver = { > + .driver = { > +.name = KBUILD_MODNAME, > + }, > + .probe = ina2xx_probe, > + .remove = ina2xx_remove, > + .id_table = ina2xx_id, > +}; > + > +module_i2c_driver(ina2xx_driver); > + > +MODULE_AUTHOR("Marc Titinger "); > +MODULE_DESCRIPTION("Texas Instruments INA2XX ADC driver"); > +MODULE_LICENSE("GPL v2"); > -- Peter Meerwald-Stadler +43-664-218 (mobile) -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Re: [PATCH v4 1/4] iio: adc: add IMX7D ADC driver support
m_ioremap_resource(&pdev->dev, mem); > + if (IS_ERR(info->regs)) { > + ret = PTR_ERR(info->regs); > + dev_err(&pdev->dev, "Failed to remap adc memory, err = %d\n", > ret); > + return ret; > + } > + > + irq = platform_get_irq(pdev, 0); > + if (irq < 0) { > + dev_err(&pdev->dev, "No irq resource?\n"); > + return irq; > + } > + > + info->clk = devm_clk_get(&pdev->dev, "adc"); > + if (IS_ERR(info->clk)) { > + ret = PTR_ERR(info->clk); > + dev_err(&pdev->dev, "Failed getting clock, err = %d\n", ret); > + return ret; > + } > + > + info->vref = devm_regulator_get(&pdev->dev, "vref"); > + if (IS_ERR(info->vref)) { > + ret = PTR_ERR(info->vref); > + dev_err(&pdev->dev, "Failed getting reference voltage, err = > %d\n", ret); > + return ret; > + } > + > + ret = regulator_enable(info->vref); > + if (ret) { > + dev_err(&pdev->dev, "Can't enable adc reference top voltage, > err = %d\n", ret); > + return ret; > + } > + > + platform_set_drvdata(pdev, indio_dev); > + > + init_completion(&info->completion); > + > + indio_dev->name = dev_name(&pdev->dev); > + indio_dev->dev.parent = &pdev->dev; > + indio_dev->info = &imx7d_adc_iio_info; > + indio_dev->modes = INDIO_DIRECT_MODE; > + indio_dev->channels = imx7d_adc_iio_channels; > + indio_dev->num_channels = ARRAY_SIZE(imx7d_adc_iio_channels); > + > + ret = clk_prepare_enable(info->clk); > + if (ret) { > + dev_err(&pdev->dev, > + "Could not prepare or enable the clock.\n"); > + goto error_adc_clk_enable; > + } > + > + ret = devm_request_irq(info->dev, irq, > + imx7d_adc_isr, 0, > + dev_name(&pdev->dev), info); > + if (ret < 0) { > + dev_err(&pdev->dev, "Failed requesting irq, irq = %d\n", irq); > + goto error_iio_device_register; > + } > + > + imx7d_adc_feature_config(info); > + imx7d_adc_hw_init(info); > + > + ret = iio_device_register(indio_dev); > + if (ret) { > + dev_err(&pdev->dev, "Couldn't register the device.\n"); probably power down > + goto error_iio_device_register; > + } > + > + return 0; > + > +error_iio_device_register: > + clk_disable_unprepare(info->clk); > +error_adc_clk_enable: > + regulator_disable(info->vref); > + > + return ret; > +} > + > +static void imx7d_adc_power_down(struct imx7d_adc *info) > +{ > + u32 adc_cfg; > + > + adc_cfg = readl(info->regs + IMX7D_REG_ADC_ADC_CFG); > + adc_cfg |= IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN | > +IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN; > + adc_cfg &= ~IMX7D_REG_ADC_ADC_CFG_ADC_EN; > + writel(adc_cfg, info->regs + IMX7D_REG_ADC_ADC_CFG); > +} > + > +static int imx7d_adc_remove(struct platform_device *pdev) > +{ > + struct iio_dev *indio_dev = platform_get_drvdata(pdev); > + struct imx7d_adc *info = iio_priv(indio_dev); > + > + imx7d_adc_power_down(info); > + > + iio_device_unregister(indio_dev); > + clk_disable_unprepare(info->clk); > + regulator_disable(info->vref); > + > + return 0; > +} > + > +static int __maybe_unused imx7d_adc_suspend(struct device *dev) > +{ > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > + struct imx7d_adc *info = iio_priv(indio_dev); > + > + imx7d_adc_power_down(info); > + > + clk_disable_unprepare(info->clk); > + regulator_disable(info->vref); > + > + return 0; > +} > + > +static int __maybe_unused imx7d_adc_resume(struct device *dev) > +{ > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > + struct imx7d_adc *info = iio_priv(indio_dev); > + int ret; > + > + ret = regulator_enable(info->vref); > + if (ret) { > + dev_err(info->dev, > + "Can't enable adc reference top voltage, err = %d\n", > ret); > + return ret; > + } > + > + ret = clk_prepare_enable(info->clk); > + if (ret) { > + dev_err(info->dev, > + "Could not prepare or enable clock.\n"); > + regulator_disable(info->vref); > + return ret; > + } > + > + imx7d_adc_hw_init(info); > + > + return 0; > +} > + > +static SIMPLE_DEV_PM_OPS(imx7d_adc_pm_ops, imx7d_adc_suspend, > imx7d_adc_resume); > + > +static struct platform_driver imx7d_adc_driver = { > + .probe = imx7d_adc_probe, > + .remove = imx7d_adc_remove, > + .driver = { > + .name = "imx7d_adc", > + .of_match_table = imx7d_adc_match, > + .pm = &imx7d_adc_pm_ops, > + }, > +}; > + > +module_platform_driver(imx7d_adc_driver); > + > +MODULE_AUTHOR("Haibo Chen "); > +MODULE_DESCRIPTION("Freeacale IMX7D ADC driver"); > +MODULE_LICENSE("GPL v2"); > -- Peter Meerwald-Stadler +43-664-218 (mobile) -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
RE: [PATCH v4 1/4] iio: adc: add IMX7D ADC driver support
> > -Original Message- > > From: Peter Meerwald-Stadler [mailto:pme...@pmeerw.net] > > Sent: Tuesday, December 01, 2015 4:51 PM > > To: Chen Haibo-B51421 > > Cc: ji...@kernel.org; knaac...@gmx.de; l...@metafoo.de; > > shawn...@kernel.org; linux-...@vger.kernel.org; linux- > > ker...@vger.kernel.org > > Subject: Re: [PATCH v4 1/4] iio: adc: add IMX7D ADC driver support > > > > > > > Freescale i.MX7D soc contains a new ADC IP. This patch add this ADC > > > driver support, and the driver only support ADC software trigger. > > > > some extreme nitpicking below :) > > > > > Signed-off-by: Haibo Chen > > > --- > > > drivers/iio/adc/Kconfig | 9 + > > > drivers/iio/adc/Makefile| 1 + > > > drivers/iio/adc/imx7d_adc.c | 588 > > > > > > 3 files changed, 598 insertions(+) > > > create mode 100644 drivers/iio/adc/imx7d_adc.c > > > > > > diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index > > > 7868c74..3493a46 100644 > > > --- a/drivers/iio/adc/Kconfig > > > +++ b/drivers/iio/adc/Kconfig > > > @@ -194,6 +194,15 @@ config HI8435 > > > This driver can also be built as a module. If so, the module will > > be > > > called hi8435. > > > > > > +config IMX7D_ADC > > > + tristate "IMX7D ADC driver" > > > + depends on ARCH_MXC || COMPILE_TEST > > > + help > > > + Say yes here to build support for IMX7D ADC. > > > + > > > + This driver can also be built as a module. If so, the module will > > be > > > + called imx7d_adc. > > > + > > > config LP8788_ADC > > > tristate "LP8788 ADC driver" > > > depends on MFD_LP8788 > > > diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index > > > 99b37a9..282ffc01 100644 > > > --- a/drivers/iio/adc/Makefile > > > +++ b/drivers/iio/adc/Makefile > > > @@ -20,6 +20,7 @@ obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o > > > obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o > > > obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o > > > obj-$(CONFIG_HI8435) += hi8435.o > > > +obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o > > > obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o > > > obj-$(CONFIG_MAX1027) += max1027.o > > > obj-$(CONFIG_MAX1363) += max1363.o > > > diff --git a/drivers/iio/adc/imx7d_adc.c b/drivers/iio/adc/imx7d_adc.c > > > new file mode 100644 index 000..4780595 > > > --- /dev/null > > > +++ b/drivers/iio/adc/imx7d_adc.c > > > @@ -0,0 +1,588 @@ > > > +/* > > > + * Freescale i.MX7D ADC driver > > > + * > > > + * Copyright (C) 2015 Freescale Semiconductor, Inc. > > > + * > > > + * This program is free software; you can redistribute it and/or > > > +modify > > > + * it under the terms of the GNU General Public License as published > > > +by > > > + * the Free Software Foundation; either version 2 of the License, or > > > + * (at your option) any later version. > > > + */ > > > + > > > +#include > > > +#include > > > +#include > > > +#include > > > +#include > > > +#include > > > +#include > > > +#include > > > +#include > > > + > > > +#include > > > +#include > > > +#include > > > + > > > +/* ADC register */ > > > +#define IMX7D_REG_ADC_CH_A_CFG1 0x00 > > > +#define IMX7D_REG_ADC_CH_A_CFG2 0x10 > > > +#define IMX7D_REG_ADC_CH_B_CFG1 0x20 > > > +#define IMX7D_REG_ADC_CH_B_CFG2 0x30 > > > +#define IMX7D_REG_ADC_CH_C_CFG1 0x40 > > > +#define IMX7D_REG_ADC_CH_C_CFG2 0x50 > > > +#define IMX7D_REG_ADC_CH_D_CFG1 0x60 > > > +#define IMX7D_REG_ADC_CH_D_CFG2 0x70 > > > +#define IMX7D_REG_ADC_CH_SW_CFG 0x80 > > > +#define IMX7D_REG_ADC_TIMER_UNIT 0x90 > > > +#define IMX7D_REG_ADC_DMA_FIFO 0xa0 > > > +#define IMX7D_REG_ADC_FIFO_STATUS0xb0 > > > +#define IMX7D_REG_ADC_INT_SIG_EN 0xc0 > > > +#define IMX7D_REG_ADC_INT_EN 0xd0 > > > +#define IMX7D_REG_ADC_INT_STATUS 0xe0 > > > +#define IMX7D_REG_ADC_CHA_B_CNV_RSLT 0xf0 > &
Re: [PATCH 1/2] iio: ina2xx: add support for TI INA2xx Power Monitors
= iio_priv(indio_dev); > + > + if (chip->task) { > + kthread_stop(chip->task); > + chip->task = NULL; > + } > + return 0; > +} > + > +static const struct iio_buffer_setup_ops ina2xx_setup_ops = { > + .postenable = &ina2xx_buffer_enable, > + .postdisable = &ina2xx_buffer_disable, > +}; > + > +static int ina2xx_debug_reg(struct iio_dev *indio_dev, > + unsigned reg, unsigned writeval, unsigned *readval) > +{ > + struct ina2xx_chip_info *chip = iio_priv(indio_dev); > + > + if (!readval) > + return regmap_write(chip->regmap, reg, writeval); > + > + return regmap_read(chip->regmap, reg, readval); > +} > + > +/* frequencies matching the cummulated integration times for vshunt and vbus > */ cumulated > +static IIO_CONST_ATTR_INT_TIME_AVAIL("140 204 332 588 1100 2116 4156 8244"); > + > +static struct attribute *ina2xx_attributes[] = { > + &iio_const_attr_integration_time_available.dev_attr.attr, > + NULL, > +}; > + > +static const struct attribute_group ina2xx_attribute_group = { > + .attrs = ina2xx_attributes, > +}; > + > +static const struct iio_info ina2xx_info = { > + .debugfs_reg_access = &ina2xx_debug_reg, > + .read_raw = &ina2xx_read_raw, > + .write_raw = &ina2xx_write_raw, > + .attrs = &ina2xx_attribute_group, > + .driver_module = THIS_MODULE, > +}; > + > +/* Initialize the configuration and calibration registers. */ > +static int ina2xx_init(struct ina2xx_chip_info *chip, unsigned int config) > +{ > + u16 regval; > + int ret = regmap_write(chip->regmap, INA2XX_CONFIG, config); > + > + if (ret < 0) > + return ret; > + /* > + * Set current LSB to 1mA, shunt is in uOhms > + * (equation 13 in datasheet). We hardcode a Current_LSB > + * of 1.0 x10-6. The only remaining parameter is RShunt full stop > + * There is no need to expose the CALIBRATION register > + * to the user for now. > + */ > + regval = DIV_ROUND_CLOSEST(chip->config->calibration_factor, > + chip->rshunt); > + > + return regmap_write(chip->regmap, INA2XX_CALIBRATION, regval); > +} > + > +static int ina2xx_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + struct ina2xx_chip_info *chip; > + struct iio_dev *indio_dev; > + struct iio_buffer *buffer; > + int ret; > + unsigned int val; > + > + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); > + if (!indio_dev) > + return -ENOMEM; > + > + chip = iio_priv(indio_dev); > + > + chip->config = &ina2xx_config[id->driver_data]; > + > + if (of_property_read_u32(client->dev.of_node, > + "shunt-resistor", &val) < 0) { > + struct ina2xx_platform_data *pdata = > + dev_get_platdata(&client->dev); > + > + if (pdata) > + val = pdata->shunt_uohms; > + else > + val = INA2XX_RSHUNT_DEFAULT; > + } > + > + if (val <= 0 || val > chip->config->calibration_factor) > + return -ENODEV; > + > + chip->rshunt = val; > + > + ina2xx_regmap_config.max_register = chip->config->registers; > + > + mutex_init(&chip->state_lock); > + > + /* this is only used for device removal purposes */ > + i2c_set_clientdata(client, indio_dev); > + > + indio_dev->name = id->name; > + indio_dev->channels = ina2xx_channels; > + indio_dev->num_channels = ARRAY_SIZE(ina2xx_channels); > + > + indio_dev->dev.parent = &client->dev; > + indio_dev->info = &ina2xx_info; > + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; > + > + chip->regmap = devm_regmap_init_i2c(client, &ina2xx_regmap_config); > + if (IS_ERR(chip->regmap)) { > + dev_err(&client->dev, "failed to allocate register map\n"); > + return PTR_ERR(chip->regmap); > + } > + > + /* Patch the current config register with default. */ > + val = chip->config->config_default; > + > + if (id->driver_data == ina226) { > + ina226_set_average(chip, INA226_DEFAULT_AVG, &val); > + ina226_set_itb(chip, INA226_DEFAULT_IT, &val); > + ina226_set_its(chip, INA226_DEFAULT_IT, &val); > + } > + > + ret = ina2xx_init(chip, val); > + if (ret < 0) { > + dev_err(&client->dev, "error configuring the device: %d\n", > + ret); > + return -ENODEV; > + } > + > + buffer = devm_iio_kfifo_allocate(&indio_dev->dev); > + if (!buffer) > + return -ENOMEM; > + > + indio_dev->setup_ops = &ina2xx_setup_ops; > + > + iio_device_attach_buffer(indio_dev, buffer); > + > + return devm_iio_device_register(&client->dev, indio_dev); > +} > + > + > +static int ina2xx_remove(struct i2c_client *client) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(client); > + struct ina2xx_chip_info *chip = iio_priv(indio_dev); > + int ret; > + > + mutex_destroy(&chip->state_lock); needed? > + > + /* Powerdown */ > + ret = regmap_update_bits(chip->regmap, INA2XX_CONFIG, > + INA2XX_MODE_MASK, 0); > + > + iio_device_unregister(indio_dev); not needed since devm_iio_device_register() is used > + > + return ret; > +} > + > + > +static const struct i2c_device_id ina2xx_id[] = { > + {"ina219", ina219}, > + {"ina220", ina219}, > + {"ina226", ina226}, > + {"ina230", ina226}, > + {"ina231", ina226}, > + {} > +}; > + > +MODULE_DEVICE_TABLE(i2c, ina2xx_id); > + > +static struct i2c_driver ina2xx_driver = { > + .driver = { > +.name = KBUILD_MODNAME, > + }, > + .probe = ina2xx_probe, > + .remove = ina2xx_remove, > + .id_table = ina2xx_id, > +}; > + > +module_i2c_driver(ina2xx_driver); > + > +MODULE_AUTHOR("Marc Titinger "); > +MODULE_DESCRIPTION("Texas Instruments INA2XX ADC driver"); > +MODULE_LICENSE("GPL v2"); > -- Peter Meerwald-Stadler +43-664-218 (mobile) -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Re: [PATCH v2] iio: add driver for Microchip MCP413X/414X/415X/416X/423X/424X/425X/426X
{ .compatible = "microchip,mcp4232-104", > + .data = &mcp4131_cfg[MCP423x_104] }, > + { .compatible = "microchip,mcp4241-502", > + .data = &mcp4131_cfg[MCP424x_502] }, > + { .compatible = "microchip,mcp4241-103", > + .data = &mcp4131_cfg[MCP424x_103] }, > + { .compatible = "microchip,mcp4241-503", > + .data = &mcp4131_cfg[MCP424x_503] }, > + { .compatible = "microchip,mcp4241-104", > + .data = &mcp4131_cfg[MCP424x_104] }, > + { .compatible = "microchip,mcp4242-502", > + .data = &mcp4131_cfg[MCP424x_502] }, > + { .compatible = "microchip,mcp4242-103", > + .data = &mcp4131_cfg[MCP424x_103] }, > + { .compatible = "microchip,mcp4242-503", > + .data = &mcp4131_cfg[MCP424x_503] }, > + { .compatible = "microchip,mcp4242-104", > + .data = &mcp4131_cfg[MCP424x_104] }, > + { .compatible = "microchip,mcp4251-502", > + .data = &mcp4131_cfg[MCP425x_502] }, > + { .compatible = "microchip,mcp4251-103", > + .data = &mcp4131_cfg[MCP425x_103] }, > + { .compatible = "microchip,mcp4251-503", > + .data = &mcp4131_cfg[MCP425x_503] }, > + { .compatible = "microchip,mcp4251-104", > + .data = &mcp4131_cfg[MCP425x_104] }, > + { .compatible = "microchip,mcp4252-502", > + .data = &mcp4131_cfg[MCP425x_502] }, > + { .compatible = "microchip,mcp4252-103", > + .data = &mcp4131_cfg[MCP425x_103] }, > + { .compatible = "microchip,mcp4252-503", > + .data = &mcp4131_cfg[MCP425x_503] }, > + { .compatible = "microchip,mcp4252-104", > + .data = &mcp4131_cfg[MCP425x_104] }, > + { .compatible = "microchip,mcp4261-502", > + .data = &mcp4131_cfg[MCP426x_502] }, > + { .compatible = "microchip,mcp4261-103", > + .data = &mcp4131_cfg[MCP426x_103] }, > + { .compatible = "microchip,mcp4261-503", > + .data = &mcp4131_cfg[MCP426x_503] }, > + { .compatible = "microchip,mcp4261-104", > + .data = &mcp4131_cfg[MCP426x_104] }, > + { .compatible = "microchip,mcp4262-502", > + .data = &mcp4131_cfg[MCP426x_502] }, > + { .compatible = "microchip,mcp4262-103", > + .data = &mcp4131_cfg[MCP426x_103] }, > + { .compatible = "microchip,mcp4262-503", > + .data = &mcp4131_cfg[MCP426x_503] }, > + { .compatible = "microchip,mcp4262-104", > + .data = &mcp4131_cfg[MCP426x_104] }, > + {} > +}; > +MODULE_DEVICE_TABLE(of, mcp4131_dt_ids); > +#endif /* CONFIG_OF */ > + > +static const struct spi_device_id mcp4131_id[] = { > + { "mcp4131-502", MCP413x_502 }, > + { "mcp4131-103", MCP413x_103 }, > + { "mcp4131-503", MCP413x_503 }, > + { "mcp4131-104", MCP413x_104 }, > + { "mcp4132-502", MCP413x_502 }, > + { "mcp4132-103", MCP413x_103 }, > + { "mcp4132-503", MCP413x_503 }, > + { "mcp4132-104", MCP413x_104 }, > + { "mcp4141-502", MCP414x_502 }, > + { "mcp4141-103", MCP414x_103 }, > + { "mcp4141-503", MCP414x_503 }, > + { "mcp4141-104", MCP414x_104 }, > + { "mcp4142-502", MCP414x_502 }, > + { "mcp4142-103", MCP414x_103 }, > + { "mcp4142-503", MCP414x_503 }, > + { "mcp4142-104", MCP414x_104 }, > + { "mcp4151-502", MCP415x_502 }, > + { "mcp4151-103", MCP415x_103 }, > + { "mcp4151-503", MCP415x_503 }, > + { "mcp4151-104", MCP415x_104 }, > + { "mcp4152-502", MCP415x_502 }, > + { "mcp4152-103", MCP415x_103 }, > + { "mcp4152-503", MCP415x_503 }, > + { "mcp4152-104", MCP415x_104 }, > + { "mcp4161-502", MCP416x_502 }, > + { "mcp4161-103", MCP416x_103 }, > + { "mcp4161-503", MCP416x_503 }, > + { "mcp4161-104", MCP416x_104 }, > + { "mcp4162-502", MCP416x_502 }, > + { "mcp4162-103", MCP416x_103 }, > + { "mcp4162-503", MCP416x_503 }, > + { "mcp4162-104", MCP416x_104 }, > + { "mcp4231-502", MCP423x_502 }, > + { "mcp4231-103", MCP423x_103 }, > + { "mcp4231-503", MCP423x_503 }, > + { "mcp4231-104", MCP423x_104 }, > + { "mcp4232-502", MCP423x_502 }, > + { "mcp4232-103", MCP423x_103 }, > + { "mcp4232-503", MCP423x_503 }, > + { "mcp4232-104", MCP423x_104 }, > + { "mcp4241-502", MCP424x_502 }, > + { "mcp4241-103", MCP424x_103 }, > + { "mcp4241-503", MCP424x_503 }, > + { "mcp4241-104", MCP424x_104 }, > + { "mcp4242-502", MCP424x_502 }, > + { "mcp4242-103", MCP424x_103 }, > + { "mcp4242-503", MCP424x_503 }, > + { "mcp4242-104", MCP424x_104 }, > + { "mcp4251-502", MCP425x_502 }, > + { "mcp4251-103", MCP425x_103 }, > + { "mcp4251-503", MCP425x_503 }, > + { "mcp4251-104", MCP425x_104 }, > + { "mcp4252-502", MCP425x_502 }, > + { "mcp4252-103", MCP425x_103 }, > + { "mcp4252-503", MCP425x_503 }, > + { "mcp4252-104", MCP425x_104 }, > + { "mcp4261-502", MCP426x_502 }, > + { "mcp4261-103", MCP426x_103 }, > + { "mcp4261-503", MCP426x_503 }, > + { "mcp4261-104", MCP426x_104 }, > + { "mcp4262-502", MCP426x_502 }, > + { "mcp4262-103", MCP426x_103 }, > + { "mcp4262-503", MCP426x_503 }, > + { "mcp4262-104", MCP426x_104 }, > + {} > +}; > +MODULE_DEVICE_TABLE(spi, mcp4131_id); > + > +static struct spi_driver mcp4131_driver = { > + .driver = { > + .name = "mcp4131", > + .of_match_table = of_match_ptr(mcp4131_dt_ids), > + }, > + .probe = mcp4131_probe, > + .remove = mcp4131_remove, > + .id_table = mcp4131_id, > +}; > + > +module_spi_driver(mcp4131_driver); > + > +MODULE_AUTHOR("Slawomir Stepien "); > +MODULE_DESCRIPTION("MCP4131 digital potentiometer"); > +MODULE_LICENSE("GPL v2"); > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH] iio: potentiometer: add driver for Maxim Integrated DS1803
+ .indexed = 1, \ > + .output = 1,\ > + .channel = (ch),\ > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ > + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ > +} > + > +static const struct iio_chan_spec ds1803_channels[] = { > + DS1803_CHANNEL(0), > + DS1803_CHANNEL(1), > +}; > + > +static int ds1803_read_raw(struct iio_dev *indio_dev, > + struct iio_chan_spec const *chan, > + int *val, int *val2, long mask) > +{ > + struct ds1803_data *data = iio_priv(indio_dev); > + int pot = chan->channel; > + s32 ret; > + > + switch (mask) { > + case IIO_CHAN_INFO_RAW: > + ret = i2c_smbus_read_word_swapped(data->client, 0); maybe a #define to explain the name of register 0 > + if (ret < 0) > + return ret; > + > + /* Get bits for given pot */ > + *val = (pot == 0 ? ret >> 8 : ret & 255); often 0xff is used to mask a byte > + return IIO_VAL_INT; > + > + case IIO_CHAN_INFO_SCALE: > + *val = 1000 * data->cfg->kohms; > + *val2 = DS1803_MAX_POS; > + return IIO_VAL_FRACTIONAL; > + } > + > + return -EINVAL; > +} > + > +static int ds1803_write_raw(struct iio_dev *indio_dev, > + struct iio_chan_spec const *chan, > + int val, int val2, long mask) > +{ > + struct ds1803_data *data = iio_priv(indio_dev); > + int pot = chan->channel; > + > + switch (mask) { > + case IIO_CHAN_INFO_RAW: > + if (val > DS1803_MAX_POS || val < 0) check that val2 is 0 or use .write_raw_get_fmt > + return -EINVAL; > + break; > + default: > + return -EINVAL; > + } > + > + return i2c_smbus_write_byte_data(data->client, DS1803_WRITE(pot), val); > +} > + > +static const struct iio_info ds1803_info = { > + .read_raw = ds1803_read_raw, > + .write_raw = ds1803_write_raw, > + .driver_module = THIS_MODULE, > +}; > + > +static int ds1803_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + struct device *dev = &client->dev; > + struct ds1803_data *data; > + struct iio_dev *indio_dev; > + > + /* Value of two pots is read at the same time */ > + if (!i2c_check_functionality(client->adapter, > + I2C_FUNC_SMBUS_WORD_DATA)) { > + dev_err(dev, "SMBUS Word Data not supported\n"); > + return -EOPNOTSUPP; > + } > + > + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); > + if (!indio_dev) > + return -ENOMEM; > + > + i2c_set_clientdata(client, indio_dev); > + > + data = iio_priv(indio_dev); > + data->client = client; > + data->cfg = &ds1803_cfg[id->driver_data]; > + > + indio_dev->dev.parent = dev; > + indio_dev->info = &ds1803_info; > + indio_dev->channels = ds1803_channels; > + indio_dev->num_channels = DS1803_NUM_WIPERS; ARRAY_SIZE(ds1803_channels) > + indio_dev->name = client->name; > + > + return devm_iio_device_register(dev, indio_dev); > +} > + > +#if defined(CONFIG_OF) > +static const struct of_device_id ds1803_dt_ids[] = { > + { .compatible = "maxim,ds1803-010", .data = &ds1803_cfg[DS1803_010] }, > + { .compatible = "maxim,ds1803-050", .data = &ds1803_cfg[DS1803_050] }, > + { .compatible = "maxim,ds1803-100", .data = &ds1803_cfg[DS1803_100] }, > + {} > +}; > +MODULE_DEVICE_TABLE(of, ds1803_dt_ids); > +#endif /* CONFIG_OF */ > + > +static const struct i2c_device_id ds1803_id[] = { > + { "ds1803-010", DS1803_010 }, > + { "ds1803-050", DS1803_050 }, > + { "ds1803-100", DS1803_100 }, > + {} > +}; > +MODULE_DEVICE_TABLE(i2c, ds1803_id); > + > +static struct i2c_driver ds1803_driver = { > + .driver = { > + .name = "ds1803", > + .of_match_table = of_match_ptr(ds1803_dt_ids), > + }, > + .probe = ds1803_probe, > + .id_table = ds1803_id, > +}; > + > +module_i2c_driver(ds1803_driver); > + > +MODULE_AUTHOR("Slawomir Stepien "); > +MODULE_DESCRIPTION("DS1803 digital potentiometer"); > +MODULE_LICENSE("GPL v2"); > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH 1/5] max44000: Initial commit
+ data = iio_priv(indio_dev); > + data->regmap = devm_regmap_init_i2c(client, &max44000_regmap_config); > + if (IS_ERR(data->regmap)) { > + dev_err(&client->dev, "regmap_init failed!\n"); > + return PTR_ERR(data->regmap); > + } > + > + i2c_set_clientdata(client, indio_dev); > + data->client = client; > + mutex_init(&data->lock); > + indio_dev->dev.parent = &client->dev; > + indio_dev->info = &max44000_info; > + indio_dev->name = MAX44000_DRV_NAME; > + indio_dev->channels = max44000_channels; > + indio_dev->num_channels = ARRAY_SIZE(max44000_channels); > + > + /* Read status at least once to clear the power-on-reset bit. */ > + ret = regmap_read(data->regmap, MAX44000_REG_STATUS, ®); > + if (ret < 0) { > + dev_err(&data->client->dev, "failed to read init status: %d\n", > ret); > + return ret; > + } > + > + /* The device has no reset command, write defaults explicitly. */ > + ret = max44000_force_write_defaults(data); > + if (ret < 0) { > + dev_err(&data->client->dev, "failed to write defaults: %d\n", > ret); > + return ret; > + } > + > + return iio_device_register(indio_dev); devm_ would work here to make _remove obsolete > +} > + > +static int max44000_remove(struct i2c_client *client) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(client); > + > + iio_device_unregister(indio_dev); > + return 0; > +} > + > +static const struct i2c_device_id max44000_id[] = { > + {"max44000", 0}, > + { } > +}; > +MODULE_DEVICE_TABLE(i2c, max44000_id); > + > +#ifdef CONFIG_OF > +static const struct of_device_id max44000_of_match[] = { > + { .compatible = "maxim,max44000" }, > + { } > +}; > +MODULE_DEVICE_TABLE(of, max44000_of_match); > +#endif > + > +#ifdef CONFIG_ACPI > +static const struct acpi_device_id max44000_acpi_match[] = { > + {"MAX44000", 0}, > + { } > +}; > +MODULE_DEVICE_TABLE(acpi, max44000_acpi_match); > +#endif > + > +static struct i2c_driver max44000_driver = { > + .driver = { > + .name = MAX44000_DRV_NAME, > + .of_match_table = of_match_ptr(max44000_of_match), > + .acpi_match_table = ACPI_PTR(max44000_acpi_match), > + }, > + .probe = max44000_probe, > + .remove = max44000_remove, > + .id_table = max44000_id, > +}; > + > +module_i2c_driver(max44000_driver); > + > +MODULE_AUTHOR("Crestez Dan Leonard "); > +MODULE_DESCRIPTION("MAX44000 Ambient and Infrared Proximity Sensor"); > +MODULE_LICENSE("GPL v2"); > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH 5/5] max44000: Initial triggered buffer support
comments below > Signed-off-by: Crestez Dan Leonard > --- > drivers/iio/light/max44000.c | 63 > > 1 file changed, 63 insertions(+) > > diff --git a/drivers/iio/light/max44000.c b/drivers/iio/light/max44000.c > index e479c53..7b1f8bc 100644 > --- a/drivers/iio/light/max44000.c > +++ b/drivers/iio/light/max44000.c > @@ -19,6 +19,9 @@ > #include > #include > #include > +#include > +#include > +#include > #include > > #define MAX44000_DRV_NAME"max44000" > @@ -123,24 +126,41 @@ static const char max44000_scale_avail_str[] = > "0.5 " >"4"; > > +#define MAX44000_SCAN_INDEX_ALS 0 > +#define MAX44000_SCAN_INDEX_PRX 1 > + > static const struct iio_chan_spec max44000_channels[] = { > { > .type = IIO_LIGHT, > .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), > .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | > BIT(IIO_CHAN_INFO_INT_TIME), > + .scan_index = MAX44000_SCAN_INDEX_ALS, > + .scan_type = { > + .sign = 'u', > + .realbits = 14, > + .storagebits= 16, > + } > }, > { > .type = IIO_PROXIMITY, > .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), > .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), > + .scan_index = MAX44000_SCAN_INDEX_PRX, > + .scan_type = { > + .sign = 'u', > + .realbits = 8, > + .storagebits= 8, code would get simpler if storagebits = 16 > + } > }, > + IIO_CHAN_SOFT_TIMESTAMP(2), > { > .type = IIO_CURRENT, > .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | > BIT(IIO_CHAN_INFO_SCALE), > .extend_name = "led", > .output = 1, > + .scan_index = -1, > }, > }; > > @@ -456,6 +476,42 @@ static int max44000_force_write_defaults(struct > max44000_data *data) > return 0; > } > > +static irqreturn_t max44000_trigger_handler(int irq, void *p) > +{ > + struct iio_poll_func *pf = p; > + struct iio_dev *indio_dev = pf->indio_dev; > + struct max44000_data *data = iio_priv(indio_dev); > + u16 buf[indio_dev->scan_bytes / 2]; > + u8 *pos = (u8 *)buf; int i = 0; > + unsigned int regval; > + int ret; > + > + mutex_lock(&data->lock); > + if (*indio_dev->active_scan_mask & (1 << MAX44000_SCAN_INDEX_ALS)) { BIT(MAX44000_SCAN_INDEX_ALS) > + ret = max44000_read_alsval(data); > + if (ret < 0) > + goto out_unlock; > + *((u16 *)pos) = ret; buf[i++] = ret; > + pos += 2; > + } > + if (*indio_dev->active_scan_mask & (1 << MAX44000_SCAN_INDEX_PRX)) { > + ret = regmap_read(data->regmap, MAX44000_REG_PRX_DATA, ®val); > + if (ret < 0) > + goto out_unlock; buf[i++] = retval; > + *pos = regval; > + } > + mutex_unlock(&data->lock); > + > + iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns()); moving iio_push_to_buffers_with_timestamp() before mutex_unlock() would allow simpler code (not sure if it's worth) > + iio_trigger_notify_done(indio_dev->trig); > + return IRQ_HANDLED; > + > +out_unlock: > + mutex_unlock(&data->lock); > + iio_trigger_notify_done(indio_dev->trig); > + return IRQ_HANDLED; > +} > + > static int max44000_probe(struct i2c_client *client, > const struct i2c_device_id *id) > { > @@ -513,6 +569,12 @@ static int max44000_probe(struct i2c_client *client, > return ret; > } > > + ret = iio_triggered_buffer_setup(indio_dev, NULL, > max44000_trigger_handler, NULL); > + if (ret < 0) { > + dev_err(&client->dev, "iio triggered buffer setup failed\n"); > + return ret; > + } > + > return iio_device_register(indio_dev); no devm_ possible anymore :-) > } > > @@ -521,6 +583,7 @@ static int max44000_remove(struct i2c_client *client) > struct iio_dev *indio_dev = i2c_get_clientdata(client); > > iio_device_unregister(indio_dev); > + iio_triggered_buffer_cleanup(indio_dev); > return 0; > } > > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH] iio: potentiometer: add driver for Maxim Integrated DS1803
> > maybe a #define to explain the name of register 0 > > Well this zero is not register nor any kind of command that DS1803 needs to > send > back pots values. > DS1803 only requires the standard R/W bit to be set as read. > I used _word_ function series to get back a word (16 bits) as this poti > returns > 16 bits. > > How should I named it? > > #define NON_COMMAND 0 > ? maybe i2c_transfer() is more appropriate then u8 databuf[2]; struct i2c_msg msgs[2] = { { .addr = client->addr, .len = sizeof(databuf), .buf = databuf, .flags = I2C_M_RD } }; ret = i2c_transfer(client->adapter, msgs, 2); this does not send any data to the chip but only reads two bytes > Or should I use different function? (2x i2c_smbus_read_byte?) > > The i2c_smbus_read_byte() function also used 0 as command > for its transfers... > > > +static int ds1803_write_raw(struct iio_dev *indio_dev, > > > + struct iio_chan_spec const *chan, > > > + int val, int val2, long mask) > > > +{ > > > + struct ds1803_data *data = iio_priv(indio_dev); > > > + int pot = chan->channel; > > > + > > > + switch (mask) { > > > + case IIO_CHAN_INFO_RAW: > > > + if (val > DS1803_MAX_POS || val < 0) > > > > check that val2 is 0 or use .write_raw_get_fmt > > At this point I do not know why should I do it, but I will look into that. write_raw expects a VAL_INT_PLUS_MICROS per default, passed in val and val2 you can change that with .write_raw_get_fmt (e.g. to expect VAL_INT), or just make sure that the micros are 0 -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH 1/2] ti-adc081c: Add support for adc101c* and adc121c*
> These chips has an almost identical interface but a deal with a > different number of value bits. Datasheet links for comparison: comments below > * http://www.ti.com/lit/ds/symlink/adc081c021.pdf > * http://www.ti.com/lit/ds/symlink/adc101c021.pdf > * http://www.ti.com/lit/ds/symlink/adc121c021.pdf > > Signed-off-by: Crestez Dan Leonard > --- > drivers/iio/adc/Kconfig | 6 +++--- > drivers/iio/adc/ti-adc081c.c | 31 +++ > 2 files changed, 30 insertions(+), 7 deletions(-) > > diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig > index 9ddcd5d..a2d0db5 100644 > --- a/drivers/iio/adc/Kconfig > +++ b/drivers/iio/adc/Kconfig > @@ -378,11 +378,11 @@ config ROCKCHIP_SARADC > module will be called rockchip_saradc. > > config TI_ADC081C > - tristate "Texas Instruments ADC081C021/027" > + tristate "Texas Instruments ADC081C/ADC101C/ADC121C family" > depends on I2C > help > - If you say yes here you get support for Texas Instruments ADC081C021 > - and ADC081C027 ADC chips. > + If you say yes here you get support for Texas Instruments ADC081C, > + ADC101C and ADC121C ADC chips. > > This driver can also be built as a module. If so, the module will be > called ti-adc081c. > diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c > index ecbc121..9b2f26f 100644 > --- a/drivers/iio/adc/ti-adc081c.c > +++ b/drivers/iio/adc/ti-adc081c.c > @@ -1,9 +1,21 @@ > /* > + * TI ADC081C/ADC101C/ADC121C 8/10/12-bit ADC driver > + * > * Copyright (C) 2012 Avionic Design GmbH > + * Copyright (C) 2016 Intel > * > * This program is free software; you can redistribute it and/or modify > * it under the terms of the GNU General Public License version 2 as > * published by the Free Software Foundation. > + * > + * Datasheets: > + * http://www.ti.com/lit/ds/symlink/adc081c021.pdf > + * http://www.ti.com/lit/ds/symlink/adc101c021.pdf > + * http://www.ti.com/lit/ds/symlink/adc121c021.pdf > + * > + * The devices have a very similar interface and differ mostly in the number > of > + * bits handled. For the 8-bit and 10-bit models the least-significant 4 or 2 > + * bits of value registers are reserved. > */ > > #include > @@ -17,6 +29,9 @@ > struct adc081c { > struct i2c_client *i2c; > struct regulator *ref; > + > + /* 8, 10 or 12 */ > + int bits; > }; > > #define REG_CONV_RES 0x00 > @@ -34,7 +49,7 @@ static int adc081c_read_raw(struct iio_dev *iio, > if (err < 0) > return err; > > - *value = (err >> 4) & 0xff; > + *value = (err & 0xFFF) >> (12 - adc->bits); > return IIO_VAL_INT; > > case IIO_CHAN_INFO_SCALE: > @@ -43,7 +58,7 @@ static int adc081c_read_raw(struct iio_dev *iio, > return err; > > *value = err / 1000; > - *shift = 8; > + *shift = adc->bits; > > return IIO_VAL_FRACTIONAL_LOG2; > > @@ -72,6 +87,9 @@ static int adc081c_probe(struct i2c_client *client, > struct adc081c *adc; > int err; > > + if (id->driver_data != 8 && id->driver_data != 10 && id->driver_data != > 12) often the id/driverdata is used to point to a table of chip-specific configuration data rather than using driver_data to store the data itself the second approach does not scale when more configuration data needs to be passed (just an observation) > + return -EINVAL; > + > if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) > return -EOPNOTSUPP; > > @@ -81,6 +99,7 @@ static int adc081c_probe(struct i2c_client *client, > > adc = iio_priv(iio); > adc->i2c = client; > + adc->bits = id->driver_data; > > adc->ref = devm_regulator_get(&client->dev, "vref"); > if (IS_ERR(adc->ref)) > @@ -124,7 +143,9 @@ static int adc081c_remove(struct i2c_client *client) > } > > static const struct i2c_device_id adc081c_id[] = { > - { "adc081c", 0 }, > + { "adc081c", 8 }, > + { "adc101c", 10 }, > + { "adc121c", 12 }, > { } > }; > MODULE_DEVICE_TABLE(i2c, adc081c_id); > @@ -132,6 +153,8 @@ MODULE_DEVICE_TABLE(i2c, adc081c_id); > #ifdef CONFIG_OF > static const struct of_device_id adc081c_of_match[] = { > { .compatible = "ti,adc081c" }, > + { .compatible = "ti,adc101c" }, > + { .compatible = "ti,adc121c" }, > { } > }; > MODULE_DEVICE_TABLE(of, adc081c_of_match); > @@ -149,5 +172,5 @@ static struct i2c_driver adc081c_driver = { > module_i2c_driver(adc081c_driver); > > MODULE_AUTHOR("Thierry Reding "); > -MODULE_DESCRIPTION("Texas Instruments ADC081C021/027 driver"); > +MODULE_DESCRIPTION("Texas Instruments ADC081C/ADC101C/ADC121C driver"); > MODULE_LICENSE("GPL v2"); > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH 2/2] ti-adc081c: Initial triggered buffer support
a != 10 && id->driver_data != > 12) > - return -EINVAL; > - > if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) > return -EOPNOTSUPP; > > @@ -99,7 +158,7 @@ static int adc081c_probe(struct i2c_client *client, > > adc = iio_priv(iio); > adc->i2c = client; > - adc->bits = id->driver_data; > + adc->bits = model->bits; > > adc->ref = devm_regulator_get(&client->dev, "vref"); > if (IS_ERR(adc->ref)) > @@ -114,18 +173,26 @@ static int adc081c_probe(struct i2c_client *client, > iio->modes = INDIO_DIRECT_MODE; > iio->info = &adc081c_info; > > - iio->channels = &adc081c_channel; > - iio->num_channels = 1; > + iio->channels = model->channels; > + iio->num_channels = 2; the number of channels could go into the adcxx1c_info struct > + > + err = iio_triggered_buffer_setup(iio, NULL, adc081c_trigger_handler, > NULL); > + if (err < 0) { > + dev_err(&client->dev, "iio triggered buffer setup failed\n"); > + goto err_regulator_disable; > + } > > err = iio_device_register(iio); > if (err < 0) > - goto regulator_disable; > + goto err_buffer_cleanup; > > i2c_set_clientdata(client, iio); > > return 0; > > -regulator_disable: > +err_buffer_cleanup: > + iio_triggered_buffer_cleanup(iio); > +err_regulator_disable: > regulator_disable(adc->ref); > > return err; > @@ -143,9 +210,9 @@ static int adc081c_remove(struct i2c_client *client) > } iio_triggered_buffer_cleanup() in _remove()? > static const struct i2c_device_id adc081c_id[] = { > - { "adc081c", 8 }, > - { "adc101c", 10 }, > - { "adc121c", 12 }, > + { "adc081c", (long)&adc081c_model }, often an enum is used instead of a pointer > + { "adc101c", (long)&adc101c_model }, > + { "adc121c", (long)&adc121c_model }, > { } > }; > MODULE_DEVICE_TABLE(i2c, adc081c_id); > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH] iio: imu: Add initial support for Bosch BMI160
uninit; > + > + ret = iio_device_register(indio_dev); > + if (ret < 0) > + goto buffer_cleanup; > + > + return 0; > +uninit: > + bmi160_chip_uninit(data); > +buffer_cleanup: > + iio_triggered_buffer_cleanup(indio_dev); > + return ret; > +} > +EXPORT_SYMBOL_GPL(bmi160_core_probe); > + > +void bmi160_core_remove(struct device *dev) > +{ > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > + struct bmi160_data *data = iio_priv(indio_dev); > + > + iio_device_unregister(indio_dev); > + iio_triggered_buffer_cleanup(indio_dev); > + bmi160_chip_uninit(data); > +} > +EXPORT_SYMBOL_GPL(bmi160_core_remove); > + > +MODULE_AUTHOR("Daniel Baluta +MODULE_DESCRIPTION("Bosch BMI160 driver"); > +MODULE_LICENSE("GPL v2"); > diff --git a/drivers/iio/imu/bmi160/bmi160_i2c.c > b/drivers/iio/imu/bmi160/bmi160_i2c.c > new file mode 100644 > index 000..af1feac > --- /dev/null > +++ b/drivers/iio/imu/bmi160/bmi160_i2c.c > @@ -0,0 +1,79 @@ > +/* > + * BMI160 - Bosch IMU, I2C bits > + * > + * Copyright (c) 2016, Intel Corporation. > + * > + * This file is subject to the terms and conditions of version 2 of > + * the GNU General Public License. See the file COPYING in the main > + * directory of this archive for more details. > + * > + * 7-bit I2C slave address is: > + * - 0x68 if SDO is pulled to GND > + * - 0x69 if SDO is pulled to VDDIO > + */ > +#include > +#include > +#include > + > +#include "bmi160.h" > + > +#define BMI160_I2C_DRV_NAME "bmi160_i2c" > + > +static const struct regmap_config bmi160_regmap_config = { could this be shared between i2c and spi? > + .reg_bits = 8, > + .val_bits = 8, > +}; > + > +static int bmi160_i2c_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + struct regmap *regmap; > + const char *name = NULL; > + > + regmap = devm_regmap_init_i2c(client, &bmi160_regmap_config); > + if (IS_ERR(regmap)) { > + dev_err(&client->dev, "Failed to register i2c regmap %d\n", > + (int)PTR_ERR(regmap)); > + return PTR_ERR(regmap); > + } > + > + if (id) > + name = id->name; > + > + return bmi160_core_probe(&client->dev, regmap, name, false); > +} > + > +static int bmi160_i2c_remove(struct i2c_client *client) > +{ > + bmi160_core_remove(&client->dev); > + > + return 0; > +} > + > +static const struct i2c_device_id bmi160_i2c_id[] = { > + {"bmi160", 0}, > + {} > +}; > +MODULE_DEVICE_TABLE(i2c, bmi160_i2c_id); > + > +static const struct acpi_device_id bmi160_acpi_match[] = { > + {"BMI0160", 0}, > + { }, > +}; > +MODULE_DEVICE_TABLE(acpi, bmi160_acpi_match); > + > +static struct i2c_driver bmi160_i2c_driver = { > + .driver = { > + .name = BMI160_I2C_DRV_NAME, > + .acpi_match_table = ACPI_PTR(bmi160_acpi_match), > + }, > + .probe = bmi160_i2c_probe, > + .remove = bmi160_i2c_remove, > + .id_table = bmi160_i2c_id, > +}; > + > +module_i2c_driver(bmi160_i2c_driver); > + > +MODULE_AUTHOR("Daniel Baluta "); > +MODULE_DESCRIPTION("BMI160 I2C driver"); > +MODULE_LICENSE("GPL v2"); > diff --git a/drivers/iio/imu/bmi160/bmi160_spi.c > b/drivers/iio/imu/bmi160/bmi160_spi.c > new file mode 100644 > index 000..444b723 > --- /dev/null > +++ b/drivers/iio/imu/bmi160/bmi160_spi.c > @@ -0,0 +1,71 @@ > +/* > + * BMI160 - Bosch IMU, SPI bits > + * > + * Copyright (c) 2016, Intel Corporation. > + * > + * This file is subject to the terms and conditions of version 2 of > + * the GNU General Public License. See the file COPYING in the main > + * directory of this archive for more details. > + * > + */ > +#include > +#include > +#include > + > +#include "bmi160.h" > + > +#define BMI160_SPI_DRV_NAME "bmi160_spi" > + > +static const struct regmap_config bmi160_regmap_config = { > + .reg_bits = 8, > + .val_bits = 8, > +}; > + > +static int bmi160_spi_probe(struct spi_device *spi) > +{ > + struct regmap *regmap; > + const struct spi_device_id *id = spi_get_device_id(spi); > + > + regmap = devm_regmap_init_spi(spi, &bmi160_regmap_config); > + if (IS_ERR(regmap)) { > + dev_err(&spi->dev, "Failed to register spi regmap %d\n", > + (int)PTR_ERR(regmap)); > + return PTR_ERR(regmap); > + } > + return bmi160_core_probe(&spi->dev, regmap, id->name, true); > +} > + > +static int bmi160_spi_remove(struct spi_device *spi) > +{ > + bmi160_core_remove(&spi->dev); > + > + return 0; > +} > + > +static const struct spi_device_id bmi160_spi_id[] = { > + {"bmi160", 0}, > + {} > +}; > + > +MODULE_DEVICE_TABLE(spi, bmi160_spi_id); > + > +static const struct acpi_device_id bmi160_acpi_match[] = { > + {"BMI0160", 0}, > + { }, > +}; > +MODULE_DEVICE_TABLE(acpi, bmi160_acpi_match); > + > +static struct spi_driver bmi160_spi_driver = { > + .probe = bmi160_spi_probe, > + .remove = bmi160_spi_remove, > + .id_table = bmi160_spi_id, > + .driver = { > + .acpi_match_table = ACPI_PTR(bmi160_acpi_match), > + .name = BMI160_SPI_DRV_NAME, > + }, > +}; > +module_spi_driver(bmi160_spi_driver); > + > +MODULE_AUTHOR("Daniel Baluta +MODULE_DESCRIPTION("Bosch BMI160 SPI driver"); > +MODULE_LICENSE("GPL v2"); > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH v3 1/4] iio: adc: add IMX7D ADC driver support
irq = platform_get_irq(pdev, 0); > + if (irq < 0) { > + dev_err(&pdev->dev, "no irq resource?\n"); > + return irq; > + } > + > + info->clk = devm_clk_get(&pdev->dev, "adc"); > + if (IS_ERR(info->clk)) { > + ret = PTR_ERR(info->clk); > + dev_err(&pdev->dev, "failed getting clock, err = %d\n", ret); > + return ret; > + } > + > + info->vref = devm_regulator_get(&pdev->dev, "vref"); > + if (IS_ERR(info->vref)) { > + ret = PTR_ERR(info->vref); > + dev_err(&pdev->dev, "failed getting reference voltage: %d\n", > ret); > + return ret; > + } > + > + ret = regulator_enable(info->vref); > + if (ret) > + return ret; > + > + platform_set_drvdata(pdev, indio_dev); > + > + init_completion(&info->completion); > + > + indio_dev->name = dev_name(&pdev->dev); > + indio_dev->dev.parent = &pdev->dev; > + indio_dev->dev.of_node = pdev->dev.of_node; > + indio_dev->info = &imx7d_adc_iio_info; > + indio_dev->modes = INDIO_DIRECT_MODE; > + indio_dev->channels = imx7d_adc_iio_channels; > + indio_dev->num_channels = ARRAY_SIZE(imx7d_adc_iio_channels); > + > + ret = clk_prepare_enable(info->clk); > + if (ret) { > + dev_err(&pdev->dev, > + "Could not prepare or enable the clock.\n"); > + goto error_adc_clk_enable; > + } > + > + ret = devm_request_irq(info->dev, irq, > + imx7d_adc_isr, 0, > + dev_name(&pdev->dev), info); > + if (ret < 0) { > + dev_err(&pdev->dev, "failed requesting irq, irq = %d\n", irq); > + goto error_iio_device_register; > + } > + > + ret = iio_device_register(indio_dev); > + if (ret) { > + dev_err(&pdev->dev, "Couldn't register the device.\n"); > + goto error_iio_device_register; > + } > + > + imx7d_adc_feature_config(info); > + imx7d_adc_hw_init(info); > + > + return 0; > + > +error_iio_device_register: > + clk_disable_unprepare(info->clk); > +error_adc_clk_enable: > + regulator_disable(info->vref); > + > + return ret; > +} > + > +static int imx7d_adc_remove(struct platform_device *pdev) > +{ > + struct iio_dev *indio_dev = platform_get_drvdata(pdev); > + struct imx7d_adc *info = iio_priv(indio_dev); > + > + iio_device_unregister(indio_dev); > + regulator_disable(info->vref); > + clk_disable_unprepare(info->clk); should be in the same order as in _probe(), so iio_device_unregister() clk_disable_unprepare() regulator_disable() > + > + return 0; > +} > + > +static int __maybe_unused imx7d_adc_suspend(struct device *dev) > +{ > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > + struct imx7d_adc *info = iio_priv(indio_dev); > + u32 adc_cfg; > + > + adc_cfg = readl(info->regs + IMX7D_REG_ADC_ADC_CFG); > + adc_cfg |= IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN | > +IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN; > + adc_cfg &= ~IMX7D_REG_ADC_ADC_CFG_ADC_EN; > + writel(adc_cfg, info->regs + IMX7D_REG_ADC_ADC_CFG); > + > + clk_disable_unprepare(info->clk); > + regulator_disable(info->vref); > + > + return 0; > +} > + > +static int __maybe_unused imx7d_adc_resume(struct device *dev) > +{ > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > + struct imx7d_adc *info = iio_priv(indio_dev); > + int ret; > + > + ret = regulator_enable(info->vref); > + if (ret) > + return ret; > + > + ret = clk_prepare_enable(info->clk); > + if (ret) { > + dev_err(info->dev, > + "Could not prepare or enable clock.\n"); > + regulator_disable(info->vref); > + return ret; > + } > + > + imx7d_adc_hw_init(info); > + > + return 0; > +} > + > +static SIMPLE_DEV_PM_OPS(imx7d_adc_pm_ops, imx7d_adc_suspend, > imx7d_adc_resume); > + > +static struct platform_driver imx7d_adc_driver = { > + .probe = imx7d_adc_probe, > + .remove = imx7d_adc_remove, > + .driver = { > + .name = "imx7d_adc", > + .of_match_table = imx7d_adc_match, > + .pm = &imx7d_adc_pm_ops, > + }, > +}; > + > +module_platform_driver(imx7d_adc_driver); > + > +MODULE_AUTHOR("Haibo Chen "); > +MODULE_DESCRIPTION("Freeacale IMX7D ADC driver"); > +MODULE_LICENSE("GPL v2"); > -- Peter Meerwald-Stadler +43-664-218 (mobile) -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Re: [PATCH 1/3] iio: light: Add driver for ap3216c
IIO_EV_TYPE_THRESH, > + IIO_EV_DIR_EITHER), > +timestamp); > + > + ret = ap3216c_clear_int(data); > + if (ret < 0) > + dev_err(&data->client->dev, "error clearing IRQ\n"); > + > + return IRQ_HANDLED; > +} > + > +static int ap3216c_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + struct ap3216c_data *data; > + struct iio_dev *indio_dev; > + int ret; > + > + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); > + if (!indio_dev) > + return -ENOMEM; > + > + data = iio_priv(indio_dev); > + data->client = client; > + indio_dev->dev.parent = &client->dev; > + indio_dev->info = &ap3216c_info; > + indio_dev->name = AP3216C_DRV_NAME; > + indio_dev->channels = ap3216c_channels; > + indio_dev->num_channels = ARRAY_SIZE(ap3216c_channels); > + > + data->regmap = devm_regmap_init_i2c(client, &ap3216c_regmap_config); > + if (IS_ERR(data->regmap)) { > + dev_err(&client->dev, "Failed to allocate register map\n"); > + return PTR_ERR(data->regmap); > + } > + > + /* Default to thresh events disabled */ > + data->als_thresh_en = false; > + data->prox_thresh_en = false; > + > + /* > + * Require that that the interrupt is cleared only when the INT that that > + * register is written to, instead of when data is read. This > + * prevents the interrupt from falsely reporting IRQ_NONE. > + */ > + ret = regmap_write(data->regmap, > +AP3216C_INT_CLR, AP3216C_INT_CLR_MANUAL); > + if (ret < 0) > + return ret; > + > + /* Before setting up IRQ, clear any stale interrupt */ > + ret = ap3216c_clear_int(data); > + if (ret < 0) > + return ret; > + > + if (client->irq) { > + ret = devm_request_threaded_irq(&client->dev, client->irq, > + NULL, ap3216c_event_handler, > + IRQF_TRIGGER_FALLING | > + IRQF_SHARED | IRQF_ONESHOT, > + client->name, indio_dev); > + if (ret) > + return ret; > + } > + > + /* Enable ALS and PS+IR */ > + ret = regmap_write(data->regmap, AP3216C_SYS, AP3216C_SYS_MODE_ALS_PS); > + if (ret < 0) > + return ret; > + > + return devm_iio_device_register(&client->dev, indio_dev); > +} > + > +static const struct of_device_id ap3216c_of_match[] = { > + { .compatible = "liteon,ap3216c", }, > + { }, > +}; > +MODULE_DEVICE_TABLE(of, ap3216c_of_match); > + > +static const struct i2c_device_id ap3216c_id[] = { > + {"ap3216c", 0}, > + { } > +}; > +MODULE_DEVICE_TABLE(i2c, ap3216c_id); > + > +static struct i2c_driver ap3216c_driver = { > + .driver = { > + .name = AP3216C_DRV_NAME, > + }, > + .probe = ap3216c_probe, > + .id_table = ap3216c_id, > +}; > +module_i2c_driver(ap3216c_driver); > + > +MODULE_AUTHOR("Robert Eshleman "); > +MODULE_DESCRIPTION("APC3216C Ambient Light and Proximity Sensor"); > +MODULE_LICENSE("GPL v2"); > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH 1/1] iio: (bma400) add driver for the BMA400
ttr, > > + &iio_dev_attr_in_accel_scale_available.dev_attr.attr, > > + NULL, > > +}; > > + > > +static const struct attribute_group bma400_attrs_group = { > > + .attrs = bma400_attributes, > > +}; > > + > > +static int bma400_read_raw(struct iio_dev *indio_dev, > > + struct iio_chan_spec const *chan, int *val, > > + int *val2, long mask) > > +{ > > + struct bma400_data *data = iio_priv(indio_dev); > > + int ret; > > + > > + switch (mask) { > > + case IIO_CHAN_INFO_PROCESSED: > > + mutex_lock(&data->mutex); > > + ret = bma400_get_temp_reg(data, val, val2); > > + mutex_unlock(&data->mutex); > > + return ret; > > + case IIO_CHAN_INFO_RAW: > > + mutex_lock(&data->mutex); > > + ret = bma400_get_accel_reg(data, chan, val); > > + mutex_unlock(&data->mutex); > > + return ret; > > + case IIO_CHAN_INFO_SAMP_FREQ: > > + switch (chan->type) { > > + case IIO_ACCEL: > > + if (!data->sample_freq) > > + return -EINVAL; > > + > > + *val = data->sample_freq->hz; > > + if (!data->sample_freq->micro_hz) > > + return IIO_VAL_INT; > > + > > + *val2 = data->sample_freq->micro_hz; > > + return IIO_VAL_INT_PLUS_MICRO; > > + case IIO_TEMP: > > + /* > > +* Runs at a fixed sampling frequency. See Section 4.4 > > +* of the datasheet. > > +*/ > > + *val = 6; > > + *val2 = 25; > > + return IIO_VAL_INT_PLUS_MICRO; > > + default: > > + return -EINVAL; > > + } > > + case IIO_CHAN_INFO_SCALE: > > + *val = 0; > > + *val2 = data->scale; > > + return IIO_VAL_INT_PLUS_MICRO; > > + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: > > + /* > > +* TODO: We could avoid this logic and returning -EINVAL here if > > +* we set both the low-power and normal mode OSR registers when > > +* we configure the device. > > +*/ > > + if (data->oversampling_ratio < 0) > > + return -EINVAL; > > + > > + *val = data->oversampling_ratio; > > + return IIO_VAL_INT; > > + default: > > + return -EINVAL; > > + } > > +} > > + > > +static int bma400_write_raw(struct iio_dev *indio_dev, > > + struct iio_chan_spec const *chan, int val, int val2, > > + long mask) > > +{ > > + int ret; > > + struct bma400_data *data = iio_priv(indio_dev); > > + > > + switch (mask) { > > + case IIO_CHAN_INFO_SAMP_FREQ: > > + /* > > +* The sample frequency is readonly for the temperature > > +* register and a fixed value in low-power mode. > > +*/ > > + if (chan->type != IIO_ACCEL) > > + return -EINVAL; > > + > > + mutex_lock(&data->mutex); > > + ret = bma400_set_accel_output_data_rate(data, val, val2); > > + mutex_unlock(&data->mutex); > > + return ret; > > + case IIO_CHAN_INFO_SCALE: > > + if (val != 0) > > + return -EINVAL; > > + > > + mutex_lock(&data->mutex); > > + ret = bma400_set_accel_scale(data, val2); > > + mutex_unlock(&data->mutex); > > + return ret; > > + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: > > + mutex_lock(&data->mutex); > > + ret = bma400_set_accel_oversampling_ratio(data, val); > > + mutex_unlock(&data->mutex); > > + return ret; > > + default: > > + return -EINVAL; > > + } > > +} > > + > > +static int bma400_write_raw_get_fmt(struct iio_dev *indio_dev, > > + struct iio_chan_spec const *chan, > > + long mask) > > +{ > > + switch (mask) { > > + case IIO_CHAN_INFO_SAMP_FREQ: > > + return IIO_VAL_INT_PLUS_MICRO; > > + case IIO_CHAN_INFO_SCALE: > > + return IIO_VAL_INT_PLUS_MICRO; > > + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: > > + return IIO_VAL_INT; > > + default: > > + return -EINVAL; > > + } > > +} > > + > > +static const struct iio_info bma400_info = { > > + .attrs = &bma400_attrs_group, > > + .read_raw = bma400_read_raw, > > + .write_raw = bma400_write_raw, > > + .write_raw_get_fmt = bma400_write_raw_get_fmt, > > +}; > > + > > +int bma400_probe(struct device *dev, > > +struct regmap *regmap, > > +const char *name) > > +{ > > + int ret; > > + struct bma400_data *data; > > + struct iio_dev *indio_dev; > > + > > + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); > > + if (!indio_dev) > > + return -ENOMEM; > > + > > + data = iio_priv(indio_dev); > > + data->regmap = regmap; > > + data->dev = dev; > > + > > + ret = bma400_init(data); > > + if (ret < 0) > > + return ret; > > + > > + ret = iio_read_mount_matrix(dev, "mount-matrix", > > + &data->orientation); > > Looks like the above will fit on one line.. > > > + if (ret) > > + return ret; > > + > > + mutex_init(&data->mutex); > > + indio_dev->dev.parent = dev; > > + indio_dev->name = name; > > + indio_dev->info = &bma400_info; > > + indio_dev->channels = bma400_channels; > > + indio_dev->num_channels = ARRAY_SIZE(bma400_channels); > > + indio_dev->modes = INDIO_DIRECT_MODE; > > + > > + dev_set_drvdata(dev, indio_dev); > > + > > + ret = iio_device_register(indio_dev); > > + if (ret < 0) { > > + dev_err(dev, "unable to register iio device\n"); > > + return ret; > > Drop the return out of the if statement and no need for the return 0 below. I'd also avoid the error message altogether > > > + } > > + > > + return 0; > > +} > > +EXPORT_SYMBOL(bma400_probe); > > + > > +int bma400_remove(struct device *dev) > > +{ > > + int ret; > > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > > + struct bma400_data *data = iio_priv(indio_dev); > > + > > + mutex_lock(&data->mutex); > > + ret = bma400_softreset(data); > > + if (ret < 0) { > > + /* > > +* If the softreset failed, try to put the device in > > +* sleep mode, but still report the error. > > +*/ > > + dev_err(data->dev, "Failed to reset the device"); > > + bma400_set_power_mode(data, POWER_MODE_SLEEP); > > + } > > + mutex_unlock(&data->mutex); > > + > > + iio_device_unregister(indio_dev); > > + > > + return ret; > > +} > > +EXPORT_SYMBOL(bma400_remove); > > + > > +MODULE_AUTHOR("Dan Robertson "); > > +MODULE_DESCRIPTION("Bosch BMA400 triaxial acceleration sensor"); > > +MODULE_LICENSE("GPL"); > > diff --git a/drivers/iio/accel/bma400_i2c.c b/drivers/iio/accel/bma400_i2c.c > > new file mode 100644 > > index ..227012a32e13 > > --- /dev/null > > +++ b/drivers/iio/accel/bma400_i2c.c > > @@ -0,0 +1,54 @@ > > +// SPDX-License-Identifier: GPL-2.0-only > > +/* > > + * bma400-i2c.c - I2C IIO driver for Bosch BMA400 triaxial acceleration > > sensor. > > + * > > + * Copyright 2019 Dan Robertson > > + * > > + * I2C address is either 0x14 or 0x15 depending on SDO > > + * > > This blank line doesn't add anything (nitpick) > > > + */ > > +#include > > +#include > > +#include > > Slight preference for alphabetical order. Also why ACPI? > > > +#include > > +#include > > + > > +#include "bma400.h" > > + > > +static int bma400_i2c_probe(struct i2c_client *client, > > + const struct i2c_device_id *id) > > +{ > > + struct regmap *regmap; > > + > > + regmap = devm_regmap_init_i2c(client, > > + &bma400_regmap_config); > > + > > + return bma400_probe(&client->dev, regmap, id->name); > > +} > > + > > +static int bma400_i2c_remove(struct i2c_client *client) > > +{ > > + return bma400_remove(&client->dev); > > +} > > + > > +static const struct i2c_device_id bma400_i2c_ids[] = { > > + { "bma400", 0 }, > > + { } > > +}; > > + > > +MODULE_DEVICE_TABLE(i2c, bma400_i2c_ids); > > + > > Good to have a of_device_id table as well from the start. > There is a general (but slow) move to stop using the fallback > to the i2c_device_id table. > > > +static struct i2c_driver bma400_i2c_driver = { > > + .driver = { > > + .name = "bma400", > > + }, > > + .probe= bma400_i2c_probe, > > + .remove = bma400_i2c_remove, > > + .id_table = bma400_i2c_ids, > > +}; > > + > > +module_i2c_driver(bma400_i2c_driver); > > + > > +MODULE_AUTHOR("Dan Robertson "); > > +MODULE_DESCRIPTION("Bosch BMA400 triaxial acceleration sensor"); > > +MODULE_LICENSE("GPL"); > > > > > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH] iio: light: add driver support for MAX44009
+ > > + buf[0] = lux; > > + iio_push_to_buffers_with_timestamp(indio_dev, &buf, data->timestamp); > > + iio_trigger_notify_done(data->trigger); > > + > > + iio_push_event(indio_dev, > > + IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, > > + IIO_EV_TYPE_THRESH, direction), > > + data->timestamp); > > + > > + ret = max44009_write_reg(data, MAX44009_REG_ENABLE, 1); > > + if (ret < 0) { > > + goto err; > > + } > > + > > + return IRQ_HANDLED; > > + > > +err: > > + /* Re-enable interrupt */ > > + max44009_write_reg(data, MAX44009_REG_ENABLE, 1); > > + return IRQ_NONE; > > +} > > + > > +static irqreturn_t max44009_irq_handler(int irq, void *p) > > +{ > > + struct iio_dev *indio_dev = p; > > + struct max44009_data *data = iio_priv(indio_dev); > > + > > + data->timestamp = iio_get_time_ns(indio_dev); > > We have a standard core function to do this.. > iio_pollfunc_store_time. > > There 'might' be a reason to do it differently depending > on whether you really need it to be that good if you > can't identify whether it is your interrupt until the > interrupt thread. > > > > + return IRQ_WAKE_THREAD; > > +} > > + > > +static int max44009_probe(struct i2c_client *client, > > + const struct i2c_device_id *id) > > +{ > > + struct max44009_data *data; > > + struct iio_dev *indio_dev; > > + int ret; > > + > > + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); > > + if (!indio_dev) { > > + return -ENOMEM; > > + } > > + data = iio_priv(indio_dev); > > + i2c_set_clientdata(client, indio_dev); > > + data->client = client; > > + indio_dev->dev.parent = &client->dev; > > + indio_dev->info = &max44009_info; > > + indio_dev->modes = INDIO_DIRECT_MODE; > > + indio_dev->name = MAX44009_DRV_NAME; > > + indio_dev->channels = max44009_channels; > > + indio_dev->num_channels = ARRAY_SIZE(max44009_channels); > > + mutex_init(&data->lock); > > + > > + /* Clear stale interrupt bit */ > > + ret = max44009_read_reg(data, MAX44009_REG_STATUS); > > + if (ret < 0) { > > + goto err; > > + } > > + > > + if (client->irq > 0) { > > + ret = devm_request_threaded_irq(&client->dev, client->irq, > > + max44009_irq_handler, > > This is wrong. The interrupt handler should call the iio_trigger_poll > functions > to cause the trigger handlers for all attached devices to be called. > > If for some reason the trigger is only suitable for use by this device > then things get more blurred, but if not, the interrupt handler itself > should establish that the interrupt is the data ready signal and call > iio_trigger_poll. If it is in a thread, call iio_trigger_poll_chained. > > The interrupt should be cleared (if it hasn't naturally happened for > some other reason, in the trigger try_reenable callback. > > > > + max44009_trigger_handler, > > + IRQF_TRIGGER_FALLING | > > + IRQF_ONESHOT, > > + MAX44009_IRQ_NAME, indio_dev); > > + if (ret < 0) { > > + goto err; > > + } > > + > > + ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, > > + max44009_irq_handler, > > + max44009_trigger_handler, > > + NULL); > > + if (ret < 0) { > > It's going to change anyway (see below) but note kernel style is no > brackets when only one item in an block like this. > > > + goto err; > > + } > > + > > + data->trigger = devm_iio_trigger_alloc(indio_dev->dev.parent, > > + "%s-dev%d", > > + indio_dev->name, > > + indio_dev->id); > > + if (!data->trigger) { > > + ret = -ENOMEM; > > + goto err; > > + } > > + data->trigger->dev.parent = indio_dev->dev.parent; > > + data->trigger->ops = &max44009_trigger_ops; > > + iio_trigger_set_drvdata(data->trigger, indio_dev); > > + > > + ret = devm_iio_trigger_register(&client->dev, data->trigger); > > + if (ret < 0) { > > + goto err; > > + } > > + } > > + > > + ret = devm_iio_device_register(&client->dev, indio_dev); > > + if (ret < 0) { > > + goto err; > > Without the mutex destroy these all just become return ret; > > > + } > > + > > + return 0; > > +err: > > + mutex_destroy(&data->lock); > > mutex destroy is only really useful for lock debugging. Given we don't > leave anything behind here anyway, it just makes the flow more complex > for no gain. We very rarely bother with it as a result. > > > + return ret; > > +} > > + > > +static const struct i2c_device_id max44009_id[] = { > > + { "max44009", 0 }, > > + { } > > +}; > > +MODULE_DEVICE_TABLE(i2c, max44009_id); > > + > > +static struct i2c_driver max44009_driver = { > > + .driver = { > > + .name = MAX44009_DRV_NAME, > > + }, > > + .probe = max44009_probe, > > + .id_table = max44009_id, > > +}; > > +module_i2c_driver(max44009_driver); > > + > > +static const struct of_device_id max44009_of_match[] = { > > + { .compatible = "maxim,max44009" }, > > + { } > > +}; > > +MODULE_DEVICE_TABLE(of, max44009_of_match); > > + > > +MODULE_AUTHOR("Robert Eshleman "); > > +MODULE_LICENSE("GPL v2"); > > +MODULE_VERSION("1.0.0"); > Please drop. MODULE_VERSION provides very little useful info > and tends to lead to people assuming it does. > > Userspace has no obligation to ever look at it and we can't > break userspace that doesn't so it doesn't provide any value. > > > +MODULE_DESCRIPTION("MAX44009 ambient light sensor driver"); > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH 3/3] iio: light: apple-ib-als: Add driver for ALS on iBridge chip.
ice */ > + als_dev->hid_dev = hdev; > + als_dev->cfg_report = state_field->report; > + als_dev->illum_field = illum_field; > + > + als_dev->cur_hysteresis = APPLEALS_DEF_CHANGE_SENS; > + als_dev->cur_sensitivity = APPLEALS_DEF_CHANGE_SENS; > + appleals_config_sensor(als_dev, false, als_dev->cur_sensitivity); > + > + rc = appleals_config_iio(als_dev); > + if (rc) > + return rc; > + > + return 0; > +} > + > +static void appleals_remove(struct hid_device *hdev) > +{ > + struct appleals_device *als_dev = > + appleib_get_drvdata(hid_get_drvdata(hdev), > + &appleals_hid_driver); > + could be a lot less if devm_ were used? > + if (als_dev->iio_dev) { > + iio_device_unregister(als_dev->iio_dev); > + > + iio_trigger_unregister(als_dev->iio_trig); > + iio_trigger_free(als_dev->iio_trig); > + als_dev->iio_trig = NULL; > + > + iio_triggered_buffer_cleanup(als_dev->iio_dev); > + iio_device_free(als_dev->iio_dev); > + als_dev->iio_dev = NULL; > + } > + > + als_dev->hid_dev = NULL; > +} > + > +#ifdef CONFIG_PM > +static int appleals_reset_resume(struct hid_device *hdev) > +{ > + struct appleals_device *als_dev = > + appleib_get_drvdata(hid_get_drvdata(hdev), > + &appleals_hid_driver); > + > + appleals_config_sensor(als_dev, als_dev->events_enabled, > +als_dev->cur_sensitivity); > + > + return 0; > +} > +#endif > + > +static struct hid_driver appleals_hid_driver = { > + .name = "apple-ib-als", > + .probe = appleals_probe, > + .remove = appleals_remove, > + .event = appleals_hid_event, > +#ifdef CONFIG_PM > + .reset_resume = appleals_reset_resume, > +#endif > +}; > + > +static int appleals_platform_probe(struct platform_device *pdev) > +{ > + struct appleib_platform_data *pdata = pdev->dev.platform_data; > + struct appleib_device *ib_dev = pdata->ib_dev; > + struct appleals_device *als_dev; > + int rc; > + > + als_dev = kzalloc(sizeof(*als_dev), GFP_KERNEL); > + if (!als_dev) > + return -ENOMEM; > + > + als_dev->ib_dev = ib_dev; > + als_dev->log_dev = pdata->log_dev; > + > + rc = appleib_register_hid_driver(ib_dev, &appleals_hid_driver, als_dev); > + if (rc) { > + dev_err(als_dev->log_dev, "Error registering hid driver: %d\n", > + rc); > + goto error; > + } > + > + platform_set_drvdata(pdev, als_dev); > + > + return 0; > + > +error: > + kfree(als_dev); > + return rc; > +} > + > +static int appleals_platform_remove(struct platform_device *pdev) > +{ > + struct appleib_platform_data *pdata = pdev->dev.platform_data; > + struct appleib_device *ib_dev = pdata->ib_dev; > + struct appleals_device *als_dev = platform_get_drvdata(pdev); > + int rc; > + > + rc = appleib_unregister_hid_driver(ib_dev, &appleals_hid_driver); > + if (rc) { > + dev_err(als_dev->log_dev, > + "Error unregistering hid driver: %d\n", rc); > + goto error; > + } > + > + kfree(als_dev); > + > + return 0; > + > +error: > + return rc; > +} > + > +static const struct platform_device_id appleals_platform_ids[] = { > + { .name = PLAT_NAME_IB_ALS }, > + { } > +}; > +MODULE_DEVICE_TABLE(platform, appleals_platform_ids); > + > +static struct platform_driver appleals_platform_driver = { > + .id_table = appleals_platform_ids, > + .driver = { > + .name = "apple-ib-als", > + }, > + .probe = appleals_platform_probe, > + .remove = appleals_platform_remove, > +}; > + > +module_platform_driver(appleals_platform_driver); > + > +MODULE_AUTHOR("Ronald Tschalär"); > +MODULE_DESCRIPTION("Apple iBridge ALS driver"); > +MODULE_LICENSE("GPL v2"); > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH v2 2/3] dt-bindings: iio: ot: Add docs pat9125
> Add documentation for the optical tracker PAT9125 and > "ot" directory for Optical Tracker chip. > > Signed-off-by: Alexandre Mergnat > --- > .../devicetree/bindings/iio/ot/pat9125.txt | 18 ++ > 1 file changed, 18 insertions(+) > create mode 100644 Documentation/devicetree/bindings/iio/ot/pat9125.txt > > diff --git a/Documentation/devicetree/bindings/iio/ot/pat9125.txt > b/Documentation/devicetree/bindings/iio/ot/pat9125.txt > new file mode 100644 > index ..2ffacaafba8e > --- /dev/null > +++ b/Documentation/devicetree/bindings/iio/ot/pat9125.txt > @@ -0,0 +1,18 @@ > +PixArt Imaging PAT9125 Optical Tracking Miniature Chip device driver > + > +Required properties: > + - compatible: must be "pixart,pat9125" > + - reg: i2c address where to find the device > + - interrupts : the sole interrupt generated by the device maybe delete space before : > + > + Refer to interrupt-controller/interrupts.txt for generic > + interrupt client node bindings. > + > +Example: > + > +pat9125@75 { > + compatible = "pixart,pat9125"; > + reg = <0x75>; > + interrupt-parent = <&gpio3>; > + interrupts = <12 IRQ_TYPE_EDGE_FALLING>; > +}; > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH v2 3/3] iio: Add PAT9125 optical tracker sensor
EG, ret); > + return ret; > + } > + > + ret = devm_iio_device_register(&client->dev, indio_dev); > + if (ret) { > + dev_err(&client->dev, "IIO device register failed"); > + iio_triggered_buffer_cleanup(indio_dev); > + return ret; > + } > + > + i2c_set_clientdata(client, indio_dev); > + > + dev_info(&client->dev, "%s: sensor '%s'\n", > + dev_name(&indio_dev->dev), > + client->name); please avoid log output, it just clutters the log > + > + /* Make read to reset motion bit status */ > + ret = pat9125_read_delta(data); > + if (ret) { > + dev_err(&client->dev, "Read register failed"); > + return ret; > + } > + > + /* Init GPIO IRQ */ > + if (client->irq) { > + ret = devm_request_threaded_irq(&client->dev, > + client->irq, > + pat9125_event_handler, > + NULL, > + IRQF_TRIGGER_FALLING, > + "pat9125", > + indio_dev); > + if (ret) { > + dev_err(&client->dev, "GPIO IRQ init failed"); > + return ret; > + } > + } > + return 0; > +} > + > +static int pat9125_remove(struct i2c_client *client) > +{ > + struct iio_dev *indio_dev = devm_iio_device_alloc(&client->dev, > sizeof(struct pat9125_data)); > + > + iio_device_unregister(indio_dev); > + iio_triggered_buffer_cleanup(indio_dev); > + > + dev_info(&client->dev, "PAT9125 removed\n"); > + > + return 0; > +} > + > +static const struct i2c_device_id pat9125_id[] = { > + { "pat9125", 0 }, > + {} > +}; > +MODULE_DEVICE_TABLE(i2c, pat9125_id); > + > +static const unsigned short normal_i2c[] = { > + PAT9125_I2C_ADDR_HI, > + PAT9125_I2C_ADDR_LO, > + PAT9125_I2C_ADDR_NC, > + I2C_CLIENT_END > +}; > + > +static struct i2c_driver pat9125_driver = { > + .driver = { > + .name = "pat9125", > + }, > + .probe = pat9125_probe, > + .remove = pat9125_remove, > + .address_list = normal_i2c, > + .id_table = pat9125_id, > +}; > + > +module_i2c_driver(pat9125_driver); > + > +MODULE_AUTHOR("Alexandre Mergnat "); > +MODULE_DESCRIPTION("Optical Tracking sensor"); > +MODULE_LICENSE("GPL"); > \ No newline at end of file > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH 1/3] iio: Add driver for Infineon DPS310
; 0) > + goto err; > + > + /* Turn on temperature measurement in the background */ > + r = regmap_write_bits(data->regmap, DPS310_MEAS_CFG, > + DPS310_MEAS_CTRL_BITS, > + DPS310_TEMP_EN | DPS310_BACKGROUND); > + if (r < 0) > + goto err; > + > + /* > + * Calibration coefficients required for reporting temperature. > + * They are available 40ms after the device has started > + */ > + r = regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready, > + ready & DPS310_COEF_RDY, 1, 4); > + if (r < 0) > + goto err; > + > + r = dps310_get_temp_coef(data); > + if (r < 0) > + goto err; > + > + r = devm_iio_device_register(&client->dev, iio); > + if (r) > + goto err; > + > + i2c_set_clientdata(client, iio); > + > + dev_info(&client->dev, "%s: sensor '%s'\n", dev_name(&iio->dev), > + client->name); don't clutter the log > + > + return 0; > + > +err: > + regmap_write(data->regmap, DPS310_RESET, DPS310_RESET_MAGIC); > + return r; > +} > + > +static int dps310_remove(struct i2c_client *client) > +{ > + struct dps310_data *data = i2c_get_clientdata(client); > + > + return regmap_write(data->regmap, DPS310_RESET, DPS310_RESET_MAGIC); > +} > + > +static const struct i2c_device_id dps310_id[] = { > + { "dps310", 0 }, > + {} > +}; > +MODULE_DEVICE_TABLE(i2c, dps310_id); > + > +static const unsigned short normal_i2c[] = { > + 0x77, 0x76, I2C_CLIENT_END > +}; > + > +static struct i2c_driver dps310_driver = { > + .driver = { > + .name = "dps310", > + }, > + .probe = dps310_probe, > + .remove = dps310_remove, > + .address_list = normal_i2c, > + .id_table = dps310_id, > +}; > +module_i2c_driver(dps310_driver); > + > +MODULE_AUTHOR("Joel Stanley "); > +MODULE_DESCRIPTION("Infineon DPS310 pressure and temperature sensor"); > +MODULE_LICENSE("GPL v2"); > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH 3/3] iio: dps310: Add pressure sensing capability
, > default: > return -EINVAL; > } > +} > > - return -EINVAL; > +static int dps310_read_raw(struct iio_dev *iio, > +struct iio_chan_spec const *chan, > +int *val, int *val2, long mask) > +{ > + struct dps310_data *data = iio_priv(iio); > + > + switch (chan->type) { > + case IIO_PRESSURE: > + return dps310_read_pressure(data, val, val2, mask); > + > + case IIO_TEMP: > + return dps310_read_temp(data, val, val2, mask); > + > + default: > + return -EINVAL; > + } > } > > static const struct regmap_config dps310_regmap_config = { > @@ -390,6 +651,13 @@ static int dps310_probe(struct i2c_client *client, > return PTR_ERR(data->regmap); > > /* > + * Set up pressure sensor in single sample, one measurement per second > + * mode > + */ > + r = regmap_write(data->regmap, DPS310_PRS_CFG, > + DPS310_CALC_RATE(1) | DPS310_CALC_PRC(1)); > + > + /* >* Set up external (MEMS) temperature sensor in single sample, one >* measurement per second mode >*/ > @@ -399,16 +667,23 @@ static int dps310_probe(struct i2c_client *client, > if (r < 0) > goto err; > > - /* Temp shift is disabled when PRC <= 8 */ > + /* Temp and pressure shifts are disabled when PRC <= 8 */ > r = regmap_write_bits(data->regmap, DPS310_CFG_REG, > - DPS310_TMP_SHIFT_EN, 0); > + DPS310_TMP_SHIFT_EN | DPS310_PRS_SHIFT_EN, 0); > + if (r < 0) > + goto err; > + > + /* MEAS_CFG doesn't seem to update unless first written with 0 */ > + r = regmap_write_bits(data->regmap, DPS310_MEAS_CFG, > + DPS310_MEAS_CTRL_BITS, 0); > if (r < 0) > goto err; > > - /* Turn on temperature measurement in the background */ > + /* Turn on temperature and pressure measurement in the background */ > r = regmap_write_bits(data->regmap, DPS310_MEAS_CFG, > DPS310_MEAS_CTRL_BITS, > - DPS310_TEMP_EN | DPS310_BACKGROUND); > + DPS310_PRS_EN | DPS310_TEMP_EN | > + DPS310_BACKGROUND); > if (r < 0) > goto err; > > @@ -421,7 +696,7 @@ static int dps310_probe(struct i2c_client *client, > if (r < 0) > goto err; > > - r = dps310_get_temp_coef(data); > + r = dps310_get_coefs(data); > if (r < 0) > goto err; > > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH v4 3/3] iio: Add PAT9125 optical tracker sensor
5_info; > + indio_dev->modes = INDIO_DIRECT_MODE; > + > + ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL, > + pat9125_threaded_trigger_handler, &pat9125_buffer_ops); > + if (ret) { > + dev_err(&client->dev, "unable to setup triggered buffer\n"); > + return ret; > + } > + > + data->indio_trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", > + indio_dev->name, indio_dev->id); > + if (!data->indio_trig) > + return -ENOMEM; > + data->indio_trig->dev.parent = &client->dev; > + data->indio_trig->ops = &pat9125_trigger_ops; > + iio_trigger_set_drvdata(data->indio_trig, data); > + ret = devm_iio_trigger_register(&client->dev, data->indio_trig); > + if (ret) { > + dev_err(&client->dev, "unable to register trigger\n"); > + return ret; > + } > + > + data->regmap = devm_regmap_init_i2c(client, &pat9125_regmap_config); > + if (IS_ERR(data->regmap)) { > + dev_err(&client->dev, "regmap init failed %ld\n", > + PTR_ERR(data->regmap)); > + return PTR_ERR(data->regmap); > + } > + > + /* Check device ID */ > + ret = regmap_read(data->regmap, PAT9125_PRD_ID1_REG, &sensor_pid); > + if (ret < 0) { > + dev_err(&client->dev, "register 0x%x access failed %d\n", > + PAT9125_PRD_ID1_REG, ret); > + return ret; > + } > + if (sensor_pid != PAT9125_SENSOR_ID_VAL) I'd rather put some logging here; what is the actual value obtained? > + return -ENODEV; > + > + /* Switch to bank0 (Magic number)*/ > + ret = regmap_write(data->regmap, 0x7F, 0x00); maybe a #define for 0x7f, PAT9125_BANK_SWITCH_REG > + if (ret < 0) { > + dev_err(indio_dev->dev.parent, "register 0x%x access failed > %d\n", > + 0x7F, ret); > + return ret; > + } > + > + /* Software reset */ > + ret = regmap_write_bits(data->regmap, > + PAT9125_CONFIG_REG, > + PAT9125_RESET_BIT, > + 1); > + if (ret < 0) { > + dev_err(&client->dev, "register 0x%x access failed %d\n", > + PAT9125_CONFIG_REG, ret); > + return ret; > + } > + > + msleep(20); > + > + /* Init GPIO IRQ */ > + if (client->irq) { > + ret = devm_request_threaded_irq(&client->dev, > + client->irq, > + NULL, > + pat9125_threaded_event_handler, > + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, > + "pat9125", > + indio_dev); > + if (ret) { > + dev_err(&client->dev, "GPIO IRQ init failed\n"); > + return ret; > + } > + } > + > + ret = devm_iio_device_register(&client->dev, indio_dev); > + if (ret) { > + dev_err(&client->dev, "IIO device register failed\n"); > + return ret; > + } > + > + return 0; > +} > + > +static const struct i2c_device_id pat9125_id[] = { > + { "pat9125", 0 }, > + {} > +}; > +MODULE_DEVICE_TABLE(i2c, pat9125_id); > + > +static const unsigned short normal_i2c[] = { pat9125_ prefix please > + PAT9125_I2C_ADDR_HI, > + PAT9125_I2C_ADDR_LO, > + PAT9125_I2C_ADDR_NC, > + I2C_CLIENT_END > +}; > + > +static struct i2c_driver pat9125_driver = { > + .driver = { > + .name = "pat9125", > + }, > + .probe = pat9125_probe, > + .address_list = normal_i2c, > + .id_table = pat9125_id, > +}; > + > +module_i2c_driver(pat9125_driver); > + > +MODULE_AUTHOR("Alexandre Mergnat "); > +MODULE_DESCRIPTION("Optical Tracking sensor"); > +MODULE_LICENSE("GPL"); > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH] iio: potentiometer: add a driver for Maxim 5432-5435
sk_separate = BIT(IIO_CHAN_INFO_RAW), > + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), > + } > +}; > + > +static int max5432_read_raw(struct iio_dev *indio_dev, > + struct iio_chan_spec const *chan, > + int *val, int *val2, long mask) > +{ > + struct max5432_data *data = iio_priv(indio_dev); > + > + if (mask != IIO_CHAN_INFO_SCALE) > + return -EINVAL; > + > + *val = data->ohm; > + *val2 = MAX5432_MAX_POS; > + > + return IIO_VAL_FRACTIONAL; > +} > + > +static int max5432_write_raw(struct iio_dev *indio_dev, > + struct iio_chan_spec const *chan, > + int val, int val2, long mask) > +{ > + struct max5432_data *data = iio_priv(indio_dev); > + u8 data_byte; > + > + if (mask != IIO_CHAN_INFO_RAW) > + return -EINVAL; > + > + if (val < 0 || val > MAX5432_MAX_POS) > + return -EINVAL; > + > + if (val2 != 0) > + return -EINVAL; > + > + /* Wiper position is in bits D7-D3. (D2-D0 are don't care bits.) */ > + data_byte = val << 3; > + return i2c_smbus_write_byte_data( > + data->client, chan->address, data_byte); > +} > + > +static const struct iio_info max5432_info = { > + .read_raw = max5432_read_raw, > + .write_raw = max5432_write_raw, > +}; > + > +static int max5432_probe( > + struct i2c_client *client, const struct i2c_device_id *id) > +{ > + struct device *dev = &client->dev; > + struct iio_dev *indio_dev; > + struct max5432_data *data; > + > + indio_dev = devm_iio_device_alloc(dev, sizeof(struct max5432_data)); > + if (!indio_dev) > + return -ENOMEM; > + > + i2c_set_clientdata(client, indio_dev); > + > + data = iio_priv(indio_dev); > + data->client = client; > + data->ohm = (u32)of_device_get_match_data(dev); > + > + indio_dev->dev.parent = dev; > + indio_dev->info = &max5432_info; > + indio_dev->channels = max5432_channels; > + indio_dev->num_channels = ARRAY_SIZE(max5432_channels); > + indio_dev->name = client->name; > + > + return devm_iio_device_register(dev, indio_dev); > +} > + > +static const struct of_device_id max5432_dt_ids[] = { > + { .compatible = "maxim,max5432", .data = (void *)OHM_50K }, > + { .compatible = "maxim,max5433", .data = (void *)OHM_100K }, > + { .compatible = "maxim,max5434", .data = (void *)OHM_50K }, > + { .compatible = "maxim,max5435", .data = (void *)OHM_100K }, > + { /* sentinel */ } > +}; > +MODULE_DEVICE_TABLE(of, max5432_dt_ids); > + > +static struct i2c_driver max5432_driver = { > + .driver = { > + .name = "max5432", > + .of_match_table = of_match_ptr(max5432_dt_ids), > + }, > + .probe = max5432_probe, > +}; > + > +module_i2c_driver(max5432_driver); > + > +MODULE_AUTHOR("Martin Kaiser "); > +MODULE_DESCRIPTION("max5432-max5435 digital potentiometers"); > +MODULE_LICENSE("GPL v2"); > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH v2 3/4] mb12x2.c: add distance iio sensor with i2c
; + indio_dev->num_channels = ARRAY_SIZE(mb12x2_channels); > + > + mutex_init(&data->lock); > + > + init_completion(&data->ranging); > + > + data->gpiod_status = devm_gpiod_get(dev, "status", GPIOD_IN); > + if (IS_ERR(data->gpiod_status)) { > + if (PTR_ERR(data->gpiod_status) == -ENOENT) { > + dev_warn(dev, "no status gpio --> use sleep instead\n"); > + data->gpiod_status = NULL; > + } else { > + dev_err(dev, "cannot setup gpio; err=%ld\n", > + PTR_ERR(data->gpiod_status)); > + return PTR_ERR(data->gpiod_status); > + } > + } > + > + if (data->gpiod_status) { > + data->irqnr = gpiod_to_irq(data->gpiod_status); > + if (data->irqnr < 0) { > + dev_err(dev, "gpiod_to_irq: %d\n", data->irqnr); > + return data->irqnr; > + } > + > + ret = devm_request_irq(dev, data->irqnr, mb12x2_handle_irq, > + IRQF_TRIGGER_FALLING, id->name, indio_dev); > + if (ret < 0) { > + dev_err(dev, "request_irq: %d\n", ret); > + return ret; > + } > + } > + > + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, > + iio_pollfunc_store_time, mb12x2_trigger_handler, NULL); > + if (ret < 0) { > + dev_err(dev, "setup of iio triggered buffer failed\n"); > + return ret; > + } > + > + return devm_iio_device_register(dev, indio_dev); > +} > + > +static const struct of_device_id of_mb12x2_match[] = { > + { .compatible = "maxbotix,i2cxl", }, > + {}, > +}; > + > +MODULE_DEVICE_TABLE(of, of_mb12x2_match); > + > +static const struct i2c_device_id mb12x2_id[] = { > + { "maxbotix-i2cxl", }, > + { } > +}; > +MODULE_DEVICE_TABLE(i2c, mb12x2_id); > + > +static struct i2c_driver mb12x2_driver = { > + .driver = { > + .name = "maxbotix-i2cxl", > + .of_match_table = of_mb12x2_match, > + }, > + .probe = mb12x2_probe, > + .id_table = mb12x2_id, > +}; > +module_i2c_driver(mb12x2_driver); > + > +MODULE_AUTHOR("Andreas Klinger "); > +MODULE_DESCRIPTION("Maxbotix I2CXL-MaxSonar i2c ultrasonic ranger driver"); > +MODULE_LICENSE("GPL"); > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH] iio: magnetometer: Add support for PNI RM3100 9-axis magnetometer
) > +{ > + return rm3100_common_remove(&client->dev); > +} > + > +static const struct of_device_id rm3100_dt_match[] = { > + { .compatible = "pni,rm3100-i2c", }, > + { } > +}; > +MODULE_DEVICE_TABLE(of, rm3100_dt_match); > + > +static struct i2c_driver rm3100_driver = { > + .driver = { > + .name = "rm3100-i2c", > + .of_match_table = rm3100_dt_match, > + }, > + .probe_new = rm3100_probe, > + .remove = rm3100_remove, > +}; > +module_i2c_driver(rm3100_driver); > + > +MODULE_AUTHOR("Song Qiang "); > +MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer i2c driver"); > +MODULE_LICENSE("GPL v2"); > diff --git a/drivers/iio/magnetometer/rm3100-spi.c > b/drivers/iio/magnetometer/rm3100-spi.c > new file mode 100644 > index ..2c7dd9e3a1a2 > --- /dev/null > +++ b/drivers/iio/magnetometer/rm3100-spi.c > @@ -0,0 +1,72 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Support for PNI RM3100 9-axis geomagnetic sensor a spi bus. > + * > + * Copyright (C) 2018 Song Qiang > + * > + * User Manual available at > + * <https://www.pnicorp.com/download/rm3100-user-manual/> > + */ > + > +#include > + > +#include "rm3100.h" > + > +static const struct regmap_config rm3100_regmap_config = { > + .reg_bits = 8, > + .val_bits = 8, > + > + .rd_table = &rm3100_readable_table, > + .wr_table = &rm3100_writable_table, > + .volatile_table = &rm3100_volatile_table, > + > + .read_flag_mask = 0x80, > + > + .cache_type = REGCACHE_RBTREE, > +}; > + > +static int rm3100_probe(struct spi_device *spi) > +{ > + struct regmap *regmap; > + int ret; > + > + /* Actually this device supports both mode 0 and mode 3. */ > + spi->mode = SPI_MODE_0; > + /* data rates cannot exceeds 1Mbits. */ exceed > + spi->max_speed_hz = 100; > + spi->bits_per_word = 8; > + ret = spi_setup(spi); > + if (ret) > + return ret; > + > + regmap = devm_regmap_init_spi(spi, &rm3100_regmap_config); > + if (IS_ERR(regmap)) > + return PTR_ERR(regmap); > + > + return rm3100_common_probe(&spi->dev, regmap, spi->irq); > +} > + > +static int rm3100_remove(struct spi_device *spi) > +{ > + return rm3100_common_remove(&spi->dev); > +} > + > +static const struct of_device_id rm3100_dt_match[] = { > + { .compatible = "pni,rm3100-spi", }, > + { } > +}; > +MODULE_DEVICE_TABLE(of, rm3100_dt_match); > + > +static struct spi_driver rm3100_driver = { > + .driver = { > + .name = "rm3100-spi", > + .of_match_table = rm3100_dt_match, > + }, > + .probe = rm3100_probe, > + .remove = rm3100_remove, > +}; > +module_spi_driver(rm3100_driver); > + > +MODULE_AUTHOR("Song Qiang "); > +MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer spi driver"); > +MODULE_LICENSE("GPL v2"); > diff --git a/drivers/iio/magnetometer/rm3100.h > b/drivers/iio/magnetometer/rm3100.h > new file mode 100644 > index ..5e30bc0f5149 > --- /dev/null > +++ b/drivers/iio/magnetometer/rm3100.h > @@ -0,0 +1,90 @@ > +/* SPDX-License-Identifier: GPL-2.0+ */ > +/* > + * Header file for PNI RM3100 driver > + * > + * Copyright (C) 2018 Song Qiang > + */ > + > +#ifndef RM3100_CORE_H > +#define RM3100_CORE_H > + > +#include > +#include > + > +#define RM_REG_REV_ID0x36 > + > +/* Cycle Count Registers MSBs and LSBs. */ > +#define RM_REG_CCXM 0x04 > +#define RM_REG_CCXL 0x05 > +#define RM_REG_CCYM 0x06 > +#define RM_REG_CCYL 0x07 > +#define RM_REG_CCZM 0x08 > +#define RM_REG_CCZL 0x09 > + > +/* Single Measurement Mode register. */ > +#define RM_REG_POLL 0x00 > +#define RM_POLL_PMX BIT(4) > +#define RM_POLL_PMY BIT(5) > +#define RM_POLL_PMZ BIT(6) > + > +/* Continues Measurement Mode register. */ > +#define RM_REG_CMM 0x01 > +#define RM_CMM_START BIT(0) > +#define RM_CMM_DRDM BIT(2) > +#define RM_CMM_PMX BIT(4) > +#define RM_CMM_PMY BIT(5) > +#define RM_CMM_PMZ BIT(6) > + > +/* TiMe Rate Configuration register. */ > +#define RM_REG_TMRC 0x0B > +#define RM_TMRC_OFFSET 0x92 > + > +/* Result Status register. */ > +#define RM_REG_STATUS0x34 > +#define RM_STATUS_DRDY BIT(7) > + > +/* Measurement result registers. */ > +#define RM_REG_MX2 0x24 > +#define RM_REG_MX1 0x25 > +#define RM_REG_MX0 0x26 > +#define RM_REG_MY2 0x27 > +#define RM_REG_MY1 0x28 > +#define RM_REG_MY0 0x29 > +#define RM_REG_MZ2 0x2a > +#define RM_REG_MZ1 0x2b > +#define RM_REG_MZ0 0x2c > + > +#define RM_REG_HSHAKE0x35 > + > +#define RM_W_REG_START RM_REG_POLL > +#define RM_W_REG_END RM_REG_REV_ID > +#define RM_R_REG_START RM_REG_POLL > +#define RM_R_REG_END RM_REG_HSHAKE > +#define RM_V_REG_START RM_REG_MX2 > +#define RM_V_REG_END RM_REG_HSHAKE > + > +/* Built-In Self Test reigister. */ > +#define RM_REG_BIST 0x33 > + > +struct rm3100_data { > + struct device *dev; > + struct regmap *regmap; > + struct completion measuring_done; > + bool use_interrupt; > + > + int conversion_time; > + > + /* To protect consistency of every measurement and sampling > + * frequency change operations. > + */ > + struct mutex lock; > +}; > + > +extern const struct regmap_access_table rm3100_readable_table; > +extern const struct regmap_access_table rm3100_writable_table; > +extern const struct regmap_access_table rm3100_volatile_table; > + > +int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq); > +int rm3100_common_remove(struct device *dev); > + > +#endif /* RM3100_CORE_H */ > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [RFC PATCH] iio: adc: Add Xilinx AMS driver
minor comments below > The AMS includes an ADC as well as on-chip sensors that can be used to > sample external voltages and monitor on-die operating conditions, such as > temperature and supply voltage levels. The AMS has two SYSMON blocks. > PL-SYSMON block is capable of monitoring off chip voltage and temperature. > PL-SYSMON block has DRP, JTAG and I2C interface to enable monitoring from > external master. Out of these interface currently only DRP is supported. > Other block PS-SYSMON is memory mapped to PS. > > The AMS can use internal channels to monitor voltage and temperature > as well as one primary and up to 16 auxiliary channels for measuring > external voltages. > > The voltage and temperature monitoring channels also have event > capability which allows to generate an interrupt when their value > falls below or raises above a set threshold. > > Signed-off-by: Manish Narani > --- > drivers/iio/adc/Kconfig | 10 + > drivers/iio/adc/Makefile |1 + > drivers/iio/adc/xilinx-ams.c | 1115 > ++ > drivers/iio/adc/xilinx-ams.h | 278 +++ > 4 files changed, 1404 insertions(+) > create mode 100644 drivers/iio/adc/xilinx-ams.c > create mode 100644 drivers/iio/adc/xilinx-ams.h > > diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig > index 72bc2b7..f1b8a5f 100644 > --- a/drivers/iio/adc/Kconfig > +++ b/drivers/iio/adc/Kconfig > @@ -931,4 +931,14 @@ config XILINX_XADC > The driver can also be build as a module. If so, the module will be > called > xilinx-xadc. > > +config XILINX_AMS > + tristate "Xilinx AMS driver" > + depends on ARCH_ZYNQMP || COMPILE_TEST > + depends on HAS_IOMEM > + help > + Say yes here to have support for the Xilinx AMS. > + > + The driver can also be build as a module. If so, the module will be > called > + xilinx-ams. > + > endmenu > diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile > index 28a9423..27ded4f 100644 > --- a/drivers/iio/adc/Makefile > +++ b/drivers/iio/adc/Makefile > @@ -85,3 +85,4 @@ obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o > xilinx-xadc-y := xilinx-xadc-core.o xilinx-xadc-events.o > obj-$(CONFIG_XILINX_XADC) += xilinx-xadc.o > obj-$(CONFIG_SD_ADC_MODULATOR) += sd_adc_modulator.o > +obj-$(CONFIG_XILINX_AMS) += xilinx-ams.o maybe put after xilinx-xadc > diff --git a/drivers/iio/adc/xilinx-ams.c b/drivers/iio/adc/xilinx-ams.c > new file mode 100644 > index 000..2f519e6 > --- /dev/null > +++ b/drivers/iio/adc/xilinx-ams.c > @@ -0,0 +1,1115 @@ > +/* > + * Xilinx AMS driver > + * > + * Licensed under the GPL-2 > + * > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include iio/buffer is not actually used? > +#include > + > +#include "xilinx-ams.h" > +#include the delay #include should be moved up > + > +static const unsigned int AMS_UNMASK_TIMEOUT = 500; > + > +static inline void ams_read_reg(struct ams *ams, unsigned int offset, u32 > *data) > +{ > + *data = readl(ams->base + offset); > +} > + > +static inline void ams_write_reg(struct ams *ams, unsigned int offset, u32 > data) > +{ > + writel(data, ams->base + offset); > +} > + > +static inline void ams_update_reg(struct ams *ams, unsigned int offset, > + u32 mask, u32 data) > +{ > + u32 val; > + > + ams_read_reg(ams, offset, &val); > + ams_write_reg(ams, offset, (val & ~mask) | (mask & data)); > +} > + > +static inline void ams_ps_read_reg(struct ams *ams, unsigned int offset, > + u32 *data) > +{ > + *data = readl(ams->ps_base + offset); > +} > + > +static inline void ams_ps_write_reg(struct ams *ams, unsigned int offset, > + u32 data) > +{ > + writel(data, ams->ps_base + offset); > +} > + > +static inline void ams_ps_update_reg(struct ams *ams, unsigned int offset, > +u32 mask, u32 data) > +{ > + u32 val; > + > + ams_ps_read_reg(ams, offset, &val); > + ams_ps_write_reg(ams, offset, (val & ~mask) | (data & mask)); > +} > + > +static inline void ams_apb_pl_read_reg(struct ams *ams, unsigned int offset, > + u32 *data) > +{ > + *data = readl(ams->pl_base + offset); > +} > + > +static inline void ams_apb_pl_write_reg(struct ams *ams, unsigned int offset, > + u32 data) > +{ > + writel(data, ams->pl_base + offset); > +} > + > +static inline void ams_apb_pl_update_reg(struct ams *ams, unsigned int > offset, > +u32 mask, u32 data) > +{ > + u32 val; > + > + ams_apb_pl_read_reg(ams, offset, &val); > + ams_apb_pl_write_reg(ams, offset, (val & ~mask) | (data & mask)); > +} can't we have some
Re: [PATCH 2/3] iio: core: Add double tap as possible gesture
> This adds the IIO_MOD_DOUBLE_TAP entry to the iio_modifier enum and the > corresponding "double_tap" string to the iio_modifier_names array. I don't think we should have gestures as channel modifiers probably a middle layer between IIO and input subsystem is needed (could be in userspace?) > Signed-off-by: Gwendal Grignou > Signed-off-by: Thierry Escande > --- > drivers/iio/industrialio-core.c | 1 + > include/uapi/linux/iio/types.h | 1 + > 2 files changed, 2 insertions(+) > > diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c > index fc340ed..82ce05f 100644 > --- a/drivers/iio/industrialio-core.c > +++ b/drivers/iio/industrialio-core.c > @@ -120,6 +120,7 @@ static const char * const iio_modifier_names[] = { > [IIO_MOD_Q] = "q", > [IIO_MOD_CO2] = "co2", > [IIO_MOD_VOC] = "voc", > + [IIO_MOD_DOUBLE_TAP] = "double_tap", > }; > > /* relies on pairs of these shared then separate */ > diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h > index 22e5e58..47d2768 100644 > --- a/include/uapi/linux/iio/types.h > +++ b/include/uapi/linux/iio/types.h > @@ -80,6 +80,7 @@ enum iio_modifier { > IIO_MOD_CO2, > IIO_MOD_VOC, > IIO_MOD_LIGHT_UV, > + IIO_MOD_DOUBLE_TAP, > }; > > enum iio_event_type { > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH v6 4/5] iio: light: lm3533-als: Support initialization from Device Tree
> Implement support for initialization of the lm3533 als driver from > Device Tree. minor comment below > Signed-off-by: Bjorn Andersson > Signed-off-by: Bjorn Andersson > --- > > Note that this patch can be merged independently of the other patches in the > series. > > Changes since v5: > - None > > Changes since v4: > - None > > Changes since v3: > - Moved als DT parsing from mfd driver > - Gave driver its own compatible > > drivers/iio/light/lm3533-als.c | 40 +++- > 1 file changed, 39 insertions(+), 1 deletion(-) > > diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c > index f409c2047c05..eef9f06eb0b1 100644 > --- a/drivers/iio/light/lm3533-als.c > +++ b/drivers/iio/light/lm3533-als.c > @@ -20,6 +20,7 @@ > #include > #include > #include > +#include > #include > #include > #include > @@ -831,6 +832,32 @@ static const struct iio_info lm3533_als_info = { > .read_raw = &lm3533_als_read_raw, > }; > > +static struct lm3533_als_platform_data *lm3533_als_of_parse(struct device > *dev) > +{ > + struct lm3533_als_platform_data *als_pdata; > + struct device_node *node = dev->of_node; > + int ret; > + u32 val; > + > + als_pdata = devm_kzalloc(dev, sizeof(*als_pdata), GFP_KERNEL); > + if (!als_pdata) > + return NULL; > + > + als_pdata->pwm_mode = of_property_read_bool(node, "ti,pwm-mode"); > + > + ret = of_property_read_u32(node, "ti,als-resistance-ohm", &val); > + if (ret < 0 && ret != -EINVAL) { > + dev_err(dev, "unable to read ti,als-resistance-ohm"); \n missing > + return NULL; > + } > + > + /* Leave at high-Z, if the property was omitted or specified as 0 */ > + if (!ret && val != 0) > + als_pdata->r_select = 20 / val; > + > + return als_pdata; > +} > + > static int lm3533_als_probe(struct platform_device *pdev) > { > struct lm3533 *lm3533; > @@ -843,7 +870,11 @@ static int lm3533_als_probe(struct platform_device *pdev) > if (!lm3533) > return -EINVAL; > > - pdata = pdev->dev.platform_data; > + if (pdev->dev.of_node) > + pdata = lm3533_als_of_parse(&pdev->dev); > + else > + pdata = pdev->dev.platform_data; > + > if (!pdata) { > dev_err(&pdev->dev, "no platform data\n"); > return -EINVAL; > @@ -914,9 +945,16 @@ static int lm3533_als_remove(struct platform_device > *pdev) > return 0; > } > > +static const struct of_device_id lm3533_als_of_match[] = { > + { .compatible = "ti,lm3533-als", }, > + { }, > +}; > +MODULE_DEVICE_TABLE(of, lm3533_als_of_match); > + > static struct platform_driver lm3533_als_driver = { > .driver = { > .name = "lm3533-als", > + .of_match_table = lm3533_als_of_match, > }, > .probe = lm3533_als_probe, > .remove = lm3533_als_remove, > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH v2 2/2] iio: distance: add devantech us ranger srf04
> > + indio_dev->num_channels = ARRAY_SIZE(srf04_chan_spec); > > + > > + return iio_device_register(indio_dev); > > +} > > + > > +static int srf04_remove(struct platform_device *pdev) > > +{ > > + struct iio_dev *indio_dev = platform_get_drvdata(pdev); > > + > > + iio_device_unregister(indio_dev); > With nothing else in here, you can use devm_iio_device_register > and drop the remove entirely. can devm_request_irq() and devm_iio_device_register() be used together? if both are devm_ we cant't guarantee that iio_device_unregister() comes first in _remove()? these devm_ things... > > + > > + return 0; > > +} > > + > > +static const struct of_device_id of_srf04_match[] = { > > + { .compatible = "devantech,srf04", }, > > + {}, > > +}; > > + > > +MODULE_DEVICE_TABLE(of, of_srf04_match); > > + > > +static struct platform_driver srf04_driver = { > > + .probe = srf04_probe, > > + .remove = srf04_remove, > > + .driver = { > > + .name = "srf04-gpio", > > + .of_match_table = of_srf04_match, > > + }, > > +}; > > + > > +module_platform_driver(srf04_driver); > > + > > +MODULE_AUTHOR("Andreas Klinger "); > > +MODULE_DESCRIPTION("SRF04 ultrasonic sensor for distance measuring using > > GPIOs"); > > +MODULE_LICENSE("GPL"); > > +MODULE_ALIAS("platform:srf04"); > > > > -- > To unsubscribe from this list: send the line "unsubscribe linux-iio" in > the body of a message to majord...@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH 1/2] iio: pressure: mpl3115: do not rely on structure field ordering
Hello, > Fixes a regression triggered by a change in the layout of > struct iio_chan_spec, but the real bug is in the driver which assumed > a specific structure layout in the first place. I don't think that this is a proper fix maybe the driver is unique in that it uses mask_separate for INFO_SCALE and not by_type, but since there is just one PRESSURE channel, it should be equivalent what do you mean by 'driver which assumed a specific structure'? thanks, p. > diff --git a/drivers/iio/pressure/mpl3115.c b/drivers/iio/pressure/mpl3115.c > index cc3f84139157..525644a7442d 100644 > --- a/drivers/iio/pressure/mpl3115.c > +++ b/drivers/iio/pressure/mpl3115.c > @@ -190,7 +190,7 @@ static const struct iio_chan_spec mpl3115_channels[] = { > { > .type = IIO_PRESSURE, > .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), > - BIT(IIO_CHAN_INFO_SCALE), > + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), > .scan_index = 0, > .scan_type = { > .sign = 'u', > @@ -203,7 +203,7 @@ static const struct iio_chan_spec mpl3115_channels[] = { > { > .type = IIO_TEMP, > .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), > - BIT(IIO_CHAN_INFO_SCALE), > + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), > .scan_index = 1, > .scan_type = { > .sign = 's', > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH 1/2] iio: pressure: mpl3115: do not rely on structure field ordering
> >> Fixes a regression triggered by a change in the layout of > >> struct iio_chan_spec, but the real bug is in the driver which assumed > >> a specific structure layout in the first place. > > what do you mean by 'driver which assumed a specific structure'? > > Look again, the two bits are not OR:ed together as implied by the > indentation. There is a comma between them, which put the ..._SCALE > bit in the next field. That next field was .info_mask_shared_by_type > before the patch adding the available attribute that triggered the > regression and .info_mask_separate_available after it. wow, now that you say it :) since I wrote these drivers I can assure you that this is just a typo, use of the 'specific structure' is a coincident maybe adding your explanation regarding not ORing to the patch description would be a good idea I was confused by the change from .info_mask_separate to .info_mask_shared_by_type a fix could have just changed - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), BIT(IIO_CHAN_INFO_SCALE), + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), as originally intended > Cheers, > peda > > > thanks, p. > > > >> diff --git a/drivers/iio/pressure/mpl3115.c > >> b/drivers/iio/pressure/mpl3115.c > >> index cc3f84139157..525644a7442d 100644 > >> --- a/drivers/iio/pressure/mpl3115.c > >> +++ b/drivers/iio/pressure/mpl3115.c > >> @@ -190,7 +190,7 @@ static const struct iio_chan_spec mpl3115_channels[] = > >> { > >>{ > >>.type = IIO_PRESSURE, > >>.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), > >> - BIT(IIO_CHAN_INFO_SCALE), > >> + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), > >>.scan_index = 0, > >>.scan_type = { > >>.sign = 'u', > >> @@ -203,7 +203,7 @@ static const struct iio_chan_spec mpl3115_channels[] = > >> { > >>{ > >>.type = IIO_TEMP, > >>.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), > >> - BIT(IIO_CHAN_INFO_SCALE), > >> + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), > >>.scan_index = 1, > >>.scan_type = { > >>.sign = 's', > >> > > > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH 1/2] iio: pressure: mpl3115: do not rely on structure field ordering
> > - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), > > BIT(IIO_CHAN_INFO_SCALE), > > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | > > BIT(IIO_CHAN_INFO_SCALE), > > as originally intended > > I considered that option, but the code in mpl3115_read_raw (and > mpl115_read_raw for that matter) return constants fro these values which > to me indicated that they were not "separate" and as that would also be > the change which replicated the exact behavior from before the regression > I went with that. But I don't care either way, so I can re-spin if you > want me to? (But don't blame me if that regresses in some other > interesting way). no, all good; shared_by_type is the way to go I'd rather respin for the not ORed comment in the patch > >> Cheers, > >> peda > >> > >>> thanks, p. > >>> > >>>> diff --git a/drivers/iio/pressure/mpl3115.c > >>>> b/drivers/iio/pressure/mpl3115.c > >>>> index cc3f84139157..525644a7442d 100644 > >>>> --- a/drivers/iio/pressure/mpl3115.c > >>>> +++ b/drivers/iio/pressure/mpl3115.c > >>>> @@ -190,7 +190,7 @@ static const struct iio_chan_spec mpl3115_channels[] > >>>> = { > >>>> { > >>>> .type = IIO_PRESSURE, > >>>> .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), > >>>> -BIT(IIO_CHAN_INFO_SCALE), > >>>> +.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), > >>>> .scan_index = 0, > >>>> .scan_type = { > >>>> .sign = 'u', > >>>> @@ -203,7 +203,7 @@ static const struct iio_chan_spec mpl3115_channels[] > >>>> = { > >>>> { > >>>> .type = IIO_TEMP, > >>>> .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), > >>>> -BIT(IIO_CHAN_INFO_SCALE), > >>>> +.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), > >>>> .scan_index = 1, > >>>> .scan_type = { > >>>> .sign = 's', > >>>> > >>> > >> > > > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH 2/2] iio: distance: srf08: add IIO driver for us ranger
+ return ret; > + > + regval = val / 43 - 1; > + mod = val % 43; > + > + if (mod || (regval < 0) || (regval > 255)) > + return -EINVAL; > + > + mutex_lock(&data->lock); > + > + ret = i2c_smbus_write_byte_data(data->client, > + SRF08_WRITE_RANGE, regval); > + if (ret < 0) { > + dev_err(&client->dev, "write_range - err: %d\n", ret); > + mutex_unlock(&data->lock); > + return ret; > + } > + > + data->range_mm = val; > + > + mutex_unlock(&data->lock); > + > + return len; > +} > + > +static IIO_DEVICE_ATTR(range_mm, S_IRUGO | S_IWUSR, > + srf08_show_range_mm, srf08_write_range_mm, 0); > + > +static ssize_t srf08_show_gain_available(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + int i, len = 0; > + > + for (i = 0; i < ARRAY_SIZE(srf08_gain); i++) > + len += sprintf(buf + len, "%d ", srf08_gain[i]); > + > + len += sprintf(buf + len, "\n"); > + > + return len; > +} > + > +static IIO_DEVICE_ATTR(gain_available, S_IRUGO, > + srf08_show_gain_available, NULL, 0); > + > +static ssize_t srf08_show_gain(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct iio_dev *indio_dev = dev_to_iio_dev(dev); > + struct srf08_data *data = iio_priv(indio_dev); > + int len; > + > + len = sprintf(buf, "%d\n", data->gain); > + > + return len; > +} > + > +static ssize_t srf08_write_gain(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t len) > +{ > + struct iio_dev *indio_dev = dev_to_iio_dev(dev); > + struct srf08_data *data = iio_priv(indio_dev); > + struct i2c_client *client = data->client; > + int ret, val, i; > + int regval = -1; u8, no init > + > + ret = kstrtoint(buf, 10, &val); kstrtouint() > + if (ret) > + return ret; > + > + for (i = 0; i < ARRAY_SIZE(srf08_gain); i++) > + if (val == srf08_gain[i]) > + regval = i; > + maybe check i >= ARRAY_SIZE instead of abusing regval > + if (regval == -1) > + return -EINVAL; > + > + mutex_lock(&data->lock); > + > + ret = i2c_smbus_write_byte_data(data->client, > + SRF08_WRITE_MAX_GAIN, regval); > + if (ret < 0) { > + dev_err(&client->dev, "write_gain - err: %d\n", ret); > + mutex_unlock(&data->lock); > + return ret; > + } > + > + data->gain = val; > + > + mutex_unlock(&data->lock); > + > + return len; > +} > + > +static IIO_DEVICE_ATTR(gain, S_IRUGO | S_IWUSR, > + srf08_show_gain, srf08_write_gain, 0); > + > +static struct attribute *srf08_attributes[] = { > + &iio_dev_attr_range_mm.dev_attr.attr, > + &iio_dev_attr_range_mm_available.dev_attr.attr, > + &iio_dev_attr_gain.dev_attr.attr, > + &iio_dev_attr_gain_available.dev_attr.attr, > + NULL, > +}; > + > +static const struct attribute_group srf08_attribute_group = { > + .attrs = srf08_attributes, > +}; > + > +static const struct iio_chan_spec srf08_channels[] = { > + { > + .type = IIO_DISTANCE, > + .info_mask_separate = > + BIT(IIO_CHAN_INFO_RAW) | > + BIT(IIO_CHAN_INFO_SCALE), > + }, > +}; > + > +static const struct iio_info srf08_info = { > + .read_raw = srf08_read_raw, > + .attrs = &srf08_attribute_group, > + .driver_module = THIS_MODULE, > +}; > + > +static int srf08_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + struct iio_dev *indio_dev; > + struct srf08_data *data; > + > + if (!i2c_check_functionality(client->adapter, > + I2C_FUNC_SMBUS_READ_BYTE_DATA | > + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) WORD_DATA if using word_swapped() above > + return -ENODEV; > + > + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); > + if (!indio_dev) > + return -ENOMEM; > + > + data = iio_priv(indio_dev); > + i2c_set_clientdata(client, indio_dev); > + data->client = client; > + > + /* set default values of device */ > + data->gain = 1025; use some defines, where do these magics come from? > + data->range_mm = 11008; > + > + indio_dev->name = dev_name(&client->dev); > + indio_dev->dev.parent = &client->dev; > + indio_dev->modes = INDIO_DIRECT_MODE; > + indio_dev->info = &srf08_info; > + indio_dev->channels = srf08_channels; > + indio_dev->num_channels = ARRAY_SIZE(srf08_channels); > + > + mutex_init(&data->lock); > + > + return devm_iio_device_register(&client->dev, indio_dev); > +} > + > +static const struct i2c_device_id srf08_id[] = { > + { "srf08", 0 }, > + { } > +}; > +MODULE_DEVICE_TABLE(i2c, srf08_id); > + > +static struct i2c_driver srf08_driver = { > + .driver = { > + .name = "srf08", > + }, > + .probe = srf08_probe, > + .id_table = srf08_id, > +}; > +module_i2c_driver(srf08_driver); > + > +MODULE_AUTHOR("Andreas Klinger "); > +MODULE_DESCRIPTION("Devantech SRF08 ultrasonic ranger driver"); > +MODULE_LICENSE("GPL"); > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH] iio: magnetometer: mag3110: claim direct mode during raw writes
> Driver was checking for direct mode but not locking it. Use > claim/release helper functions to guarantee the device stays > in direct mode during raw writes. looks good, Acked-by: Peter Meerwald-Stadler > Signed-off-by: Alison Schofield > --- > drivers/iio/magnetometer/mag3110.c | 30 -- > 1 file changed, 20 insertions(+), 10 deletions(-) > > diff --git a/drivers/iio/magnetometer/mag3110.c > b/drivers/iio/magnetometer/mag3110.c > index f2b3bd7..b4f643f 100644 > --- a/drivers/iio/magnetometer/mag3110.c > +++ b/drivers/iio/magnetometer/mag3110.c > @@ -222,29 +222,39 @@ static int mag3110_write_raw(struct iio_dev *indio_dev, >int val, int val2, long mask) > { > struct mag3110_data *data = iio_priv(indio_dev); > - int rate; > + int rate, ret; > > - if (iio_buffer_enabled(indio_dev)) > - return -EBUSY; > + ret = iio_device_claim_direct_mode(indio_dev); > + if (ret) > + return ret; > > switch (mask) { > case IIO_CHAN_INFO_SAMP_FREQ: > rate = mag3110_get_samp_freq_index(data, val, val2); > - if (rate < 0) > - return -EINVAL; > + if (rate < 0) { > + ret = -EINVAL; > + break; > + } > > data->ctrl_reg1 &= ~MAG3110_CTRL_DR_MASK; > data->ctrl_reg1 |= rate << MAG3110_CTRL_DR_SHIFT; > - return i2c_smbus_write_byte_data(data->client, > + ret = i2c_smbus_write_byte_data(data->client, > MAG3110_CTRL_REG1, data->ctrl_reg1); > + break; > case IIO_CHAN_INFO_CALIBBIAS: > - if (val < -1 || val > 1) > - return -EINVAL; > - return i2c_smbus_write_word_swapped(data->client, > + if (val < -1 || val > 1) { > + ret = -EINVAL; > + break; > + } > + ret = i2c_smbus_write_word_swapped(data->client, > MAG3110_OFF_X + 2 * chan->scan_index, val << 1); > + break; > default: > - return -EINVAL; > + ret = -EINVAL; > + break; > } > + iio_device_release_direct_mode(indio_dev); > + return ret; > } > > static irqreturn_t mag3110_trigger_handler(int irq, void *p) > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH v8 09/12] dt-bindings: mux-adg792a: document devicetree bindings for ADG792A/G mux
On Fri, 27 Jan 2017, Peter Rosin wrote: comments below, picking on typos > On 2017-01-27 20:50, Rob Herring wrote: > > On Wed, Jan 18, 2017 at 04:57:12PM +0100, Peter Rosin wrote: > >> Analog Devices ADG792A/G is a triple 4:1 mux. > >> > >> Acked-by: Jonathan Cameron > >> Signed-off-by: Peter Rosin > >> --- > >> .../devicetree/bindings/mux/mux-adg792a.txt| 79 > >> ++ > >> 1 file changed, 79 insertions(+) > >> create mode 100644 Documentation/devicetree/bindings/mux/mux-adg792a.txt > >> > >> diff --git a/Documentation/devicetree/bindings/mux/mux-adg792a.txt > >> b/Documentation/devicetree/bindings/mux/mux-adg792a.txt > >> new file mode 100644 > >> index ..0b26dd11f070 > >> --- /dev/null > >> +++ b/Documentation/devicetree/bindings/mux/mux-adg792a.txt > >> @@ -0,0 +1,79 @@ > >> +Bindings for Analog Devices ADG792A/G Triple 4:1 Multiplexers > >> + > >> +Required properties: > >> +- compatible : "adi,adg792a" or "adi,adg792g" > >> +- #mux-control-cells : <0> if parallel, or <1> if not. > >> +* Standard mux-controller bindings as decribed in mux-controller.txt described > >> + > >> +Optional properties for ADG792G: > >> +- gpio-controller : if present, #gpio-cells below is required. > >> +- #gpio-cells : should be <2> > >> +- First cell is the GPO line number, i.e. 0 or 1 > >> +- Second cell is used to specify active high (0) > >> + or active low (1) > >> + > >> +Optional properties: > >> +- adi,parallel : if present, the three muxes are bound together with a > >> single > >> + mux controller, controlling all three muxes in parallel. > > > > Can't this be implied by #mux-control-cells == 0? > > Right, good point! I'll drop adi,parallel. > > >> +- adi,idle-state : if present, array of 2-tuples with mux controller > >> number > >> + and state that mux controllers will have when idle. States 0 through 3 > >> + correspond to signals A through D in the datasheet. > >> +- adi,idle-high-impedance : if present, array of mux controller numbers > >> that > >> + should be in the disconnected high-impedance state when idle. > > > > Perhaps combine these 2 to a common idle-state with the index being the > > mux controller number and a value of -1 (or 4) being hi-Z. > > At one point [1] I had a straight array for the mux controllers with state 4 > being hi-Z and state 5 being the default "leave mux as-is on release", but > Jonathan didn't like magic numbers. Can you please read the follow-ups to the > referenced mail for some background, and then pick out the most suitable color > for me to paint with? :-) > > [1] https://lkml.org/lkml/2016/11/30/110 > > >> + > >> +Mux controller states 0 through 3 correspond to signals A through D in the > >> +datasheet. If a mux controller is mentioned in neither adi,idle-state nor that is already being said under 'adi,idle-state' > >> +adi,idle-high-impedance it is left in its previously selected state when > >> idle. > >> + > >> +Example: > >> + > >> + /* > >> + * Three independent mux controllers (of which one is used). > >> + * Mux 0 is disconnected when idle, mux 1 idles with signal C > >> + * and mux 2 idles with signal A. > >> + */ > >> + &i2c0 { > >> + mux: adg792a@50 { > > > > mux-controller@50 > > Yep. Hmm, where have I seen that before? :-) > > Cheers, > peda > > >> + compatible = "adi,adg792a"; > >> + reg = <0x50>; > >> + #mux-control-cells = <1>; > >> + > >> + adi,idle-high-impedance = <0>; > >> + adi,idle-state = <1 2>, <2 0>; > >> + }; > >> + }; > >> + > >> + adc-mux { > >> + compatible = "io-channel-mux"; > >> + io-channels = <&adc 0>; > >> + io-channel-names = "parent"; > >> + > >> + mux-controls = <&mux 1>; > >> + > >> + channels = "sync-1", "", "out"; > >> + }; > >> + > >> + > >> + /* > >> + *
Re: [PATCH 2/2] iio: distance: add devantech us ranger srf04
if (IS_ERR(data->gpiod_trig)) { > + dev_err(dev, "failed to get trig-gpiod: err=%ld\n", > + PTR_ERR(data->gpiod_trig)); > + return PTR_ERR(data->gpiod_trig); > + } > + > + data->gpiod_echo = devm_gpiod_get(dev, "echo", GPIOD_IN); > + if (IS_ERR(data->gpiod_echo)) { > + dev_err(dev, "failed to get echo-gpiod: err=%ld\n", > + PTR_ERR(data->gpiod_echo)); > + return PTR_ERR(data->gpiod_echo); > + } > + > + if (gpiod_cansleep(data->gpiod_echo)) { > + dev_err(data->dev, "cansleep-GPIOs not supported\n"); > + return -ENODEV; > + } > + > + data->irqnr = gpiod_to_irq(data->gpiod_echo); > + if (data->irqnr < 0) { > + dev_err(data->dev, "gpiod_to_irq: %d\n", ret); ret? should be data->irqnr? > + return ret; > + } > + > + ret = request_irq(data->irqnr, srf04_handle_irq, could use devm_ > + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, > + pdev->name, indio_dev); > + if (ret < 0) { > + dev_err(data->dev, "request_irq: %d\n", ret); > + return ret; > + } > + > + platform_set_drvdata(pdev, indio_dev); > + > + indio_dev->name = "srf04"; > + indio_dev->dev.parent = &pdev->dev; > + indio_dev->info = &srf04_iio_info; > + indio_dev->modes = INDIO_DIRECT_MODE; > + indio_dev->channels = srf04_chan_spec; > + indio_dev->num_channels = ARRAY_SIZE(srf04_chan_spec); > + > + return devm_iio_device_register(dev, indio_dev); you can't use devm_iio_device_register() if you need/have a _remove() > +} > + > +static int srf04_remove(struct platform_device *pdev) > +{ struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct srf04_data *data = iio_priv(indio_dev); // (but not needed anymore) > + struct srf04_data *data = NULL; > + struct iio_dev *indio_dev; > + > + indio_dev = platform_get_drvdata(pdev); > + data = iio_priv(indio_dev); > + > + free_irq(data->irqnr, indio_dev); I suggest to use in _probe() devm_request_irq() iio_device_register() and in _remove() just iio_device_unregister() > + > + return 0; > +} > + > +static const struct of_device_id of_srf04_match[] = { > + { .compatible = "devantech,srf04", }, > + {}, > +}; > + > +MODULE_DEVICE_TABLE(of, of_srf04_match); > + > +static struct platform_driver srf04_driver = { > + .probe = srf04_probe, > + .remove = srf04_remove, > + .driver = { > + .name = "srf04-gpio", > + .of_match_table = of_srf04_match, > + }, > +}; > + > +module_platform_driver(srf04_driver); > + > +MODULE_AUTHOR("Andreas Klinger "); > +MODULE_DESCRIPTION("SRF04 ultrasonic sensor for distance measuring using > GPIOs"); > +MODULE_LICENSE("GPL"); > +MODULE_ALIAS("platform:srf04"); > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH 1/2] iio: distance: add dt binding for devantech-srf04
On Sun, 29 Jan 2017, Andreas Klinger wrote: > This patch adds dt binding for devantech ultrasonic ranger srf04. comments below > The vendor "devantech" was already added to the vendor list with > "[PATCH v4 1/3] iio: distance: srf08: add trivial DT binding" > > Signed-off-by: Andreas Klinger > --- > .../bindings/iio/proximity/devantech-srf04.txt | 17 > + > 1 file changed, 17 insertions(+) > create mode 100644 > Documentation/devicetree/bindings/iio/proximity/devantech-srf04.txt > > diff --git > a/Documentation/devicetree/bindings/iio/proximity/devantech-srf04.txt > b/Documentation/devicetree/bindings/iio/proximity/devantech-srf04.txt > new file mode 100644 > index ..4ea29ef3ff89 > --- /dev/null > +++ b/Documentation/devicetree/bindings/iio/proximity/devantech-srf04.txt > @@ -0,0 +1,17 @@ > +* Devantech SRF04 ultrasonic range finder > + Bit-banging driver > + > +Required properties: > + - compatible: Should be "devantech,srf04" > + - trig-gpios: Definition of the GPIO for the triggering input > + - echo-gpios: Definition of the GPIO for the echo "for the echo" maybe some more explanation? it sounds somewhat incomplete to me in the driver you have "failed to get echo-gpiod", maybe it should be gpios there? > + needs to be an interrupt input > + See Documentation/devicetree/bindings/gpio/gpio.txt > + > +Example: > +srf04@0 { > + compatible = "devantech,srf04"; > + trig-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>; > + echo-gpios = <&gpio2 6 GPIO_ACTIVE_HIGH>; > +}; > + > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH 04/10] iio: cros_ec: Add common functions for cros_ec sensors.
s_ec_motion_send_host_cmd(st, 0); > + mutex_unlock(&st->cmd_lock); > + } > + return 0; > +} > + > +static void __maybe_unused cros_ec_sensors_complete(struct device *dev) > +{ > + struct platform_device *pdev = to_platform_device(dev); > + struct iio_dev *indio_dev = platform_get_drvdata(pdev); > + struct cros_ec_sensors_core_state *st = iio_priv(indio_dev); > + > + if (st->curr_sampl_freq == 0) > + return; > + > + if (st->curr_sampl_freq < CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY) { > + mutex_lock(&st->cmd_lock); > + st->param.cmd = MOTIONSENSE_CMD_EC_RATE; > + st->param.ec_rate.data = st->curr_sampl_freq; > + cros_ec_motion_send_host_cmd(st, 0); > + mutex_unlock(&st->cmd_lock); > + } > +} > + > +#ifdef CONFIG_PM_SLEEP > +const struct dev_pm_ops cros_ec_sensors_pm_ops = { > + .prepare = cros_ec_sensors_prepare, > + .complete = cros_ec_sensors_complete > +}; > +#else > +const struct dev_pm_ops cros_ec_sensors_pm_ops = { }; > +#endif > +EXPORT_SYMBOL_GPL(cros_ec_sensors_pm_ops); > + > +MODULE_DESCRIPTION("ChromeOS EC sensor hub core functions"); > +MODULE_LICENSE("GPL v2"); > diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h > b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h > new file mode 100644 > index 000..53e09e1 > --- /dev/null > +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h > @@ -0,0 +1,155 @@ > +/* > + * ChromeOS EC sensor hub > + * > + * Copyright (C) 2015 Google, Inc > + * > + * This software is licensed under the terms of the GNU General Public > + * License version 2, as published by the Free Software Foundation, and > + * may be copied, distributed, and modified under those terms. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + */ > + > +#ifndef __CROS_EC_SENSORS_CORE_H > +#define __CROS_EC_SENSORS_CORE_H > + > +#include > + > +enum { > + X, such short #defines scare me :) > + Y, > + Z, > + MAX_AXIS, > +}; > + > +/* > + * EC returns sensor values using signed 16 bit registers > + */ > +#define CROS_EC_SENSOR_BITS 16 > + > +/* > + * 4 16 bit channels are allowed. > + * Good enough for current sensors, thye use up to 3 16 bit vectors. they > + */ > +#define CROS_EC_SAMPLE_SIZE (sizeof(s64) * 2) > + > +/* > + * minimum sampling period to use when device is suspending. Minimum (uppercase) > + */ > +#define CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY 1000 /* 1 second */ > + > +/* > + * Function to read the sensor data. > + * > + * Data can be retrieve using the cros ec command protocol. > + * Some machines also allow accessing some sensor data via > + * IO space. > + */ > +typedef int (cros_ec_sensors_read_t)(struct iio_dev *indio_dev, > + unsigned long scan_mask, s16 *data); > + > +cros_ec_sensors_read_t cros_ec_sensors_read_lpc; > +cros_ec_sensors_read_t cros_ec_sensors_read_cmd; > + > +/* State data for ec_sensors iio driver. */ > +struct cros_ec_sensors_core_state { > + struct cros_ec_device *ec; > + /* > + * Location to store command and response to the EC. > + */ > + struct mutex cmd_lock; > + > + /* > + * Statically allocated command structure that holds parameters > + * and response. > + */ > + struct cros_ec_command *msg; > + struct ec_params_motion_sense param; > + struct ec_response_motion_sense *resp; > + > + /* Type of sensor */ > + enum motionsensor_type type; > + enum motionsensor_location loc; > + > + /* > + * Calibration parameters. Note that trigger captured data will always > + * provide the calibrated values. > + */ > + struct calib_data { > + s16 offset; > + } calib[MAX_AXIS]; > + > + /* > + * Static array to hold data from a single capture. For each > + * channel we need 2 bytes, except for the timestamp. The timestamp > + * is always last and is always 8-byte aligned. > + */ > + u8 samples[CROS_EC_SAMPLE_SIZE]; > + > + /* Pointer to function used for accessing sensors values. */ > + cros_ec_sensors_read_t *read_ec_sensors_data; > + > + /* Current sampling period */ > + int curr_sampl_freq; > +}; > + > +/* Basic initialization of the core structure. */ > +int cros_ec_sensors_core_init(struct platform_device *pdev, > + struct iio_dev *indio_dev, > + bool physical_device); > + > +/* > + * cros_ec_sensors_capture - the trigger handler function > + * > + * @irq: the interrupt number > + * @p: private data - always a pointer to the poll func. > + * > + * On a trigger event occurring, if the pollfunc is attached then this > + * handler is called as a threaded interrupt (and hence may sleep). It > + * is responsible for grabbing data from the device and pushing it into > + * the associated buffer. > + */ > +irqreturn_t cros_ec_sensors_capture(int irq, void *p); > + > + > +/* > + * cros_ec_motion_send_host_cmd - send motion sense host command > + * > + * @st Pointer to state information for device. > + * @opt_length: optional length: to reduce the response size, > + *useful on the data path. > + *Otherwise, the maximal allowed response size is used. > + * @return 0 if ok, -ve on error. > + * > + * Note, when called, the sub-command is assumed to be set in param->cmd. > + */ > +int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *st, > + u16 opt_length); > + > +/* > + * cros_ec_sensors_core_read/write: handler for core attributes. > + * > + * Handler for attributes identical among sensors: > + * - frequency, > + * - sampling_frequency. > + * > + * cmd_lock lock must be held. > + */ > +int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st, > + struct iio_chan_spec const *chan, > + int *val, int *val2, long mask); > + > +int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st, > +struct iio_chan_spec const *chan, > +int val, int val2, long mask); > + > +extern const struct dev_pm_ops cros_ec_sensors_pm_ops; > + > +/* List of extended channel specification for all sensors */ > +extern const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[]; > +extern const struct iio_chan_spec_ext_info cros_ec_sensors_limited_info[]; > + > +#endif /* __CROS_EC_SENSORS_CORE_H */ > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH 1/1] iio: vcnl4000: Add IR current adjust support
ret = vcnl4000_write_led_current_raw(data, val); > + mutex_unlock(&data->lock); > + return ret; > + } > + return -EINVAL; > +} > + > static const struct iio_info vcnl4000_info = { > .read_raw = vcnl4000_read_raw, > + .write_raw = vcnl4000_write_raw, > .driver_module = THIS_MODULE, > }; > > +static bool vcnl4000_readable_reg(struct device *dev, unsigned int reg) > +{ > + switch (reg) { > + case VCNL4000_COMMAND: > + case VCNL4000_PROD_REV: > + case VCNL4000_LED_CURRENT: > + case VCNL4000_AL_PARAM: > + case VCNL4000_AL_RESULT_HI: > + case VCNL4000_AL_RESULT_LO: > + case VCNL4000_PS_RESULT_HI: > + case VCNL4000_PS_RESULT_LO: > + case VCNL4000_PS_MEAS_FREQ: > + case VCNL4000_PS_MOD_ADJ: > + return true; > + default: > + return false; > + } > +} > + > +static bool vcnl4000_writeable_reg(struct device *dev, unsigned int reg) > +{ > + switch (reg) { > + case VCNL4000_COMMAND: > + case VCNL4000_LED_CURRENT: > + case VCNL4000_AL_PARAM: > + case VCNL4000_PS_MEAS_FREQ: > + case VCNL4000_PS_MOD_ADJ: > + return true; > + default: > + return false; > + } > +} > + > +static const struct regmap_config vcnl4000_regmap_config = { > + .reg_bits = 8, > + .val_bits = 8, > + .max_register = VCNL4000_PS_MOD_ADJ, > + .readable_reg = vcnl4000_readable_reg, > + .writeable_reg = vcnl4000_writeable_reg, > +}; > + > static int vcnl4000_probe(struct i2c_client *client, > const struct i2c_device_id *id) > { > @@ -162,7 +269,13 @@ static int vcnl4000_probe(struct i2c_client *client, > return -ENOMEM; > > data = iio_priv(indio_dev); > + data->regmap = devm_regmap_init_i2c(client, &vcnl4000_regmap_config); > + if (IS_ERR(data->regmap)) { > + dev_err(&client->dev, "regmap_init failed!\n"); > + return PTR_ERR(data->regmap); > + } > i2c_set_clientdata(client, indio_dev); > + mutex_init(&data->lock); > data->client = client; > > ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV); > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH 2/2] iio: stx104: Move the STX104 IIO driver to the ADC directory
/* Only four gain states (x1, x2, x4, x8) */ > - switch (val) { > - case 1: > - outb(0, priv->base + 11); > - break; > - case 2: > - outb(1, priv->base + 11); > - break; > - case 4: > - outb(2, priv->base + 11); > - break; > - case 8: > - outb(3, priv->base + 11); > - break; > - default: > - return -EINVAL; > - } > - > - break; > - default: > - return -EINVAL; > - } > - > - return 0; > -} > - > -static const struct iio_info stx104_info = { > - .driver_module = THIS_MODULE, > - .read_raw = stx104_read_raw, > - .write_raw = stx104_write_raw > -}; > - > -static struct iio_chan_spec stx104_channels[STX104_MAX_NUM_CHAN] = { > - STX104_OUT_CHAN(0), STX104_OUT_CHAN(1), > - STX104_GAIN_CHAN, > - STX104_IN_CHAN(0), STX104_IN_CHAN(1), STX104_IN_CHAN(2), > - STX104_IN_CHAN(3), STX104_IN_CHAN(4), STX104_IN_CHAN(5), > - STX104_IN_CHAN(6), STX104_IN_CHAN(7), STX104_IN_CHAN(8), > - STX104_IN_CHAN(9), STX104_IN_CHAN(10), STX104_IN_CHAN(11), > - STX104_IN_CHAN(12), STX104_IN_CHAN(13), STX104_IN_CHAN(14), > - STX104_IN_CHAN(15) > -}; > - > -static int stx104_gpio_get_direction(struct gpio_chip *chip, > - unsigned int offset) > -{ > - if (offset < 4) > - return 1; > - > - return 0; > -} > - > -static int stx104_gpio_direction_input(struct gpio_chip *chip, > - unsigned int offset) > -{ > - if (offset >= 4) > - return -EINVAL; > - > - return 0; > -} > - > -static int stx104_gpio_direction_output(struct gpio_chip *chip, > - unsigned int offset, int value) > -{ > - if (offset < 4) > - return -EINVAL; > - > - chip->set(chip, offset, value); > - return 0; > -} > - > -static int stx104_gpio_get(struct gpio_chip *chip, unsigned int offset) > -{ > - struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip); > - > - if (offset >= 4) > - return -EINVAL; > - > - return !!(inb(stx104gpio->base) & BIT(offset)); > -} > - > -static void stx104_gpio_set(struct gpio_chip *chip, unsigned int offset, > - int value) > -{ > - struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip); > - const unsigned int mask = BIT(offset) >> 4; > - unsigned long flags; > - > - if (offset < 4) > - return; > - > - spin_lock_irqsave(&stx104gpio->lock, flags); > - > - if (value) > - stx104gpio->out_state |= mask; > - else > - stx104gpio->out_state &= ~mask; > - > - outb(stx104gpio->out_state, stx104gpio->base); > - > - spin_unlock_irqrestore(&stx104gpio->lock, flags); > -} > - > -static int stx104_probe(struct device *dev, unsigned int id) > -{ > - struct iio_dev *indio_dev; > - struct stx104_iio *priv; > - struct stx104_gpio *stx104gpio; > - int i; > - int err; > - > - indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); > - if (!indio_dev) > - return -ENOMEM; > - > - stx104gpio = devm_kzalloc(dev, sizeof(*stx104gpio), GFP_KERNEL); > - if (!stx104gpio) > - return -ENOMEM; > - > - if (!devm_request_region(dev, base[id], STX104_EXTENT, > - dev_name(dev))) { > - dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n", > - base[id], base[id] + STX104_EXTENT); > - return -EBUSY; > - } > - > - indio_dev->info = &stx104_info; > - indio_dev->modes = INDIO_DIRECT_MODE; > - indio_dev->num_channels = IN_CHAN_OFFSET + STX104_NUM_IN_CHAN; > - > - /* determine if differential inputs */ > - if (inb(base[id] + 8) & BIT(5)) { > - indio_dev->num_channels -= STX104_NUM_IN_CHAN / 2; > - > - for (i = 0; i < STX104_NUM_IN_CHAN / 2; i++) { > - stx104_channels[i + IN_CHAN_OFFSET].differential = 1; > - stx104_channels[i + IN_CHAN_OFFSET].channel2 = i; > - } > - } > - > - indio_dev->channels = stx104_channels; > - indio_dev->name = dev_name(dev); > - > - priv = iio_priv(indio_dev); > - priv->base = base[id]; > - > - /* configure device for software trigger operation */ > - outb(0, base[id] + 9); > - > - /* initialize gain setting to x1 */ > - outb(0, base[id] + 11); > - > - /* initialize DAC output to 0V */ > - outw(0, base[id] + 4); > - outw(0, base[id] + 6); > - > - err = devm_iio_device_register(dev, indio_dev); > - if (err) { > - dev_err(dev, "IIO device registering failed (%d)\n", err); > - return err; > - } > - > - stx104gpio->chip.label = dev_name(dev); > - stx104gpio->chip.parent = dev; > - stx104gpio->chip.owner = THIS_MODULE; > - stx104gpio->chip.base = -1; > - stx104gpio->chip.ngpio = 8; > - stx104gpio->chip.get_direction = stx104_gpio_get_direction; > - stx104gpio->chip.direction_input = stx104_gpio_direction_input; > - stx104gpio->chip.direction_output = stx104_gpio_direction_output; > - stx104gpio->chip.get = stx104_gpio_get; > - stx104gpio->chip.set = stx104_gpio_set; > - stx104gpio->base = base[id] + 3; > - stx104gpio->out_state = 0x0; > - > - spin_lock_init(&stx104gpio->lock); > - > - dev_set_drvdata(dev, stx104gpio); > - > - err = gpiochip_add_data(&stx104gpio->chip, stx104gpio); > - if (err) { > - dev_err(dev, "GPIO registering failed (%d)\n", err); > - return err; > - } > - > - return 0; > -} > - > -static int stx104_remove(struct device *dev, unsigned int id) > -{ > - struct stx104_gpio *const stx104gpio = dev_get_drvdata(dev); > - > - gpiochip_remove(&stx104gpio->chip); > - > - return 0; > -} > - > -static struct isa_driver stx104_driver = { > - .probe = stx104_probe, > - .driver = { > - .name = "stx104" > - }, > - .remove = stx104_remove > -}; > - > -module_isa_driver(stx104_driver, num_stx104); > - > -MODULE_AUTHOR("William Breathitt Gray "); > -MODULE_DESCRIPTION("Apex Embedded Systems STX104 IIO driver"); > -MODULE_LICENSE("GPL v2"); > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH 1/1] iio: vcnl4000: Add IR current adjust support
> > comments below; nice addition > > > > it seems this patch clashes with the recent changes to this driver in > > iio-testing; can you rebase on top please? > > Where is iio-testing? I couldn't found it in linux. https://git.kernel.org/cgit/linux/kernel/git/jic23/iio.git/log/?h=testing p. -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH 2/2] iio: accel: Add support for Domintech DMARD06 accelerometer
+u8 buf; > > >> +bool reversed; > > >> +int error; > > >> +int tmp; > > >> + > > >> +switch (mask) { > > >> +case IIO_CHAN_INFO_RAW: > > >> +error = dmard06_i2c_read(dmard06->client, > > >> chan->address, &buf); > > >> +if (error) { > > >> +dev_err(&dmard06->client->dev, > > >> +"Failed to read data %d\n", error); > > >> +return error; > > >> +} > > >> + > > >> +reversed = (buf >> 7) & 1; > > >> +tmp = (int)((buf & 0x7f) >> 1); > > > > > > > > >> + > > >> +switch (chan->type) { > > >> +case IIO_ACCEL: > > > > > > wouldn't sign_extend32(buf >> 1, 6) do the job? > > > > > >> +*val = reversed ? (tmp - DMARD06_AXIS_MAX_VAL) > > >> : tmp; > > >> +return IIO_VAL_INT; > > >> +case IIO_TEMP: > > >> +*val = reversed ? -tmp : tmp; > > >> +return IIO_VAL_INT; > > >> +default: > > >> +return -EINVAL; > > >> +} > > >> +case IIO_CHAN_INFO_OFFSET: > > >> +switch (chan->type) { > > >> +case IIO_TEMP: > > >> +*val = DMARD06_TEMP_CENTER_VAL; > > >> +return IIO_VAL_INT; > > >> +default: > > >> +return -EINVAL; > > >> +} > > >> +case IIO_CHAN_INFO_SCALE: > > >> +switch (chan->type) { > > >> +case IIO_ACCEL: > > >> +*val = 0; > > >> +*val2 = DMARD06_AXIS_SCALE_VAL; > > >> +return IIO_VAL_INT_PLUS_MICRO; > > >> +default: > > >> +return -EINVAL; > > >> +} > > >> +default: > > >> +return -EINVAL; > > >> +} > > >> +} > > >> + > > >> +static const struct iio_info dmard06_info = { > > >> +.driver_module = THIS_MODULE, > > >> +.read_raw = dmard06_read_raw, > > >> +}; > > >> + > > >> +#define DMARD06_ACCEL_CHANNEL(_axis, reg) { \ > > > > > > maybe rename reg to _reg > > > > > >> +.type = IIO_ACCEL, \ > > >> +.address = reg, \ > > >> +.channel2 = IIO_MOD_##_axis,\ > > >> +.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ > > >> +.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ > > >> +.modified = 1, \ > > >> +} > > >> + > > >> +#define DMARD06_TEMP_CHANNEL(reg) { \ > > >> +.type = IIO_TEMP, \ > > >> +.address = reg, \ > > >> +.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ > > >> + BIT(IIO_CHAN_INFO_OFFSET),\ > > >> +} > > >> + > > >> +static const struct iio_chan_spec dmard06_channels[] = { > > >> +DMARD06_ACCEL_CHANNEL(X, DMARD06_XOUT_REG), > > >> +DMARD06_ACCEL_CHANNEL(Y, DMARD06_YOUT_REG), > > >> +DMARD06_ACCEL_CHANNEL(Z, DMARD06_ZOUT_REG), > > >> +DMARD06_TEMP_CHANNEL(DMARD06_TOUT_REG), > > >> +}; > > >> + > > >> +static int dmard06_probe(struct i2c_client *client, > > >> +const struct i2c_device_id *id) > > >> +{ > > >> +int error; > > >> +struct iio_dev *indio_dev; > > >> +struct dmard06_data *dmard06; > > >> + > > >> +dev_dbg(&client->dev, "I2C Address: 0x%02x\n", client->addr); > > > > > > not needed > > > > > >> + > > >> +if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { > > >> +dev_err(&client->dev, "I2C check functionality > > >> failed\n"); > > >> +return -ENXIO; > > >> +} > > >> + > > >> +indio_dev = devm_iio_device_alloc(&client->dev, > > >> sizeof(*dmard06)); > > >> +if (!indio_dev) { > > >> +dev_err(&client->dev, "Failed to allocate iio > > >> device\n"); > > >> +return -ENOMEM; > > >> +} > > >> + > > >> +dmard06 = iio_priv(indio_dev); > > >> +dmard06->client = client; > > >> + > > >> +error = dmard06_read_chip_id(dmard06); > > >> +if (error) > > >> +return error; > > >> + > > >> +i2c_set_clientdata(client, indio_dev); > > >> +indio_dev->dev.parent = &client->dev; > > >> +indio_dev->name = DMARD06_DRV_NAME; > > >> +indio_dev->modes = INDIO_DIRECT_MODE; > > >> +indio_dev->channels = dmard06_channels; > > >> +indio_dev->num_channels = ARRAY_SIZE(dmard06_channels); > > >> +indio_dev->info = &dmard06_info; > > >> + > > >> +return devm_iio_device_register(&client->dev, indio_dev); > > >> +} > > >> + > > >> +static const struct i2c_device_id dmard06_id[] = { > > >> +{ "dmard06", 0 }, > > >> +{ } > > >> +}; > > >> +MODULE_DEVICE_TABLE(i2c, dmard06_id); > > >> + > > >> +static const struct of_device_id dmard06_of_match[] = { > > >> +{ .compatible = "domintech,dmard06" }, > > >> +{ } > > >> +}; > > >> +MODULE_DEVICE_TABLE(of, dmard06_of_match); > > >> + > > >> +static struct i2c_driver dmard06_driver = { > > >> +.probe = dmard06_probe, > > >> +.id_table = dmard06_id, > > >> +.driver = { > > >> +.name = DMARD06_DRV_NAME, > > >> +.of_match_table = of_match_ptr(dmard06_of_match), > > >> +}, > > >> +}; > > >> +module_i2c_driver(dmard06_driver); > > >> + > > >> +MODULE_AUTHOR("Aleksei Mamlin "); > > >> +MODULE_DESCRIPTION("Domintech DMARD06 accelerometer driver"); > > >> +MODULE_LICENSE("GPL v2"); > > >> > > > > > > > > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH] iio: orientation: Add BNO055 9-axis Absolute Orientation Sensor driver
_dev->num_channels = ARRAY_SIZE(bno055_channels); > + indio_dev->info = &bno055_info; > + indio_dev->modes = INDIO_DIRECT_MODE; > + > + data->regmap = devm_regmap_init_i2c(client, &bno055_regmap_config); > + if (IS_ERR(data->regmap)) { > + dev_err(&client->dev, "Failed to allocate register map.\n"); > + return PTR_ERR(data->regmap); > + } > + > + indio_dev->dev.driver_data = data; > + i2c_set_clientdata(client, indio_dev); > + > + ret = regmap_read(data->regmap, BNO055_REG_ID, &chip_id); > + if (ret < 0) > + return ret; > + if (chip_id != BNO055_CHIP_ID) { > + dev_err(&client->dev, "bad chip id. expected %x got %x\n", > + BNO055_CHIP_ID, chip_id); > + return -EINVAL; > + } > + > + ret = bno055_chip_init(data); > + if (ret < 0) > + return ret; > + > + ret = devm_iio_device_register(&client->dev, indio_dev); > + if (ret){ space before { > + dev_err(&client->dev, "failed to register IIO device.\n"); > + return ret; > + } > + > + ret = device_create_file(&indio_dev->dev, &dev_attr_calib_status); needs to be done BEFORE devm_iio_device_register() IIO has specific mechanisms to create custom sysfs entries... > + if (ret){ space before { > + dev_err(&client->dev, "failed to create sysfs entry.\n"); > + devm_iio_device_unregister(&client->dev, indio_dev); no need to call devm_iio_device_unregister > + return ret; > + } > + > + return ret; > +} > + > +static int bno055_remove(struct i2c_client *client) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(client); > + > + device_remove_file(&indio_dev->dev, &dev_attr_calib_status); > + devm_iio_device_unregister(&client->dev, indio_dev); > + return 0; > +} > + > +static const struct acpi_device_id bno055_acpi_match[] = { > + {"bno055", 0}, > + { }, > +}; > +MODULE_DEVICE_TABLE(acpi, bno055_acpi_match); > + > +static const struct i2c_device_id bno055_id[] = { > + {"bno055", 0}, > + { }, > +}; > +MODULE_DEVICE_TABLE(i2c, bno055_id); > + > +static struct i2c_driver bno055_driver = { > + .driver = { > + .name = "bno055", > + .acpi_match_table = ACPI_PTR(bno055_acpi_match), > + }, > + .probe = bno055_probe, > + .remove = bno055_remove, > + .id_table = bno055_id, > +}; > +module_i2c_driver(bno055_driver); > + > +MODULE_AUTHOR("navin patidar "); > +MODULE_DESCRIPTION("Driver for Bosch Sensortec BNO055 orientation sensor"); > +MODULE_LICENSE("GPL v2"); > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH] iio: orientation: Add BNO055 9-axis Absolute Orientation Sensor driver
Hallo Navin, > Thanks for reviewing the patch. I will send the updated patch. I am sorry to have overlooked a previous patch by Vlad Dogaru, see http://comments.gmane.org/gmane.linux.kernel.iio/24643, for the BNO055 there have been a number of issues, it has not been accepted yet I think our policy is first come, first served; I have compared the two patches, maybe you can collaborate on a common proposal? regards, p. > regards, > --navin-patidar > > On Tue, Jul 26, 2016 at 1:07 AM, Peter Meerwald-Stadler > wrote: > > > > > > BNO055 provides the following motion sensors data: > > > > > > * Gyroscope > > > * Accelerometer > > > * Magnetometer > > > * Absolute Orientation (Quaternion) > > > > comments below > > > > > Signed-off-by: navin patidar > > > --- > > > drivers/iio/orientation/Kconfig | 10 + > > > drivers/iio/orientation/Makefile | 1 + > > > drivers/iio/orientation/bno055.c | 422 > > +++ > > > 3 files changed, 433 insertions(+) > > > create mode 100644 drivers/iio/orientation/bno055.c > > > > > > diff --git a/drivers/iio/orientation/Kconfig > > b/drivers/iio/orientation/Kconfig > > > index e3aa1e5..9ac21ee 100644 > > > --- a/drivers/iio/orientation/Kconfig > > > +++ b/drivers/iio/orientation/Kconfig > > > @@ -28,4 +28,14 @@ config HID_SENSOR_DEVICE_ROTATION > > > device rotation. The output of a device rotation sensor > > > is presented using quaternion format. > > > > > > +config BNO055 > > > + tristate "BOSCH BNO055 Absolute Orientation" > > > + depends on I2C > > > + select REGMAP_I2C > > > + help > > > + Say yes here to build support for BOSCH BNO55 9-axis absolute > > orientation sensor > > > + driver connected via I2C. > > > + This driver can also be built as a module. If so, the module > > > + will be called bno055. > > > + > > > endmenu > > > diff --git a/drivers/iio/orientation/Makefile > > b/drivers/iio/orientation/Makefile > > > index 4734dab..7d00037 100644 > > > --- a/drivers/iio/orientation/Makefile > > > +++ b/drivers/iio/orientation/Makefile > > > @@ -5,3 +5,4 @@ > > > # When adding new entries keep the list in alphabetical order > > > obj-$(CONFIG_HID_SENSOR_INCLINOMETER_3D) += hid-sensor-incl-3d.o > > > obj-$(CONFIG_HID_SENSOR_DEVICE_ROTATION) += hid-sensor-rotation.o > > > +obj-$(CONFIG_BNO055) += bno055.o > > > diff --git a/drivers/iio/orientation/bno055.c > > b/drivers/iio/orientation/bno055.c > > > new file mode 100644 > > > index 000..48b9e93 > > > --- /dev/null > > > +++ b/drivers/iio/orientation/bno055.c > > > @@ -0,0 +1,422 @@ > > > +/* > > > + * Copyright (c) 2016 Intel Corporation > > > + * > > > + * Driver for Bosch Sensortec BNO055 digital motion sensor. > > > + * > > > + * This program is free software; you can redistribute it and/or modify > > > + * it under the terms of the GNU General Public License version 2 as > > > + * published by the Free Software Foundation. > > > + * > > > > I find it useful to state the 7-bit i2c chip address here > > > > > + */ > > > + > > > +#define pr_fmt(fmt) "bno055: " fmt > > > + > > > +#include > > > +#include > > > +#include > > > +#include > > > +#include > > > +#include > > > + > > > > drop one newline > > > > > + > > > +#define BNO055_CHIP_ID 0xA0 > > > +#define REG_MAG_RADIUS_MSB 0x6A > > > > please consistently prefix with BNO055_ > > > > > + > > > +/* BNO055 configuration registers */ > > > +#define REG_PWR_MODE 0x3E > > > +#define REG_OPR_MODE 0x3D > > > +#define REG_UNIT_SEL 0x3B > > > +#define REG_AXIS_MAP_SIGN0x42 > > > +#define REG_AXIS_MAP_CONFIG 0x41 > > > +#define REG_UNIT_SEL 0x3B > > > +#define REG_SYS_TRIGGER 0x3F > > > +#define BNO055_REG_PAGE_ID 0x07 > > > +#define BNO055_REG_ID0x00 > > > + > > > +/* BNO055 status registers */ > > > +#define SYS_STATUS 0x39 > > > +#define INT_STATUS 0x37 > > > +#define CALIB_STATUS 0x35 > > > + > > > +/* BNO055 data reg
Re: [PATCH] iio: dac: mcp4725: Remove unneeded conversions to bool
> Found with scripts/coccinelle/misc/boolconv.cocci. I'd argue that pd = (inbuf[0] >> 1) & 0x3; if (pd) { data->powerdown = true; data->powerdown_mode = pd - 1; } else { data->powerdown = false; data->powerdown_mode = 2; /* largest register to gnd */ } is less compact but probably easier to read/understand; > Signed-off-by: Andrew F. Davis but I'm fine with the proposed patch as well to make cocci happy regards, p. > --- > drivers/iio/dac/mcp4725.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c > index cca935c..c3bb3fd 100644 > --- a/drivers/iio/dac/mcp4725.c > +++ b/drivers/iio/dac/mcp4725.c > @@ -361,7 +361,7 @@ static int mcp4725_probe(struct i2c_client *client, > return err; > } > pd = (inbuf[0] >> 1) & 0x3; > - data->powerdown = pd > 0 ? true : false; > + data->powerdown = pd > 0; > data->powerdown_mode = pd ? pd - 1 : 2; /* largest register to gnd */ > data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4); > > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH v2 4/7] dt-bindings: iio: document dpot-dac bindings
nitpicking below > Signed-off-by: Peter Rosin > --- > .../devicetree/bindings/iio/dac/dpot-dac.txt | 41 > ++ > MAINTAINERS| 6 > 2 files changed, 47 insertions(+) > create mode 100644 Documentation/devicetree/bindings/iio/dac/dpot-dac.txt > > diff --git a/Documentation/devicetree/bindings/iio/dac/dpot-dac.txt > b/Documentation/devicetree/bindings/iio/dac/dpot-dac.txt > new file mode 100644 > index ..4fd5d63cc2d6 > --- /dev/null > +++ b/Documentation/devicetree/bindings/iio/dac/dpot-dac.txt > @@ -0,0 +1,41 @@ > +Bindings for DAC emulation using a digital potentiometer > + > +It is assumed the that the dpot is used as a voltage divider between the delete 'the' before 'that' > +current dpot wiper setting and the maximum resistance of the dpot. The > +divided voltage is provided by a vref regulator. > + > + .--. > + .---. | | > + | vref |--'.---. > + | regulator |--.| | > + '---' || d | > + || p | > + || o | wiper > + || t |<-+ > + || | > + |'---' dac output voltage > + | | > + '--++ > + > +Required properties: > +- compatible: Should be "dpot-dac" > +- vref-supply: The regulator supplying the voltage divider. > +- io-channels: Channel node of the dpot to be used for the voltage division. > +- io-channel-names: Should be "dpot". > + > +Example: > + > + &i2c { > + dpot: mcp4651-503@28 { > + compatible = "microchip,mcp4651-503"; > + reg = <0x28>; > + #io-channel-cells = <1>; > + }; > + }; > + > + dac { > + compatible = "dpot-dac"; > + vref-supply = <®_3v3>; > + io-channels = <&dpot 0>; > + io-channel-names = "dpot"; > + }; > diff --git a/MAINTAINERS b/MAINTAINERS > index 1cd38a7e0064..c68b72088945 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -6111,6 +6111,12 @@ L: linux-me...@vger.kernel.org > S: Maintained > F: drivers/media/rc/iguanair.c > > +IIO DIGITAL POTENTIOMETER DAC > +M: Peter Rosin > +L: linux-...@vger.kernel.org > +S: Maintained > +F: Documentation/devicetree/bindings/iio/dac/dpot-dac.txt > + > IIO SUBSYSTEM AND DRIVERS > M: Jonathan Cameron > R: Hartmut Knaack > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH v3 2/8] iio: inkern: add helpers to query available values from channels
_raw); > > + > > int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type > > *type) > > { > > int ret = 0; > > diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h > > index 9edccfba1ffb..baab5e45734f 100644 > > --- a/include/linux/iio/consumer.h > > +++ b/include/linux/iio/consumer.h > > @@ -226,6 +226,35 @@ int iio_read_channel_processed(struct iio_channel > > *chan, int *val); > > int iio_write_channel_raw(struct iio_channel *chan, int val); > > > > /** > > + * iio_read_max_channel_raw() - read maximum available raw value from a > > given > > + * channel > > + * @chan: The channel being queried. > > + * @val: Value read back. > > + * > > + * Note raw reads from iio channels are in adc counts and hence "Note: raw reads..." would be easier... here and below why is there no val2 here? just reading the documentation ("maximum available raw value") I am not sure what the function does: does it return the maximum value possible? or the maximum value in some internal buffer? maximum value ever seen? > > + * scale will need to be applied if standard units are required. > > + */ > > +int iio_read_max_channel_raw(struct iio_channel *chan, int *val); > > + > > +/** > > + * iio_read_avail_channel_raw() - read available raw values from a given > > channel > > + * @chan: The channel being queried. > > + * @vals: Available values read back. no vals2? > > + * @type: Type of available values in vals. it is not clear what type is > > + * @length:Number of entries in vals. > > + * > > + * Returns an error code, IIO_AVAIL_RANGE or IIO_AVAIL_LIST. > > + * > > + * For ranges, three vals are always returned; min, step and max. > > + * For lists, all the possible values are enumerated. > > + * > > + * Note raw available values from iio channels are in adc counts and > > + * hence scale will need to be applied if standard units are required. > > + */ > > +int iio_read_avail_channel_raw(struct iio_channel *chan, > > + const int **vals, int *type, int *length); > > + > > +/** > > * iio_get_channel_type() - get the type of a channel > > * @channel: The channel being queried. > > * @type: The type of the channel. > > diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h > > index 45b781084a4b..e565701d13ce 100644 > > --- a/include/linux/iio/iio.h > > +++ b/include/linux/iio/iio.h > > @@ -315,6 +315,23 @@ static inline bool iio_channel_has_info(const struct > > iio_chan_spec *chan, > > (chan->info_mask_shared_by_all & BIT(type)); > > } > > > > +/** > > + * iio_channel_has_available() - Checks if a channel has an available > > attribute > > + * @chan: The channel to be queried > > + * @type: Type of the available attribute to be checked > > + * > > + * Returns true if the channels supports reporting available values for the channel > > + * given attribute type, false otherwise. > > + */ > > +static inline bool iio_channel_has_available(const struct iio_chan_spec > > *chan, > > +enum iio_chan_info_enum type) > > +{ > > + return (chan->info_mask_separate_available & BIT(type)) | > > + (chan->info_mask_shared_by_type_available & BIT(type)) | > > + (chan->info_mask_shared_by_dir_available & BIT(type)) | > > + (chan->info_mask_shared_by_all_available & BIT(type)); > > +} > > + > > #define IIO_CHAN_SOFT_TIMESTAMP(_si) { > > \ > > .type = IIO_TIMESTAMP, \ > > .channel = -1, \ > > > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH v3 6/8] iio: dpot-dac: DAC driver based on a digital potentiometer
> > + .read_avail = dpot_dac_read_avail, > > + .write_raw = dpot_dac_write_raw, > > + .driver_module = THIS_MODULE, > > +}; > > + > > +static int dpot_dac_channel_max_ohms(struct iio_dev *indio_dev) > > +{ > > + struct device *dev = &indio_dev->dev; > > + struct dpot_dac *dac = iio_priv(indio_dev); > > + unsigned long long tmp; > > + int ret; > > + int val; > > + int val2; > > + int max; > > + > > + ret = iio_read_max_channel_raw(dac->dpot, &max); > > + if (ret < 0) { > > + dev_err(dev, "dpot does not indicate its raw maximum value\n"); > > + return ret; > > + } > > + > > + switch (iio_read_channel_scale(dac->dpot, &val, &val2)) { > > + case IIO_VAL_INT: > > + return max * val; > > + case IIO_VAL_FRACTIONAL: > > + tmp = (unsigned long long)max * val; > > + do_div(tmp, val2); > > + return tmp; > > + case IIO_VAL_FRACTIONAL_LOG2: > > + tmp = (s64)val * 10LL * max >> val2; (s64) necessary? or rather unsigned long long? > > + do_div(tmp, 10LL); > > + return tmp; > > + default: > > + dev_err(dev, "dpot has a scale that is too weird\n"); > > + } > > + > > + return -EINVAL; > > +} > > + > > +static int dpot_dac_probe(struct platform_device *pdev) > > +{ > > + struct device *dev = &pdev->dev; > > + struct iio_dev *indio_dev; > > + struct dpot_dac *dac; > > + enum iio_chan_type type; > > + int ret; > > + > > + indio_dev = devm_iio_device_alloc(dev, sizeof(*dac)); > > + if (!indio_dev) > > + return -ENOMEM; > > + > > + platform_set_drvdata(pdev, indio_dev); > > + dac = iio_priv(indio_dev); > > + > > + indio_dev->name = dev_name(dev); > > + indio_dev->dev.parent = dev; > > + indio_dev->info = &dpot_dac_info; > > + indio_dev->modes = INDIO_DIRECT_MODE; > > + indio_dev->channels = &dpot_dac_iio_channel; > > + indio_dev->num_channels = 1; > > + > > + dac->vref = devm_regulator_get(dev, "vref"); > > + if (IS_ERR(dac->vref)) { > > + if (PTR_ERR(dac->dpot) != -EPROBE_DEFER) > > + dev_err(&pdev->dev, "failed to get vref regulator\n"); > > + return PTR_ERR(dac->vref); > > + } > > + > > + dac->dpot = devm_iio_channel_get(dev, "dpot"); > > + if (IS_ERR(dac->dpot)) { > > + if (PTR_ERR(dac->dpot) != -EPROBE_DEFER) > > + dev_err(dev, "failed to get dpot input channel\n"); > > + return PTR_ERR(dac->dpot); > > + } > > + > > + ret = iio_get_channel_type(dac->dpot, &type); > > + if (ret < 0) > > + return ret; > > + > > + if (type != IIO_RESISTANCE) { > > + dev_err(dev, "dpot is of the wrong type\n"); > > + return -EINVAL; > > + } > > + > > + ret = dpot_dac_channel_max_ohms(indio_dev); > > + if (ret < 0) > > + return ret; > > + dac->max_ohms = ret; > > + dev_info(dev, "dpot max is %d\n", dac->max_ohms); > Given we can query this (indirectly) from the dpot itself, I'd drop this now. max_ohms is u32, so this should be %u not %d? > > > + > > + ret = regulator_enable(dac->vref); > > + if (ret) { > > + dev_err(dev, "failed to enable the vref regulator\n"); > > + return ret; > > + } > > + > > + ret = iio_device_register(indio_dev); > > + if (ret) { > > + dev_err(dev, "failed to register iio device\n"); > > + goto disable_reg; > > + } > > + > > + return 0; > > + > > +disable_reg: > > + regulator_disable(dac->vref); > > + return ret; > > +} > > + > > +static int dpot_dac_remove(struct platform_device *pdev) > > +{ > > + struct iio_dev *indio_dev = platform_get_drvdata(pdev); > > + struct dpot_dac *dac = iio_priv(indio_dev); > > + > > + iio_device_unregister(indio_dev); > > + regulator_disable(dac->vref); > > + > > + return 0; > > +} > > + > > +static const struct of_device_id dpot_dac_match[] = { > > + { .compatible = "dpot-dac" }, > > + { /* sentinel */ } > > +}; > > +MODULE_DEVICE_TABLE(of, dpot_dac_match); > > + > > +static struct platform_driver dpot_dac_driver = { > > + .probe = dpot_dac_probe, > > + .remove = dpot_dac_remove, > > + .driver = { > > + .name = "iio-dpot-dac", > > + .of_match_table = dpot_dac_match, > > + }, > > +}; > > +module_platform_driver(dpot_dac_driver); > > + > > +MODULE_DESCRIPTION("DAC emulation driver using a digital potentiometer"); > > +MODULE_AUTHOR("Peter Rosin "); > > +MODULE_LICENSE("GPL v2"); > > > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH v4 2/2] iio: adc: hx711: Add IIO driver for AVIA HX711
_RAW), > + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), > + }, > +}; > + > +static int hx711_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct hx711_data *hx711_data; > + struct iio_dev *iio; > + int ret; > + int i; > + > + iio = devm_iio_device_alloc(dev, sizeof(struct hx711_data)); > + if (!iio) { > + dev_err(dev, "failed to allocate IIO device\n"); > + return -ENOMEM; > + } > + > + hx711_data = iio_priv(iio); > + hx711_data->dev = dev; > + > + mutex_init(&hx711_data->lock); > + > + /* PD_SCK stands for power down and serial clock input of HX711 > + * in the driver it is an output > + */ > + hx711_data->gpiod_pd_sck = devm_gpiod_get(dev, "sck", GPIOD_OUT_LOW); > + if (IS_ERR(hx711_data->gpiod_pd_sck)) { > + dev_err(dev, "failed to get sck-gpiod: err=%ld\n", > + PTR_ERR(hx711_data->gpiod_pd_sck)); > + return PTR_ERR(hx711_data->gpiod_pd_sck); > + } > + > + /* DOUT stands for serial data output of HX711 > + * for the driver it is an input > + */ > + hx711_data->gpiod_dout = devm_gpiod_get(dev, "dout", GPIOD_IN); > + if (IS_ERR(hx711_data->gpiod_dout)) { > + dev_err(dev, "failed to get dout-gpiod: err=%ld\n", > + PTR_ERR(hx711_data->gpiod_dout)); > + return PTR_ERR(hx711_data->gpiod_dout); > + } > + > + hx711_data->reg_avdd = devm_regulator_get(dev, "avdd"); > + if (IS_ERR(hx711_data->reg_avdd)) > + return PTR_ERR(hx711_data->reg_avdd); > + > + ret = regulator_enable(hx711_data->reg_avdd); > + if (ret < 0) > + return ret; > + > + /* > + * with > + * full scale differential input range: AVDD / GAIN > + * full scale output data: 2^24 > + * we can say: > + * AVDD / GAIN = 2^24 > + * therefore: > + * 1 LSB = AVDD / GAIN / 2^24 > + * AVDD is in uV, but we need 10^-9 mV > + * approximately to fit into a 32 bit number: > + * 1 LSB = (AVDD * 100) / GAIN / 1678 [10^-9 mV] > + */ > + ret = regulator_get_voltage(hx711_data->reg_avdd); > + if (ret < 0) disable regulator > + return ret; > + /* we need 10^-9 mV */ > + ret *= 100; > + > + for (i = 0; i < HX711_GAIN_MAX; i++) > + hx711_gain_to_scale[i].scale = > + ret / hx711_gain_to_scale[i].gain / 1678; > + > + hx711_data->gain_set = 128; > + hx711_data->gain_chan_a = 128; > + > + platform_set_drvdata(pdev, iio); > + > + iio->name = "hx711"; > + iio->dev.parent = &pdev->dev; > + iio->info = &hx711_iio_info; > + iio->modes = INDIO_DIRECT_MODE; > + iio->channels = hx711_chan_spec; > + iio->num_channels = ARRAY_SIZE(hx711_chan_spec); > + > + return devm_iio_device_register(dev, iio); disable regulator if this fails > +} > + > +static int hx711_remove(struct platform_device *pdev) > +{ > + struct hx711_data *hx711_data; > + struct iio_dev *iio; > + > + iio = platform_get_drvdata(pdev); > + hx711_data = iio_priv(iio); > + devm_iio_device_register and _remove() doesn't mix > + regulator_disable(hx711_data->reg_avdd); > + > + return 0; > +} > + > +static const struct of_device_id of_hx711_match[] = { > + { .compatible = "avia,hx711", }, > + {}, > +}; > + > +MODULE_DEVICE_TABLE(of, of_hx711_match); > + > +static struct platform_driver hx711_driver = { > + .probe = hx711_probe, > + .remove = hx711_remove, > + .driver = { > + .name = "hx711-gpio", > + .of_match_table = of_hx711_match, > + }, > +}; > + > +module_platform_driver(hx711_driver); > + > +MODULE_AUTHOR("Andreas Klinger "); > +MODULE_DESCRIPTION("HX711 bitbanging driver - ADC for weight cells"); > +MODULE_LICENSE("GPL"); > +MODULE_ALIAS("platform:hx711-gpio"); > + > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH 1/2] iio: adc: hx711: Add DT binding for avia,hx711
> Add DT bindings for avia,hx711 > Add vendor avia to vendor list > > Signed-off-by: Andreas Klinger > --- > .../devicetree/bindings/iio/adc/avia-hx711.txt | 23 > ++ > .../devicetree/bindings/vendor-prefixes.txt| 1 + > 2 files changed, 24 insertions(+) > create mode 100644 Documentation/devicetree/bindings/iio/adc/avia-hx711.txt > > diff --git a/Documentation/devicetree/bindings/iio/adc/avia-hx711.txt > b/Documentation/devicetree/bindings/iio/adc/avia-hx711.txt > new file mode 100644 > index ..d56f95705fd2 > --- /dev/null > +++ b/Documentation/devicetree/bindings/iio/adc/avia-hx711.txt > @@ -0,0 +1,23 @@ > +* AVIA HX711 ADC chip for weight cells > + Bit-banging driver > + > +Required properties: > + - compatible: Should be "avia,hx711" > + - sck-gpios:Definition of the GPIO for the clock > + - dout-gpios: Definition of the GPIO for Data-Out data-out (lowercase) > + See Documentation/devicetree/bindings/gpio/gpio.txt > + > +Recommended properties: > + - gain: Gain select, can be 32, 64 or 128 > + default is 128 > + > +Optional properties: maybe delete this empty section? > + > +Example: > +weight@0 { > + compatible = "avia,hx711"; > + sck-gpios = <&gpio3 10 GPIO_ACTIVE_HIGH>; > + dout-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>; > + gain = <32> > +}; > + > diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt > b/Documentation/devicetree/bindings/vendor-prefixes.txt > index 44ddc980b085..4696bb5c2198 100644 > --- a/Documentation/devicetree/bindings/vendor-prefixes.txt > +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt > @@ -32,6 +32,7 @@ atlas Atlas Scientific LLC > atmelAtmel Corporation > auo AU Optronics Corporation > avago Avago Technologies > +avia avia semiconductor > avic Shanghai AVIC Optoelectronics Co., Ltd. > axis Axis Communications AB > boe BOE Technology Group Co., Ltd. > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH 2/2] iio: adc: hx711: Add IIO driver for AVIA HX711
goto err; > + } > + > + ret = gpiod_direction_output(hx711_data->gpiod_sck, 0); > + if (ret < 0) { > + dev_err(hx711_data->dev, "gpiod_direction_output: %d\n", ret); > + goto err; > + } > + > + platform_set_drvdata(pdev, iio); > + > + iio->name = pdev->name; > + iio->dev.parent = &pdev->dev; > + iio->info = &hx711_iio_info; > + iio->modes = INDIO_DIRECT_MODE; > + iio->channels = hx711_chan_spec; > + iio->num_channels = ARRAY_SIZE(hx711_chan_spec); > + > + dev_err(hx711_data->dev, "initialized\n"); excessive logging, please remove > + > + return devm_iio_device_register(dev, iio); > + > +err: > + return ret; just return directly without goto? > +} > + > + > +static int hx711_suspend(struct device *dev) > +{ pointless, just don't do PM support > + return 0; > +} > + > +static int hx711_resume(struct device *dev) > +{ > + return 0; > +} > + > +static SIMPLE_DEV_PM_OPS(hx711_pm_ops, hx711_suspend, hx711_resume); > + > + > +static const struct of_device_id of_hx711_match[] = { > + { .compatible = "avia,hx711", }, > + {}, > +}; > + > +MODULE_DEVICE_TABLE(of, of_hx711_match); > + > +static struct platform_driver hx711_driver = { > + .probe = hx711_probe, > + .driver = { > + .name = "hx711-gpio", > + .pm = &hx711_pm_ops, > + .of_match_table = of_hx711_match, > + }, > +}; > + > +module_platform_driver(hx711_driver); > + > +MODULE_AUTHOR("Andreas Klinger "); > +MODULE_DESCRIPTION("HX711 bitbanging driver - ADC for weight cells"); > +MODULE_LICENSE("GPL"); > +MODULE_ALIAS("platform:hx711-gpio"); > + > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH] iio: adc: New driver for TI ADS79XX chips
; + int ret; > > + > > + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); > > + if (indio_dev == NULL) > > + return -ENOMEM; > > + > > + st = iio_priv(indio_dev); > > + > > + spi_set_drvdata(spi, indio_dev); > > + > > + st->spi = spi; > > + st->settings = ADS79XX_CR_MANUAL | ADS79XX_CR_RANGE_5V; > > + > > + info = &ti_ads79xx_chip_info[spi_get_device_id(spi)->driver_data]; > > + > > + indio_dev->name = spi_get_device_id(spi)->name; > > + indio_dev->dev.parent = &spi->dev; > > + indio_dev->modes = INDIO_DIRECT_MODE; > > + indio_dev->channels = info->channels; > > + indio_dev->num_channels = info->num_channels; > > + indio_dev->info = &ti_ads79xx_info; > > + > > + /* > > +* Setup default message. The chip takes one full cycle to convert a > > +* sample. The conversion process is driven by the SPI clock, which > > +* is why we have 3 transfers. The middle one is just dummy data sent > > +* while the chip is converting the sample from first transfer. > > +*/ > > + > > + st->scan_single_xfer[0].tx_buf = &st->tx_buf[0]; > > + st->scan_single_xfer[0].len = 2; > > + st->scan_single_xfer[0].cs_change = 1; > > + st->scan_single_xfer[1].tx_buf = &st->tx_buf[0]; > > + st->scan_single_xfer[1].len = 2; > > + st->scan_single_xfer[1].cs_change = 1; > > + st->scan_single_xfer[2].rx_buf = &st->rx_buf[0]; > > + st->scan_single_xfer[2].len = 2; > > + > Use spi_message_init_with_transfers (really minor but why not ;) > > + spi_message_init(&st->scan_single_msg); > > + spi_message_add_tail(&st->scan_single_xfer[0], &st->scan_single_msg); > > + spi_message_add_tail(&st->scan_single_xfer[1], &st->scan_single_msg); > > + spi_message_add_tail(&st->scan_single_xfer[2], &st->scan_single_msg); > > + > > + st->reg = devm_regulator_get(&spi->dev, "refin"); > > + if (IS_ERR(st->reg)) { > > + dev_err(&spi->dev, "Failed get get regulator \"refin\"\n"); > > + return PTR_ERR(st->reg); > > + } > > + > > + ret = regulator_enable(st->reg); > > + if (ret) { > > + dev_err(&spi->dev, "Failed to enable regulator \"refin\"\n"); > > + return ret; > > + } > > + > > + ret = iio_triggered_buffer_setup(indio_dev, NULL, > > +&ti_ads79xx_trigger_handler, NULL); > > + if (ret) { > > + dev_err(&spi->dev, "Failed to setup triggered buffer\n"); > > + goto error_disable_reg; > > + } > > + > > + ret = iio_device_register(indio_dev); > > + if (ret) { > > + dev_err(&spi->dev, "Failed to register iio device\n"); > > + goto error_cleanup_ring; > > + } > > + > Don't bother with this. It's easy to tell from sysfs or the results of the > probe that it worked or not. This just puts uninformative noise in the > logs (different matter if there is a serial number of similar that provides > additional info!) > > + dev_info(&spi->dev, "Registered %s\n", indio_dev->name); > > + > > + return 0; > > + > > +error_cleanup_ring: > > + iio_triggered_buffer_cleanup(indio_dev); > > +error_disable_reg: > > + regulator_disable(st->reg); > > + > > + return ret; > > +} > > + > > +static int ti_ads79xx_remove(struct spi_device *spi) > > +{ > > + struct iio_dev *indio_dev = spi_get_drvdata(spi); > > + struct ti_ads79xx_state *st = iio_priv(indio_dev); > > + > > + iio_device_unregister(indio_dev); > > + iio_triggered_buffer_cleanup(indio_dev); > > + regulator_disable(st->reg); > > + > > + return 0; > > +} > > + > > +static const struct spi_device_id ti_ads79xx_id[] = { > > + {"ti-ads7950", ADS7950}, > > + {"ti-ads7951", ADS7951}, > > + {"ti-ads7952", ADS7952}, > > + {"ti-ads7953", ADS7953}, > > + {"ti-ads7954", ADS7954}, > > + {"ti-ads7955", ADS7955}, > > + {"ti-ads7956", ADS7956}, > > + {"ti-ads7957", ADS7957}, > > + {"ti-ads7958", ADS7958}, > > + {"ti-ads7959", ADS7959}, > > + {"ti-ads7960", ADS7960}, > > + {"ti-ads7961", ADS7961}, > > + { } > > +}; > > +MODULE_DEVICE_TABLE(spi, ti_ads79xx_id); > > + > > +static struct spi_driver ti_ads79xx_driver = { > > + .driver = { > > + .name = "ti-ads79xx", > > + }, > > + .probe = ti_ads79xx_probe, > > + .remove = ti_ads79xx_remove, > > + .id_table = ti_ads79xx_id, > > +}; > > +module_spi_driver(ti_ads79xx_driver); > > + > > +MODULE_AUTHOR("David Lechner "); > > +MODULE_DESCRIPTION("TI ADS79XX ADC"); > > +MODULE_LICENSE("GPL v2"); > > > > -- > To unsubscribe from this list: send the line "unsubscribe linux-iio" in > the body of a message to majord...@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH v2] iio: adc: New driver for TI ADS7950 chips
> > +static int ti_ads7950_read_raw(struct iio_dev *indio_dev, > > + struct iio_chan_spec const *chan, > > + int *val, int *val2, long m) > > +{ > > + struct ti_ads7950_state *st = iio_priv(indio_dev); > > + int ret; > > + > > + switch (m) { > > + case IIO_CHAN_INFO_RAW: > > + > > + ret = iio_device_claim_direct_mode(indio_dev); > > + if (ret < 0) > > + return ret; > > + > > + ret = ti_ads7950_scan_direct(st, chan->address); > > + iio_device_release_direct_mode(indio_dev); > > + if (ret < 0) > > + return ret; > > + > > + if (chan->address != TI_ADS7950_EXTRACT(ret, 12, 4)) > > + return -EIO; > > + > > + *val = TI_ADS7950_EXTRACT(ret, 0, 12); > > I'm not sure if I am doing this right. There are 8- 10- and 12-bit versions of > this chip. The 8- and 10-bit versions still return a 12-bit number where the > last 4 or 2 bits are always 0. Should I be shifting the 12-bit value here > based on the chip being used so that *val is 0-255 for 8-bit and 0-1023 for > 10-bit? Or should this be *really* raw and not even use TI_ADS7950_EXTRACT() > to mask the channel address bits? I'd shift and adjust _SCALE so that *val * scale gives mV > > + > > + return IIO_VAL_INT; > > + case IIO_CHAN_INFO_SCALE: > > + ret = ti_ads7950_get_range(st); > > + if (ret < 0) > > + return ret; > > + > > + *val = ret; > > + *val2 = chan->scan_type.realbits; > > + > > + return IIO_VAL_FRACTIONAL_LOG2; > > + } > > + > > + return -EINVAL; > > +} -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH v2 2/2] iio: Aspeed AST2400/AST2500 ADC
hw_register_divider( > + &pdev->dev, "scaler", "prescaler", > + CLK_SET_RATE_PARENT, > + data->base + ASPEED_ADC_REG_CLOCK_CONTROL, > + 0, 10, 0, &data->clk_lock); > + if (IS_ERR(data->clk_scaler)) { > + dev_err(&pdev->dev, "Failed allocating scaler clock\n"); > + ret = PTR_ERR(data->clk_scaler); > + goto scaler_error; > + } > + > + /* Start all channels in normal mode. */ > + clk_prepare_enable(data->clk_scaler->clk); > + adc_engine_control_reg_val = GENMASK(31, 16) | > + ASPEED_ADC_OPERATION_MODE_NORMAL | ASPEED_ADC_ENGINE_ENABLE; > + writel(adc_engine_control_reg_val, > + data->base + ASPEED_ADC_REG_ENGINE_CONTROL); > + > + indio_dev->name = dev_name(&pdev->dev); > + indio_dev->dev.parent = &pdev->dev; > + indio_dev->info = &aspeed_adc_iio_info; > + indio_dev->modes = INDIO_DIRECT_MODE; > + indio_dev->channels = aspeed_adc_iio_channels; > + indio_dev->num_channels = ARRAY_SIZE(aspeed_adc_iio_channels); > + > + ret = iio_device_register(indio_dev); > + if (ret) { > + dev_err(&pdev->dev, "Could't register the device.\n"); Couldn't > + goto iio_register_error; > + } > + > + return 0; > + > +iio_register_error: > + writel(0x0, data->base + ASPEED_ADC_REG_ENGINE_CONTROL); > + clk_disable_unprepare(data->clk_scaler->clk); > + clk_hw_unregister_divider(data->clk_scaler); > + > +scaler_error: > + clk_hw_unregister_divider(data->clk_prescaler); > + > +prescaler_error: > +resource_error: > + return ret; > +} > + > +static int aspeed_adc_remove(struct platform_device *pdev) > +{ > + struct iio_dev *indio_dev = platform_get_drvdata(pdev); > + struct aspeed_adc_data *data = iio_priv(indio_dev); > + > + iio_device_unregister(indio_dev); writel(0x0, data->base + ASPEED_ADC_REG_ENGINE_CONTROL); I guess this power off the device? the MODE #defines are now used (powerdown, normal, etc.) > + clk_disable_unprepare(data->clk_scaler->clk); > + clk_hw_unregister_divider(data->clk_scaler); > + clk_hw_unregister_divider(data->clk_prescaler); > + > + return 0; > +} > + > +const struct of_device_id aspeed_adc_matches[] = { > + { .compatible = "aspeed,ast2400-adc" }, > + { .compatible = "aspeed,ast2500-adc" }, > +}; > +MODULE_DEVICE_TABLE(of, aspeed_adc_matches); > + > +static struct platform_driver aspeed_adc_driver = { > + .probe = aspeed_adc_probe, > + .remove = aspeed_adc_remove, > + .driver = { > + .name = KBUILD_MODNAME, > + .of_match_table = aspeed_adc_matches, > + } > +}; > + > +module_platform_driver(aspeed_adc_driver); > + > +MODULE_AUTHOR("Rick Altherr "); > +MODULE_DESCRIPTION("Aspeed AST2400/2500 ADC Driver"); > +MODULE_LICENSE("GPL"); > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH] iio/adc/ltc2497: Driver for Linear Technology LTC2497 ADC
97_info = { > + .read_raw = ltc2497_read_raw, > + .driver_module = THIS_MODULE, > +}; > + > +static int ltc2497_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + struct iio_dev *indio_dev; > + struct ltc2497_st *st; > + int ret; > + > + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | > + I2C_FUNC_SMBUS_WRITE_BYTE)) > + return -EOPNOTSUPP; > + > + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st)); > + if (!indio_dev) > + return -ENOMEM; > + > + st = iio_priv(indio_dev); > + i2c_set_clientdata(client, indio_dev); > + st->client = client; > + > + indio_dev->dev.parent = &client->dev; > + indio_dev->name = id->name; > + indio_dev->info = <c2497_info; > + indio_dev->modes = INDIO_DIRECT_MODE; > + indio_dev->channels = ltc2497_channel; > + indio_dev->num_channels = ARRAY_SIZE(ltc2497_channel); > + > + st->ref = devm_regulator_get(&client->dev, "vref"); > + if (IS_ERR(st->ref)) > + return PTR_ERR(st->ref); > + > + ret = regulator_enable(st->ref); > + if (ret < 0) > + return ret; > + > + ret = i2c_smbus_write_byte(st->client, LTC2497_CONFIG_DEFAULT); > + if (ret < 0) > + goto err_regulator_disable; > + > + st->addr_prev = LTC2497_CONFIG_DEFAULT; > + st->time_prev = ktime_get(); > + > + ret = devm_iio_device_register(&client->dev, indio_dev); no devm_ since we have a non-empty _remove()? > + if (ret < 0) > + goto err_regulator_disable; > + > + return 0; > + > +err_regulator_disable: > + regulator_disable(st->ref); > + > + return ret; > +} > + > +static int ltc2497_remove(struct i2c_client *client) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(client); > + struct ltc2497_st *st = iio_priv(indio_dev); > + > + regulator_disable(st->ref); > + > + return 0; > +} > + > +static const struct i2c_device_id ltc2497_id[] = { > + { "ltc2497", 0 }, > + { } > +}; > +MODULE_DEVICE_TABLE(i2c, ltc2497_id); > + > +static const struct of_device_id ltc2497_of_match[] = { > + { .compatible = "lltc,ltc2497", }, > + {}, > +}; > +MODULE_DEVICE_TABLE(of, ltc2497_of_match); > + > +static struct i2c_driver ltc2497_driver = { > + .driver = { > + .name = "ltc2497", > + .of_match_table = of_match_ptr(ltc2497_of_match), > + }, > + .probe = ltc2497_probe, > + .remove = ltc2497_remove, > + .id_table = ltc2497_id, > +}; > +module_i2c_driver(ltc2497_driver); > + > +MODULE_AUTHOR("Michael Hennerich "); > +MODULE_DESCRIPTION("Linear Technology LTC2497 ADC driver"); > +MODULE_LICENSE("GPL v2"); > -- Peter Meerwald-Stadler Mobile: +43 664 24 44 418
Re: [PATCH 1/3] iio: humidity: add sht21 relative humidity and temperature sensor driver
eturn ret; > + > + mutex_lock(&state->lock); > + ret = sht21_set_resolution(state, i); > + mutex_unlock(&state->lock); > + > + iio_device_release_direct_mode(indio_dev); > + > + return ret; > + default: > + return -EINVAL; > + } > +} > + > +static ssize_t sht21_show_heater(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct iio_dev *indio_dev = dev_to_iio_dev(dev); > + struct sht21_state *state = iio_priv(indio_dev); > + int data, ret; > + > + ret = sht21_read_data(state, SHT21_READ_USER_REG, &data); > + if (ret) > + return ret; > + > + return sprintf(buf, "%d\n", !!(data & SHT21_USER_REG_ENABLE_HEATER)); > +} > + > +static ssize_t sht21_write_heater(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t len) > +{ > + struct iio_dev *indio_dev = dev_to_iio_dev(dev); > + struct sht21_state *state = iio_priv(indio_dev); > + int val, data, ret; > + > + ret = kstrtoint(buf, 10, &val); > + if (ret) > + return ret; > + if (val != 0 && val != 1) > + return -EINVAL; > + > + mutex_lock(&state->lock); > + ret = sht21_read_data(state, SHT21_READ_USER_REG, &data); > + if (ret) { > + mutex_unlock(&state->lock); > + return ret; > + } > + > + if (val) > + data |= SHT21_USER_REG_ENABLE_HEATER; > + else > + data &= ~SHT21_USER_REG_ENABLE_HEATER; > + > + ret = sht21_write_data(state, SHT21_WRITE_USER_REG, &data); > + mutex_unlock(&state->lock); > + > + return ret ? ret : len; > +} > + > +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("0.88 1.93 2.28 3.85"); > +static IIO_DEVICE_ATTR(heater_enable, S_IRUGO | S_IWUSR, > +sht21_show_heater, sht21_write_heater, 0); > + > +static struct attribute *sht21_attributes[] = { > + &iio_const_attr_sampling_frequency_available.dev_attr.attr, > + &iio_dev_attr_heater_enable.dev_attr.attr, > + NULL > +}; > + > +static const struct attribute_group sht21_attribute_group = { > + .attrs = sht21_attributes, > +}; > + > +static const struct iio_info sht21_info = { > + .driver_module = THIS_MODULE, > + .read_raw = sht21_read_raw, > + .write_raw = sht21_write_raw, > + .attrs = &sht21_attribute_group, > +}; > + > +#define SHT21_CHANNEL(_type, _index) { \ > + .type = _type, \ > + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ > + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ > + .scan_index = _index, \ > + .scan_type = { \ > + .sign = 's', \ > + .realbits = 32, \ > + .storagebits = 32, \ > + .endianness = IIO_CPU, \ > + }, \ > +} > + > +static const struct iio_chan_spec sht21_channels[] = { > + SHT21_CHANNEL(IIO_HUMIDITYRELATIVE, 0), > + SHT21_CHANNEL(IIO_TEMP, 1), > + IIO_CHAN_SOFT_TIMESTAMP(2), > +}; > + > +static int sht21_probe(struct i2c_client *client, const struct i2c_device_id > *id) > +{ > + struct iio_dev *indio_dev; > + struct sht21_state *data; > + int ret; > + > + if (!i2c_check_functionality(client->adapter, > + I2C_FUNC_SMBUS_WRITE_BYTE | > + I2C_FUNC_SMBUS_BYTE_DATA | > + I2C_FUNC_SMBUS_READ_I2C_BLOCK)) > + return -ENODEV; > + > + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); > + if (!indio_dev) > + return -ENOMEM; > + > + data = iio_priv(indio_dev); > + i2c_set_clientdata(client, indio_dev); > + data->client = client; > + mutex_init(&data->lock); > + > + indio_dev->dev.parent = &client->dev; > + indio_dev->name = id->name; > + indio_dev->modes = INDIO_DIRECT_MODE; > + indio_dev->info = &sht21_info; > + indio_dev->channels = sht21_channels; > + indio_dev->num_channels = ARRAY_SIZE(sht21_channels); > + > + ret = sht21_soft_reset(data); > + if (ret) > + return ret; > + > + ret = iio_triggered_buffer_setup(indio_dev, NULL, > + sht21_trigger_handler, NULL); > + if (ret) > + return ret; > + > + ret = iio_device_register(indio_dev); > + if (ret) > + iio_triggered_buffer_cleanup(indio_dev); > + > + return ret; > +} > + > +static int sht21_remove(struct i2c_client *client) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(client); > + > + iio_device_unregister(indio_dev); > + iio_triggered_buffer_cleanup(indio_dev); > + > + return 0; > +} > + > +static const struct i2c_device_id sht21_id[] = { > + { "sht21", 0 }, > + { } > +}; > +MODULE_DEVICE_TABLE(i2c, sht21_id); > + > +static const struct of_device_id sht21_of_match[] = { > + { .compatible = "sensirion,sht21" }, > + { } > +}; > +MODULE_DEVICE_TABLE(of, sht21_of_match); > + > +static struct i2c_driver sht21_driver = { > + .driver = { > + .name = SHT21_DRV_NAME, > + .of_match_table = of_match_ptr(sht21_of_match), > + }, > + .id_table = sht21_id, > + .probe = sht21_probe, > + .remove = sht21_remove, > +}; > +module_i2c_driver(sht21_driver); > + > +MODULE_DESCRIPTION("Sensirion SHT21 relative humidity and temperature sensor > driver"); > +MODULE_AUTHOR("Tomasz Duszynski "); > +MODULE_LICENSE("GPL v2"); > -- > 2.11.1 > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH 2/4] iio: chemical: add driver for dsm501/ppd42ns particle sensors
oncentration) > + * can be approximated by following polynomial: > + * > + * p(r) = 3844.2r^3 - 16201.3r^2 + 1848746.1r + 52497.2 > + * > + * Note: Result is in pcs/m3. To convert to pcs/0.01cf multiply > + *by 0.0002831685. > + */ > +static int ppd42ns_number_concentration(struct dsm501_data *data) > +{ > + s64 retval, r3, r2, r = div64_s64(ktime_to_ns(data->low_time) * 100, > + ktime_to_ns(data->meas_time)); > + > + r2 = r * r; > + r3 = r2 * r; > + > + retval = 38442 * r3; > + retval -= 162013 * r2; > + retval += 18487461 * r; > + retval += 524972; > + > + return div_s64(retval, 10); > +} > + > +static irqreturn_t dsm501_irq(int irq, void *dev_id) > +{ > + struct dsm501_data *data = iio_priv(dev_id); > + int val = gpiod_get_value(data->gpio); > + ktime_t dt, ts = ktime_get(); > + > + if (ktime_to_ns(data->ts) == 0) { > + data->ts = ts; > + data->low_time = ktime_set(0, 0); > + } > + > + if (val) { > + dt = ktime_sub(ts, data->ts); > + data->low_time = ktime_add(data->low_time, dt); > + } else { > + data->ts = ts; > + } {} needed? > + > + return IRQ_HANDLED; > +} > + > +static int dsm501_read_raw(struct iio_dev *indio_dev, > +struct iio_chan_spec const *chan, int *val, > +int *val2, long mask) > +{ > + struct dsm501_data *data = iio_priv(indio_dev); > + struct device *dev = indio_dev->dev.parent; > + unsigned long irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; > + int ret; > + > + > + switch (mask) { > + case IIO_CHAN_INFO_PROCESSED: > + mutex_lock(&data->lock); > + data->ts = ktime_set(0, 0); > + > + ret = devm_request_irq(dev, data->irq, dsm501_irq, irqflags, > +DSM501_IRQ_NAME, indio_dev); why devm_()? if the irq is explicitly freed below? requesting the irq for every measurement seems weird even if it only happens every 30s > + if (ret) { > + dev_err(dev, "Failed to request interrupt %d\n", > data->irq); > + mutex_unlock(&data->lock); > + return ret; > + } > + > + msleep_interruptible(ktime_to_ms(data->meas_time)); > + devm_free_irq(dev, data->irq, indio_dev); > + > + *val = data->number_concentration(data); > + mutex_unlock(&data->lock); > + > + return IIO_VAL_INT; > + default: > + return -EINVAL; > + } > +} > + > +static const struct iio_info dsm501_info = { > + .driver_module = THIS_MODULE, > + .read_raw = dsm501_read_raw, > +}; > + > +static const struct iio_chan_spec dsm501_channels[] = { > + { > + .type = IIO_NUMBERCONCENTRATION, > + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), > + }, > +}; > + > +static int dsm501_probe(struct platform_device *pdev) > +{ > + struct dsm501_data *data; > + struct iio_dev *indio_dev; > + struct device *dev = &pdev->dev; > + > + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data)); > + if (!indio_dev) > + return -ENOMEM; > + > + data = iio_priv(indio_dev); > + platform_set_drvdata(pdev, indio_dev); > + > + data->gpio = devm_gpiod_get_index(dev, NULL, 0, GPIOD_IN); > + if (IS_ERR(data->gpio)) { > + dev_err(dev, "Failed to get GPIO\n"); > + return PTR_ERR(data->gpio); > + } > + > + data->irq = gpiod_to_irq(data->gpio); > + if (data->irq < 0) { > + dev_err(dev, "GPIO has no interrupt\n"); > + return data->irq; > + } > + > + data->meas_time = ktime_set(DSM501_DEFAULT_MEASUREMENT_TIME, 0); > + data->number_concentration = of_device_get_match_data(dev); > + mutex_init(&data->lock); > + > + indio_dev->name = DSM501_DRV_NAME; > + indio_dev->dev.parent = dev; > + indio_dev->info = &dsm501_info; > + indio_dev->modes = INDIO_DIRECT_MODE; > + indio_dev->channels = dsm501_channels; > + indio_dev->num_channels = ARRAY_SIZE(dsm501_channels); > + > + return devm_iio_device_register(&pdev->dev, indio_dev); > +} > + > +static const struct of_device_id dsm501_id[] = { > + { > + .compatible = "samyoung,dsm501", > + .data = dsm501_number_concentartion, type: concentration > + }, > + { > + .compatible = "shinyei,ppd42ns", > + .data = ppd42ns_number_concentration, > + }, > + { } > +}; > +MODULE_DEVICE_TABLE(of, dsm501_id); > + > +static struct platform_driver dsm501_driver = { > + .driver = { > + .name = DSM501_DRV_NAME, > + .of_match_table = of_match_ptr(dsm501_id) > + }, > + .probe = dsm501_probe > +}; > +module_platform_driver(dsm501_driver); > + > +MODULE_AUTHOR("Tomasz Duszynski "); > +MODULE_DESCRIPTION("Samyoung DSM501 particle sensor driver"); > +MODULE_LICENSE("GPL v2"); > -- Peter Meerwald-Stadler +43-664-218 (mobile)
Re: [PATCH 1/4] iio: chemical: add particle number concentration channel type
> This patch adds channel type for expressing particle number > concentration. numberconcentration sound a bit strange to be, more like a number theoretic concept how about particlecontentration? or numberparticles? > Signed-off-by: Tomasz Duszynski > --- > Documentation/ABI/testing/sysfs-bus-iio | 7 +++ > drivers/iio/industrialio-core.c | 1 + > include/uapi/linux/iio/types.h | 1 + > 3 files changed, 9 insertions(+) > > diff --git a/Documentation/ABI/testing/sysfs-bus-iio > b/Documentation/ABI/testing/sysfs-bus-iio > index 530809ccfacf..481771e585ee 100644 > --- a/Documentation/ABI/testing/sysfs-bus-iio > +++ b/Documentation/ABI/testing/sysfs-bus-iio > @@ -1608,3 +1608,10 @@ Description: > provides an absolute positional reference (e.g. a pulse once per > revolution) which may be used to home positional systems as > required. > + > +What: > /sys/bus/iio/devices/iio:deviceX/in_numberconcentration_input > +KernelVersion: 4.12 > +Contact: linux-...@vger.kernel.org > +Description: > + Reading of particulates number in a volume of a mixture. > + Unit is in 1/m3. > diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c > index d18ded45bedd..451e3bb8d623 100644 > --- a/drivers/iio/industrialio-core.c > +++ b/drivers/iio/industrialio-core.c > @@ -85,6 +85,7 @@ static const char * const iio_chan_type_name_spec[] = { > [IIO_COUNT] = "count", > [IIO_INDEX] = "index", > [IIO_GRAVITY] = "gravity", > + [IIO_NUMBERCONCENTRATION] = "numberconcentration", > }; > > static const char * const iio_modifier_names[] = { > diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h > index ffafd6c25a48..52696e251c4d 100644 > --- a/include/uapi/linux/iio/types.h > +++ b/include/uapi/linux/iio/types.h > @@ -43,6 +43,7 @@ enum iio_chan_type { > IIO_COUNT, > IIO_INDEX, > IIO_GRAVITY, > + IIO_NUMBERCONCENTRATION, > }; > > enum iio_modifier { > -- Peter Meerwald-Stadler +43-664-218 (mobile)
atmel_ssc_dai, duplex audio support?
Hello, it appears that atmel_ssc_dai does not support duplex audio, i.e. when first starting a playback stream and then starting a record stream, the playback stops problem seems to be in atmel_ssc_startup(): /* Reset the SSC to keep it at a clean status */ ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST)); which is performed unconditionally when a stream starts code was introduced with ASoC: atmel_ssc_dai: refactor the startup and shutdown, commit cbaadf0f90d6 tested with the 4.1 branch of linux-at91 git thanks, regards, p. -- Peter Meerwald-Stadler +43-664-218 (mobile)