Accelerometer driver for STMicroeletronics-LIS331DL-three-axis-digital

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Trisal, Kalhan wrote:
> Thanks Jonathan.
>   Please find my comments inline.
Hi Kalhan,

Few responses below.  For reference, IIO (which might be a more
suitable location than hwmon) is going to go into the staging tree shortly.

If you are interested, I'd be happy to do a conversion if you'd be willing
to test against the hardware.  To support the current functionality should
be straight forward

...
>> +static unsigned int i2c_write_current_data(struct i2c_client *client,
>> +                                     unsigned int reg, unsigned int value)
>> +{
>> +     int ret_val;
>> +
>> +     ret_val = i2c_smbus_write_byte_data(client, reg, value);
>> +     return ret_val;
>> +}
> Why bother with above. Doesn't seem to being anything?  Presumably
> it was to allow for later inclusion of spi support, but as is you
> might as well call the smbus command directly.
> 
> 
>>> yes can be done but for future support/enhancement  let's keep it as modular as possible.
But it's not modular unless you make it take the say a struct acclero_data (with the i2c_client
as a member).  Thus when you add spi support, you can use function pointers to call the relevant
write function.  As is, it adds a module that doesn't do anything.

...
> 
> Not clear what state is refering to. Looking at the data sheet I
> can see that it is power down control. Do you want userspace access
> to this, or should it be done via the fine grained power management
> stuff that is working its way into the kernel?
> 
>>> in mids the Apps. manager are intelligent to check if no apps are going to use the device they can power down the device,
>>> power management can do this, but we decided that exposing this to app manager will be more effective as we can put the
>>> device in active mode only when needed.
There's been some recent work on runtime power management.
I've only been vaguely following it, but I believe the intention is to allow
for putting individual devices to sleep much as you are here.

http://lkml.org/lkml/2009/8/9/128

Having read the above posting, it appears that the i2c bus may need
support for this as well.  Perhaps your state sysfs interface
is the best way to go in the meantime.

> 
> Up to you whether you do a single access point for all 3 readings (scan
> across them) or individual reads.  Guess which makes sense depends on the
> application.
> 
>>> thought about this but apps want all the data at a given time stamp, this also many not be accurate but by calling individual x,y,z
>>> this seems to be more accurate at given time stamp what is the current position.
Make sense, though the average dma based i2c controller is going to make
knowing when a transfer occured complex, particularly with the 3 separate
transfers used below.
 
>> +static ssize_t xyz_pos_show(struct device *dev,
>> +                             struct device_attribute *attr, char *buf)
>> +{
>> +     int x, y, z;
>> +     struct i2c_client *client = to_i2c_client(dev);
> I haven't read the data sheet thoroughly, but is the device capable of
> doing more complex reads? (would be nice to cut down the bus load
> of this given the device is running at maybe 400 Hz).
> Table 14 of the data sheet seems to hint this can be done.
> I guess it would also be dependant on what your i2c adapter supports.
> 
>>> Agree It is adapter dependent, will put the note here saying that if the adapter support multiple
>>> register read simultaneously then that also can be performed.
That's fair enough.
>> +     x = i2c_smbus_read_byte_data(client, 0x29);
>> +     y = i2c_smbus_read_byte_data(client, 0x2B);
>> +     z = i2c_smbus_read_byte_data(client, 0x2D);
>> +     return sprintf(buf, "%d, %d, %d \n", x, y, z);
>> +}
>> +
>> +static ssize_t rate_store(struct device *dev,
>> +             struct device_attribute *attr, const char *buf, size_t count)
>> +{
>> +     struct i2c_client *client = to_i2c_client(dev);
>> +     struct acclero_data *data = i2c_get_clientdata(client);
>> +     unsigned int ret_val, set_val;
>> +     unsigned long val;
>> +
>> +     if (strict_strtoul(buf, 10, &val))
>> +             return -EINVAL;
> Why bother locking for updates without protecting the reads as well?
> Given I'm guessing a given i2c write is atomic, I'm not sure you gain
> anything.
> 
> 
>  > > will remove mutex
Might be better to just move it to cover the read as well.  Although I don't
think there are any cases here that could lead to an undesirable state
I haven't read the data sheet thoroughly enough to be sure there aren't
any that might be added in the future.

 
>> +     ret_val = i2c_smbus_read_byte_data(client, 0x20);
>> +
>> +     mutex_lock(&data->update_lock);
>> +     if (val == ACCEL_DATA_RATE_100HZ)
>> +             set_val = (ret_val & 0x7F); /* setting the 8th bit to 0 */
>> +     else if (val == ACCEL_DATA_RATE_400HZ)
>> +             set_val = (ret_val | (1 << 7));
>> +     else
>> +             goto invarg;
>> +
>> +     i2c_write_current_data(client, 0x20, set_val);
>> +     mutex_unlock(&data->update_lock);
>> +     return count;
>> +invarg:
>> +     mutex_unlock(&data->update_lock);
>> +     return -EINVAL;
>> +}

>> +static ssize_t state_store(struct device *dev,
>> +             struct device_attribute *attr, const  char *buf, size_t count)
>> +{
>> +     struct i2c_client *client = to_i2c_client(dev);
>> +     struct acclero_data *data = i2c_get_clientdata(client);
>> +     unsigned int ret_val, set_val;
>> +     unsigned long val;
>> +
>> +     if (strict_strtoul(buf, 10, &val))
>> +             return -EINVAL;
>> +     ret_val = i2c_smbus_read_byte_data(client, 0x20);
>> +
>> +     mutex_lock(&data->update_lock);
>> +     if (val == ACCEL_POWER_MODE_DOWN)
>> +             set_val = ret_val & 0xBF; /* if value id 0 */
>> +     else if (val == ACCEL_POWER_MODE_ACTIVE)
>> +             set_val = (ret_val | (1<<6)); /* if value is 1 */
>> +     else
>> +             goto invarg;
>> +
>> +     i2c_write_current_data(client, 0x20, set_val);
>> +     mutex_unlock(&data->update_lock);
>> +     return count;
>> +invarg:
>> +     mutex_unlock(&data->update_lock);
>> +     return -EINVAL;
>> +}
> 
> Why do you want this exposed to userspace? To my reading
> the primary purpose in doing this is to reset any changes
> to the various calibrated parameters. Exporting to userspace
> might make sense if you were also exporting interfaces for them
> (HPcoeff for example) Even then, I'm not sure providing access
> to this isn't more confusing than ideal.
> 
>>> this is exposed to user space if the application manager is sensing that the values
>>> from the device are inconsistent, then best way is to reboot the device. This option is
>>> only for sensor application manager. I felt it will be useful when device misbehaves and can be rectified
>>> dynamically by rebooting the device.
Does this actually happen?  If so definitely worth adding some documentation
of the circumstances as this would be an out and out hardware bug and might
trip other people up.  In general I'd be loath to have such a command exported
to userspace, but if it's needed to work round a hardware problem, fair enough.
I'd have thought the principle reason for having the support in the hardware
was to ensure a known consistent state on driver load rather than to fix problems
during use.
> 
>> +static ssize_t reboot_mem_store(struct device *dev,
>> +             struct device_attribute *attr, const char *buf, size_t count)
>> +{
>> +     struct i2c_client *client = to_i2c_client(dev);
>> +     struct acclero_data *data = i2c_get_clientdata(client);
>> +     unsigned int ret_val, set_val;
>> +     unsigned long val;
>> +
>> +     if (strict_strtoul(buf, 10, &val))
>> +             return -EINVAL;
>> +     ret_val = i2c_smbus_read_byte_data(client, 0x21);
>> +     if (val == ACCEL_MEMORY_REBOOT) {
>> +             mutex_lock(&data->update_lock);
>> +             set_val = (ret_val | (1 << 6)); /* setting the 6th  bit */
>> +             i2c_write_current_data(client, 0x21, set_val);
>> +             mutex_unlock(&data->update_lock);
>> +     } else
>> +             return -EINVAL;
>> +     return count;
>> +}
>> +
>> +static DEVICE_ATTR(data_rate, S_IRUGO | S_IWUSR, rate_show, rate_store);
>> +static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR, state_show, state_store);
>> +static DEVICE_ATTR(reboot_mem, S_IWUSR, NULL, reboot_mem_store);
>> +static DEVICE_ATTR(position, S_IRUGO, xyz_pos_show, NULL);
>> +
>> +static struct attribute *mid_att_acclero[] = {
>> +     &dev_attr_data_rate.attr,
>> +     &dev_attr_power_state.attr,
>> +     &dev_attr_reboot_mem.attr,
>> +     &dev_attr_position.attr,
>> +     NULL
>> +};
> This is a little odd, why have a device with an attribute group
> of the same name? I think this will lead to a somewhat unusual
> structure in sysfs.
> 
>>> I am not clear here, does it mean that we should rename it as accelerometer.
No, don't give it any name at all.  Then all the sysfs files should appear in 
/sys/class/hwmon/device0/device/ rather than adding an additional directory layer.
> 
>> +static struct attribute_group m_acclero_gr = {
>> +     .name = "lis331dl",
>> +     .attrs = mid_att_acclero
>> +};
...



[Index of Archives]     [Linux Kernel]     [Linux Hardware Monitoring]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux