This patch adds two new sysfs files, bus_frequency and bus_supported_frequencies which allows the user to view or change the bus frequency on a per bus level. This is required for e.g. USB I2C buses where we can have multiple device plugged in a system and where a module parameter does not allow controlling the bus speed individually. Signed-off-by: Octavian Purdila <octavian.purdila@xxxxxxxxx> --- Documentation/ABI/testing/sysfs-bus-i2c | 24 ++++++++++ drivers/i2c/i2c-core.c | 77 +++++++++++++++++++++++++++++++++ include/linux/i2c.h | 10 +++++ 3 files changed, 111 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-i2c b/Documentation/ABI/testing/sysfs-bus-i2c index 22c621a..058ac68 100644 --- a/Documentation/ABI/testing/sysfs-bus-i2c +++ b/Documentation/ABI/testing/sysfs-bus-i2c @@ -43,3 +43,27 @@ Contact: linux-i2c@xxxxxxxxxxxxxxx Description: An i2c device attached to bus X that is enumerated via ACPI. Y is the ACPI device name and its format is "%s". + +What: /sys/bus/i2c/devices/i2c-X/bus_frequency +KernelVersion: 3.19 +Contact: linux-i2c@xxxxxxxxxxxxxxx +Description: + The current bus frequency for bus X. Can be updated if + the bus supports it. The unit is HZ and format is + "%d\n". + If the bus does not support showing/changing the + frequency then reading/writting to this entry will + fail with -EOPNOTSUPP. + When updating the bus frequency that value must be one + of the values in bus_supported_frequencies otherwise + writting will fail with -EINVAL. + +What: /sys/bus/i2c/devices/i2c-X/bus_supported_frequencies +KernelVersion: 3.19 +Contact: linux-i2c@xxxxxxxxxxxxxxx +Description: + Supported frequencies for bus X. The unit is HZ and + format is "%d %d %d ... %d\n". + If the bus does not support showing/changing the + frequency then reading to this entry will fail with + -EOPNOTSUPP. diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 632057a..32b918a 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -940,10 +940,87 @@ static DEVICE_ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device); static DEVICE_ATTR_IGNORE_LOCKDEP(delete_device, S_IWUSR, NULL, i2c_sysfs_delete_device); +static ssize_t +i2c_sysfs_freq_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct i2c_adapter *adap = to_i2c_adapter(dev); + + if (!adap->freq) + return -EOPNOTSUPP; + + return snprintf(buf, PAGE_SIZE, "%d\n", adap->freq); +} + +static ssize_t +i2c_sysfs_freq_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_adapter *adap = to_i2c_adapter(dev); + unsigned int freq; + int ret; + int i; + + if (!adap->set_freq) + return -EOPNOTSUPP; + + if (kstrtouint(buf, 0, &freq)) + return -EINVAL; + + if (freq == 0) + return -EINVAL; + + for (i = 0; i < I2C_MAX_FREQS && adap->supported_freqs[i]; i++) + if (adap->supported_freqs[i] >= freq) + break; + + if (adap->supported_freqs[i] != freq) + return -EINVAL; + + i2c_lock_adapter(adap); + ret = adap->set_freq(adap, freq); + i2c_unlock_adapter(adap); + + if (ret) + return ret; + + adap->freq = freq; + + return count; +} + +static DEVICE_ATTR(bus_frequency, S_IWUSR | S_IRUGO, i2c_sysfs_freq_show, + i2c_sysfs_freq_store); + + +static ssize_t +i2c_sysfs_supp_freqs_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct i2c_adapter *adap = to_i2c_adapter(dev); + ssize_t count = 0; + int i; + + if (!adap->supported_freqs[0]) + return -EOPNOTSUPP; + + for (i = 0; i < I2C_MAX_FREQS && adap->supported_freqs[i]; i++) + count += snprintf(buf + count, PAGE_SIZE - count, "%d ", + adap->supported_freqs[i]); + buf[count - 1] = '\n'; + + return count; +} + +static DEVICE_ATTR(bus_supported_frequencies, S_IRUGO, + i2c_sysfs_supp_freqs_show, NULL); + static struct attribute *i2c_adapter_attrs[] = { &dev_attr_name.attr, &dev_attr_new_device.attr, &dev_attr_delete_device.attr, + &dev_attr_bus_frequency.attr, + &dev_attr_bus_supported_frequencies.attr, NULL }; diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 36041e2..e410637 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -418,6 +418,8 @@ int i2c_recover_bus(struct i2c_adapter *adap); int i2c_generic_gpio_recovery(struct i2c_adapter *adap); int i2c_generic_scl_recovery(struct i2c_adapter *adap); +#define I2C_MAX_FREQS 16 + /** * struct i2c_adapter - represents an I2C physical bus * @@ -431,6 +433,10 @@ int i2c_generic_scl_recovery(struct i2c_adapter *adap); * usb->interface->dev, platform_device->dev etc.) * @name: name of this i2c bus * @bus_recovery_info: see struct i2c_bus_recovery_info. Optional. + * @supported_freqs: supported bus frequencies (in HZ). Must be sorted + * in ascending order. Optional. + * @freq: initial bus frequency. Optional. + * @set_bus_freq: set the bus frequency (in HZ). Optional. */ struct i2c_adapter { struct module *owner; @@ -438,6 +444,10 @@ struct i2c_adapter { const struct i2c_algorithm *algo; /* the algorithm to access the bus */ void *algo_data; + unsigned int supported_freqs[I2C_MAX_FREQS]; + unsigned int freq; + int (*set_freq)(struct i2c_adapter *a, unsigned int freq); + /* data fields that are valid for all devices */ struct rt_mutex bus_lock; -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-api" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html