On 25/10/2023 15:44, marius.cristea@xxxxxxxxxxxxx wrote: > From: Marius Cristea <marius.cristea@xxxxxxxxxxxxx> > > This is the iio driver for Microchip > PAC193X series of Power Monitor with Accumulator chip family. > > Signed-off-by: Marius Cristea <marius.cristea@xxxxxxxxxxxxx> > --- ... > + > +static ssize_t reset_accumulators_store(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + struct iio_dev *indio_dev = dev_to_iio_dev(dev); > + struct pac1934_chip_info *info = iio_priv(indio_dev); > + int ret, i; > + u8 refresh_cmd = PAC1934_REFRESH_REG_ADDR; > + > + ret = i2c_smbus_write_byte(info->client, refresh_cmd); > + if (ret) { > + dev_err(&indio_dev->dev, > + "%s - cannot send 0x%02X\n", > + __func__, refresh_cmd); > + } > + > + for (i = 0 ; i < info->phys_channels; i++) > + info->chip_reg_data.energy_sec_acc[i] = 0; > + > + return count; > +} > + > +static IIO_DEVICE_ATTR(in_shunt_resistor_1, 0644, shunt_value_show, shunt_value_store, 0); > +static IIO_DEVICE_ATTR(in_shunt_resistor_2, 0644, shunt_value_show, shunt_value_store, 0); > +static IIO_DEVICE_ATTR(in_shunt_resistor_3, 0644, shunt_value_show, shunt_value_store, 0); > +static IIO_DEVICE_ATTR(in_shunt_resistor_4, 0644, shunt_value_show, shunt_value_store, 0); > +static IIO_DEVICE_ATTR(reset_accumulators, 0200, NULL, reset_accumulators_store, 0); > + > +static struct attribute *pac1934_all_attributes[] = { > + PAC1934_DEV_ATTR(in_shunt_resistor_1), > + PAC1934_DEV_ATTR(in_shunt_resistor_2), > + PAC1934_DEV_ATTR(in_shunt_resistor_3), > + PAC1934_DEV_ATTR(in_shunt_resistor_4), > + PAC1934_DEV_ATTR(reset_accumulators), > + NULL > +}; > + > +static int pac1934_prep_custom_attributes(struct pac1934_chip_info *info, > + struct iio_dev *indio_dev) > +{ > + int i, j, active_channels_count = 0; > + struct attribute **pac1934_custom_attributes; > + struct attribute_group *pac1934_group; > + struct i2c_client *client = info->client; > + > + for (i = 0 ; i < info->phys_channels; i++) > + if (info->active_channels[i]) > + active_channels_count++; > + > + pac1934_group = devm_kzalloc(&client->dev, sizeof(*pac1934_group), GFP_KERNEL); > + > + pac1934_custom_attributes = devm_kzalloc(&client->dev, > + (PAC1934_CUSTOM_ATTR_FOR_CHANNEL * > + active_channels_count + > + PAC1934_SHARED_DEVATTRS_COUNT) > + * sizeof(*pac1934_group) + 1, > + GFP_KERNEL); > + j = 0; > + > + for (i = 0 ; i < info->phys_channels; i++) { > + if (info->active_channels[i]) { > + pac1934_custom_attributes[PAC1934_CUSTOM_ATTR_FOR_CHANNEL * j] = > + pac1934_all_attributes[PAC1934_CUSTOM_ATTR_FOR_CHANNEL * i]; > + pac1934_custom_attributes[PAC1934_CUSTOM_ATTR_FOR_CHANNEL * j + 1] = > + pac1934_all_attributes[PAC1934_CUSTOM_ATTR_FOR_CHANNEL * i + 1]; > + j++; > + } > + } > + > + for (i = 0; i < PAC1934_SHARED_DEVATTRS_COUNT; i++) > + pac1934_custom_attributes[PAC1934_CUSTOM_ATTR_FOR_CHANNEL * > + active_channels_count + i] = > + pac1934_all_attributes[PAC1934_CUSTOM_ATTR_FOR_CHANNEL * > + info->phys_channels + i]; > + > + pac1934_group->attrs = pac1934_custom_attributes; > + info->pac1934_info.attrs = pac1934_group; > + > + return 0; > +} > + > +static void pac1934_remove(struct i2c_client *client) Remove functions goes always after probe. > +{ > + struct iio_dev *indio_dev = dev_get_drvdata(&client->dev); > + struct pac1934_chip_info *info = iio_priv(indio_dev); > + > + cancel_delayed_work_sync(&info->work_chip_rfsh); > +} > + > +static int pac1934_probe(struct i2c_client *client) > +{ > + struct pac1934_chip_info *info; > + struct iio_dev *indio_dev; > + const char *name = NULL; > + int cnt, ret; > + > + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*info)); > + if (!indio_dev) > + return -ENOMEM; > + > + info = iio_priv(indio_dev); > + > + i2c_set_clientdata(client, indio_dev); > + info->client = client; > + > + /* > + * load default settings - all channels disabled, > + * uni directional flow > + */ > + for (cnt = 0; cnt < PAC1934_MAX_NUM_CHANNELS; cnt++) { > + info->active_channels[cnt] = false; > + info->bi_dir[cnt] = false; > + } > + > + info->crt_samp_spd_bitfield = PAC1934_SAMP_1024SPS; > + > + ret = pac1934_chip_identify(info); > + if (ret) > + return -EINVAL; > + > + if (ACPI_HANDLE(&client->dev)) { > + if (!info->phys_channels) > + /* failed to identify part number, unknown number of channels available */ > + return -EINVAL; > + > + name = pac1934_match_acpi_device(client, info); > + } else { > + name = pac1934_match_of_device(client, info); > + } > + > + if (!name) { > + dev_dbg(&client->dev, "parameter parsing returned an error\n"); > + return -EINVAL; > + } > + > + mutex_init(&info->lock); > + > + /* > + * do now any chip specific initialization (e.g. read/write > + * some registers), enable/disable certain channels, change the sampling > + * rate to the requested value > + */ > + ret = pac1934_chip_configure(info); > + if (ret < 0) > + goto fail; Why do you need to go to fail here? Is the work scheduled in error cases? > + > + /* prepare the channel information */ > + ret = pac1934_prep_iio_channels(info, indio_dev); > + if (ret < 0) > + goto fail; > + > + ret = pac1934_prep_custom_attributes(info, indio_dev); > + if (ret < 0) { > + dev_err_probe(&indio_dev->dev, ret, > + "Can't configure custom attributes for PAC1934 device\n"); > + goto fail; > + } > + > + info->pac1934_info.read_raw = pac1934_read_raw; > + info->pac1934_info.read_avail = pac1934_read_avail; > + info->pac1934_info.write_raw = pac1934_write_raw; > + info->pac1934_info.read_label = pac1934_read_label; > + > + indio_dev->info = &info->pac1934_info; > + indio_dev->name = name; > + indio_dev->modes = INDIO_DIRECT_MODE; > + > + /* > + * read whatever has been accumulated in the chip so far > + * and reset the accumulators > + */ > + ret = pac1934_reg_snapshot(info, true, PAC1934_REFRESH_REG_ADDR, > + PAC1934_MIN_UPDATE_WAIT_TIME_US); > + if (ret < 0) > + goto fail; > + > + ret = devm_iio_device_register(&client->dev, indio_dev); > + if (ret < 0) { > + dev_err_probe(&indio_dev->dev, ret, > + "Can't register IIO device\n"); > + goto fail; > + } > + > + return 0; > + > +fail: > + cancel_delayed_work_sync(&info->work_chip_rfsh); > + > + return ret; > +} > + > +static const struct i2c_device_id pac1934_id[] = { > + { .name = "pac1931", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1931] }, > + { .name = "pac1932", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1932] }, > + { .name = "pac1933", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1933] }, > + { .name = "pac1934", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1934] }, > + {} > +}; > + > +MODULE_DEVICE_TABLE(i2c, pac1934_id); > + > +static const struct of_device_id pac1934_of_match[] = { > + { > + .compatible = "microchip,pac1931", > + .data = &pac1934_chip_config[PAC1931] > + }, > + { > + .compatible = "microchip,pac1932", > + .data = &pac1934_chip_config[PAC1932] > + }, > + { > + .compatible = "microchip,pac1933", > + .data = &pac1934_chip_config[PAC1933] > + }, > + { > + .compatible = "microchip,pac1934", > + .data = &pac1934_chip_config[PAC1934] > + }, > + {} > +}; > + > +MODULE_DEVICE_TABLE(of, pac1934_of_match); > + > +/* using MCHP1930 to be compatible with WINDOWS ACPI */ > +static const struct acpi_device_id pac1934_acpi_match[] = { > + {"MCHP1930", 0}, > + { } > +}; > + > +MODULE_DEVICE_TABLE(acpi, pac1934_acpi_match); > + > +static struct i2c_driver pac1934_driver = { > + .driver = { > + .name = "pac1934", > + .of_match_table = pac1934_of_match, > + .acpi_match_table = ACPI_PTR(pac1934_acpi_match) Drop ACPI_PTR, causes warnings. Best regards, Krzysztof