diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 88f4306744c0..2aee41655ce9 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -346,7 +346,7 @@ void acpi_free_properties(struct acpi_device *adev) * * Return: %0 if property with @name has been found (success), * %-EINVAL if the arguments are invalid, - * %-ENODATA if the property doesn't exist, + * %-EINVAL if the property doesn't exist, * %-EPROTO if the property value type doesn't match @type. */ static int acpi_data_get_property(struct acpi_device_data *data, @@ -360,7 +360,7 @@ static int acpi_data_get_property(struct acpi_device_data *data, return -EINVAL; if (!data->pointer || !data->properties) - return -ENODATA; + return -EINVAL; properties = data->properties; for (i = 0; i < properties->package.count; i++) { @@ -375,13 +375,13 @@ static int acpi_data_get_property(struct acpi_device_data *data, if (!strcmp(name, propname->string.pointer)) { if (type != ACPI_TYPE_ANY && propvalue->type != type) return -EPROTO; - else if (obj) + if (obj) *obj = propvalue; return 0; } } - return -ENODATA; + return -EINVAL; } /** @@ -439,7 +439,7 @@ int acpi_node_prop_get(struct fwnode_handle *fwnode, const char *propname, * * Return: %0 if array property (package) with @name has been found (success), * %-EINVAL if the arguments are invalid, - * %-ENODATA if the property doesn't exist, + * %-EINVAL if the property doesn't exist, * %-EPROTO if the property is not a package or the type of its elements * doesn't match @type. */ diff --git a/drivers/base/core.c b/drivers/base/core.c index b7d56c5ea3c6..0a8bdade53f2 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -2261,7 +2261,10 @@ void set_primary_fwnode(struct device *dev, struct fwnode_handle *fwnode) if (fwnode_is_primary(fn)) fn = fn->secondary; - fwnode->secondary = fn; + if (fn) { + WARN_ON(fwnode->secondary); + fwnode->secondary = fn; + } dev->fwnode = fwnode; } else { dev->fwnode = fwnode_is_primary(dev->fwnode) ? diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 1dd6d3bf1098..d77ed0c946dd 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "base.h" #include "power/power.h" @@ -298,6 +299,22 @@ int platform_device_add_data(struct platform_device *pdev, const void *data, } EXPORT_SYMBOL_GPL(platform_device_add_data); +/** + * platform_device_add_properties - add built-in properties to a platform device + * @pdev: platform device to add properties to + * @pset: properties to add + * + * The function will take deep copy of the properties in @pset and attach + * the copy to the platform device. The memory associated with properties + * will be freed when the platform device is released. + */ +int platform_device_add_properties(struct platform_device *pdev, + const struct property_set *pset) +{ + return device_add_property_set(&pdev->dev, pset); +} +EXPORT_SYMBOL_GPL(platform_device_add_properties); + /** * platform_device_add - add a platform device to device hierarchy * @pdev: platform device we're adding @@ -409,6 +426,8 @@ void platform_device_del(struct platform_device *pdev) if (r->parent) release_resource(r); } + + device_remove_property_set(&pdev->dev); } } EXPORT_SYMBOL_GPL(platform_device_del); @@ -487,6 +506,12 @@ struct platform_device *platform_device_register_full( if (ret) goto err; + if (pdevinfo->pset) { + ret = platform_device_add_properties(pdev, pdevinfo->pset); + if (ret) + goto err; + } + ret = platform_device_add(pdev); if (ret) { err: diff --git a/drivers/base/property.c b/drivers/base/property.c index 1325ff225cc4..c359351d50f1 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -19,32 +19,14 @@ #include #include -/** - * device_add_property_set - Add a collection of properties to a device object. - * @dev: Device to add properties to. - * @pset: Collection of properties to add. - * - * Associate a collection of device properties represented by @pset with @dev - * as its secondary firmware node. - */ -void device_add_property_set(struct device *dev, struct property_set *pset) -{ - if (!pset) - return; - - pset->fwnode.type = FWNODE_PDATA; - set_secondary_fwnode(dev, &pset->fwnode); -} -EXPORT_SYMBOL_GPL(device_add_property_set); - -static inline bool is_pset(struct fwnode_handle *fwnode) +static inline bool is_pset_node(struct fwnode_handle *fwnode) { return fwnode && fwnode->type == FWNODE_PDATA; } -static inline struct property_set *to_pset(struct fwnode_handle *fwnode) +static inline struct property_set *to_pset_node(struct fwnode_handle *fwnode) { - return is_pset(fwnode) ? + return is_pset_node(fwnode) ? container_of(fwnode, struct property_set, fwnode) : NULL; } @@ -63,45 +45,135 @@ static struct property_entry *pset_prop_get(struct property_set *pset, return NULL; } -static int pset_prop_read_array(struct property_set *pset, const char *name, - enum dev_prop_type type, void *val, size_t nval) +static void *pset_prop_find(struct property_set *pset, const char *propname, + size_t length) { struct property_entry *prop; - unsigned int item_size; + void *pointer; - prop = pset_prop_get(pset, name); + prop = pset_prop_get(pset, propname); if (!prop) - return -ENODATA; + return ERR_PTR(-EINVAL); + if (prop->is_array) + pointer = prop->pointer.raw_data; + else + pointer = &prop->value.raw_data; + if (!pointer) + return ERR_PTR(-ENODATA); + if (length > prop->length) + return ERR_PTR(-EOVERFLOW); + return pointer; +} - if (prop->type != type) - return -EPROTO; +static int pset_prop_read_u8_array(struct property_set *pset, + const char *propname, + u8 *values, size_t nval) +{ + void *pointer; + size_t length = nval * sizeof(*values); - if (!val) - return prop->nval; + pointer = pset_prop_find(pset, propname, length); + if (IS_ERR(pointer)) + return PTR_ERR(pointer); - if (prop->nval < nval) - return -EOVERFLOW; + memcpy(values, pointer, length); + return 0; +} - switch (type) { - case DEV_PROP_U8: - item_size = sizeof(u8); - break; - case DEV_PROP_U16: - item_size = sizeof(u16); - break; - case DEV_PROP_U32: - item_size = sizeof(u32); - break; - case DEV_PROP_U64: - item_size = sizeof(u64); - break; - case DEV_PROP_STRING: - item_size = sizeof(const char *); - break; - default: +static int pset_prop_read_u16_array(struct property_set *pset, + const char *propname, + u16 *values, size_t nval) +{ + void *pointer; + size_t length = nval * sizeof(*values); + + pointer = pset_prop_find(pset, propname, length); + if (IS_ERR(pointer)) + return PTR_ERR(pointer); + + memcpy(values, pointer, length); + return 0; +} + +static int pset_prop_read_u32_array(struct property_set *pset, + const char *propname, + u32 *values, size_t nval) +{ + void *pointer; + size_t length = nval * sizeof(*values); + + pointer = pset_prop_find(pset, propname, length); + if (IS_ERR(pointer)) + return PTR_ERR(pointer); + + memcpy(values, pointer, length); + return 0; +} + +static int pset_prop_read_u64_array(struct property_set *pset, + const char *propname, + u64 *values, size_t nval) +{ + void *pointer; + size_t length = nval * sizeof(*values); + + pointer = pset_prop_find(pset, propname, length); + if (IS_ERR(pointer)) + return PTR_ERR(pointer); + + memcpy(values, pointer, length); + return 0; +} + +static int pset_prop_count_elems_of_size(struct property_set *pset, + const char *propname, size_t length) +{ + struct property_entry *prop; + + prop = pset_prop_get(pset, propname); + if (!prop) return -EINVAL; + + return prop->length / length; +} + +static int pset_prop_read_string_array(struct property_set *pset, + const char *propname, + const char **strings, size_t nval) +{ + void *pointer; + size_t length = nval * sizeof(*strings); + + pointer = pset_prop_find(pset, propname, length); + if (IS_ERR(pointer)) + return PTR_ERR(pointer); + + memcpy(strings, pointer, length); + return 0; +} + +static int pset_prop_read_string(struct property_set *pset, + const char *propname, const char **strings) +{ + struct property_entry *prop; + const char **pointer; + + prop = pset_prop_get(pset, propname); + if (!prop) + return -EINVAL; + if (!prop->is_string) + return -EILSEQ; + if (prop->is_array) { + pointer = prop->pointer.str; + if (!pointer) + return -ENODATA; + } else { + pointer = &prop->value.str; + if (*pointer && strnlen(*pointer, prop->length) >= prop->length) + return -EILSEQ; } - memcpy(val, prop->value.raw_data, nval * item_size); + + *strings = *pointer; return 0; } @@ -124,6 +196,18 @@ bool device_property_present(struct device *dev, const char *propname) } EXPORT_SYMBOL_GPL(device_property_present); +static bool __fwnode_property_present(struct fwnode_handle *fwnode, + const char *propname) +{ + if (is_of_node(fwnode)) + return of_property_read_bool(to_of_node(fwnode), propname); + else if (is_acpi_node(fwnode)) + return !acpi_node_prop_get(fwnode, propname, NULL); + else if (is_pset_node(fwnode)) + return !!pset_prop_get(to_pset_node(fwnode), propname); + return false; +} + /** * fwnode_property_present - check if a property of a firmware node is present * @fwnode: Firmware node whose property to check @@ -131,12 +215,12 @@ EXPORT_SYMBOL_GPL(device_property_present); */ bool fwnode_property_present(struct fwnode_handle *fwnode, const char *propname) { - if (is_of_node(fwnode)) - return of_property_read_bool(to_of_node(fwnode), propname); - else if (is_acpi_node(fwnode)) - return !acpi_node_prop_get(fwnode, propname, NULL); + bool ret; - return !!pset_prop_get(to_pset(fwnode), propname); + ret = __fwnode_property_present(fwnode, propname); + if (ret == false && fwnode && fwnode->secondary) + ret = __fwnode_property_present(fwnode->secondary, propname); + return ret; } EXPORT_SYMBOL_GPL(fwnode_property_present); @@ -309,25 +393,40 @@ int device_property_match_string(struct device *dev, const char *propname, } EXPORT_SYMBOL_GPL(device_property_match_string); -#define OF_DEV_PROP_READ_ARRAY(node, propname, type, val, nval) \ - (val) ? of_property_read_##type##_array((node), (propname), (val), (nval)) \ +#define OF_DEV_PROP_READ_ARRAY(node, propname, type, val, nval) \ + (val) ? of_property_read_##type##_array((node), (propname), (val), (nval)) \ : of_property_count_elems_of_size((node), (propname), sizeof(type)) -#define FWNODE_PROP_READ_ARRAY(_fwnode_, _propname_, _type_, _proptype_, _val_, _nval_) \ -({ \ - int _ret_; \ - if (is_of_node(_fwnode_)) \ - _ret_ = OF_DEV_PROP_READ_ARRAY(to_of_node(_fwnode_), _propname_, \ - _type_, _val_, _nval_); \ - else if (is_acpi_node(_fwnode_)) \ - _ret_ = acpi_node_prop_read(_fwnode_, _propname_, _proptype_, \ - _val_, _nval_); \ - else if (is_pset(_fwnode_)) \ - _ret_ = pset_prop_read_array(to_pset(_fwnode_), _propname_, \ - _proptype_, _val_, _nval_); \ - else \ - _ret_ = -ENXIO; \ - _ret_; \ +#define PSET_PROP_READ_ARRAY(node, propname, type, val, nval) \ + (val) ? pset_prop_read_##type##_array((node), (propname), (val), (nval)) \ + : pset_prop_count_elems_of_size((node), (propname), sizeof(type)) + +#define FWNODE_PROP_READ(_fwnode_, _propname_, _type_, _proptype_, _val_, _nval_) \ +({ \ + int _ret_; \ + if (is_of_node(_fwnode_)) \ + _ret_ = OF_DEV_PROP_READ_ARRAY(to_of_node(_fwnode_), _propname_, \ + _type_, _val_, _nval_); \ + else if (is_acpi_node(_fwnode_)) \ + _ret_ = acpi_node_prop_read(_fwnode_, _propname_, _proptype_, \ + _val_, _nval_); \ + else if (is_pset_node(_fwnode_)) \ + _ret_ = PSET_PROP_READ_ARRAY(to_pset_node(_fwnode_), _propname_, \ + _type_, _val_, _nval_); \ + else \ + _ret_ = -ENXIO; \ + _ret_; \ +}) + +#define FWNODE_PROP_READ_ARRAY(_fwnode_, _propname_, _type_, _proptype_, _val_, _nval_) \ +({ \ + int _ret_; \ + _ret_ = FWNODE_PROP_READ(_fwnode_, _propname_, _type_, _proptype_, \ + _val_, _nval_); \ + if (_ret_ == -EINVAL && _fwnode_ && _fwnode_->secondary) \ + _ret_ = FWNODE_PROP_READ(_fwnode_->secondary, _propname_, _type_, \ + _proptype_, _val_, _nval_); \ + _ret_; \ }) /** @@ -434,6 +533,41 @@ int fwnode_property_read_u64_array(struct fwnode_handle *fwnode, } EXPORT_SYMBOL_GPL(fwnode_property_read_u64_array); +static int __fwnode_property_read_string_array(struct fwnode_handle *fwnode, + const char *propname, + const char **val, size_t nval) +{ + if (is_of_node(fwnode)) + return val ? + of_property_read_string_array(to_of_node(fwnode), + propname, val, nval) : + of_property_count_strings(to_of_node(fwnode), propname); + else if (is_acpi_node(fwnode)) + return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING, + val, nval); + else if (is_pset_node(fwnode)) + return val ? + pset_prop_read_string_array(to_pset_node(fwnode), + propname, val, nval) : + pset_prop_count_elems_of_size(to_pset_node(fwnode), + propname, + sizeof(const char *)); + return -ENXIO; +} + +static int __fwnode_property_read_string(struct fwnode_handle *fwnode, + const char *propname, const char **val) +{ + if (is_of_node(fwnode)) + return of_property_read_string(to_of_node(fwnode), propname, val); + else if (is_acpi_node(fwnode)) + return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING, + val, 1); + else if (is_pset_node(fwnode)) + return pset_prop_read_string(to_pset_node(fwnode), propname, val); + return -ENXIO; +} + /** * fwnode_property_read_string_array - return string array property of a node * @fwnode: Firmware node to get the property of @@ -456,18 +590,13 @@ int fwnode_property_read_string_array(struct fwnode_handle *fwnode, const char *propname, const char **val, size_t nval) { - if (is_of_node(fwnode)) - return val ? - of_property_read_string_array(to_of_node(fwnode), - propname, val, nval) : - of_property_count_strings(to_of_node(fwnode), propname); - else if (is_acpi_node(fwnode)) - return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING, - val, nval); - else if (is_pset(fwnode)) - return pset_prop_read_array(to_pset(fwnode), propname, - DEV_PROP_STRING, val, nval); - return -ENXIO; + int ret; + + ret = __fwnode_property_read_string_array(fwnode, propname, val, nval); + if (ret == -EINVAL && fwnode && fwnode->secondary) + ret = __fwnode_property_read_string_array(fwnode->secondary, + propname, val, nval); + return ret; } EXPORT_SYMBOL_GPL(fwnode_property_read_string_array); @@ -489,14 +618,13 @@ EXPORT_SYMBOL_GPL(fwnode_property_read_string_array); int fwnode_property_read_string(struct fwnode_handle *fwnode, const char *propname, const char **val) { - if (is_of_node(fwnode)) - return of_property_read_string(to_of_node(fwnode), propname, val); - else if (is_acpi_node(fwnode)) - return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING, - val, 1); + int ret; - return pset_prop_read_array(to_pset(fwnode), propname, - DEV_PROP_STRING, val, 1); + ret = __fwnode_property_read_string(fwnode, propname, val); + if (ret == -EINVAL && fwnode && fwnode->secondary) + ret = __fwnode_property_read_string(fwnode->secondary, + propname, val); + return ret; } EXPORT_SYMBOL_GPL(fwnode_property_read_string); @@ -525,6 +653,9 @@ int fwnode_property_match_string(struct fwnode_handle *fwnode, if (nval < 0) return nval; + if (nval == 0) + return -ENODATA; + values = kcalloc(nval, sizeof(*values), GFP_KERNEL); if (!values) return -ENOMEM; @@ -546,6 +677,182 @@ int fwnode_property_match_string(struct fwnode_handle *fwnode, } EXPORT_SYMBOL_GPL(fwnode_property_match_string); +/** + * pset_free_set - releases memory allocated for copied property set + * @pset: Property set to release + * + * Function takes previously copied property set and releases all the + * memory allocated to it. + */ +static void pset_free_set(struct property_set *pset) +{ + const struct property_entry *prop; + size_t i, nval; + + if (!pset) + return; + + for (prop = pset->properties; prop->name; prop++) { + if (prop->is_array) { + if (prop->is_string && prop->pointer.str) { + nval = prop->length / sizeof(const char *); + for (i = 0; i < nval; i++) + kfree(prop->pointer.str[i]); + } + kfree(prop->pointer.raw_data); + } else if (prop->is_string) { + kfree(prop->value.str); + } + kfree(prop->name); + } + + kfree(pset->properties); + kfree(pset); +} + +static int pset_copy_entry(struct property_entry *dst, + const struct property_entry *src) +{ + const char **d, **s; + size_t i, nval; + + dst->name = kstrdup(src->name, GFP_KERNEL); + if (!dst->name) + return -ENOMEM; + + if (src->is_array) { + if (!src->length) + return -ENODATA; + + if (src->is_string) { + nval = src->length / sizeof(const char *); + dst->pointer.str = kcalloc(nval, sizeof(const char *), + GFP_KERNEL); + if (!dst->pointer.str) + return -ENOMEM; + + d = dst->pointer.str; + s = src->pointer.str; + for (i = 0; i < nval; i++) { + d[i] = kstrdup(s[i], GFP_KERNEL); + if (!d[i] && s[i]) + return -ENOMEM; + } + } else { + dst->pointer.raw_data = kmemdup(src->pointer.raw_data, + src->length, GFP_KERNEL); + if (!dst->pointer.raw_data) + return -ENOMEM; + } + } else if (src->is_string) { + dst->value.str = kstrdup(src->value.str, GFP_KERNEL); + if (!dst->value.str && src->value.str) + return -ENOMEM; + } else { + dst->value.raw_data = src->value.raw_data; + } + + dst->length = src->length; + dst->is_array = src->is_array; + dst->is_string = src->is_string; + + return 0; +} + +/** + * pset_copy_set - copies property set + * @pset: Property set to copy + * + * This function takes a deep copy of the given property set and returns + * pointer to the copy. Call device_free_property_set() to free resources + * allocated in this function. + * + * Return: Pointer to the new property set or error pointer. + */ +static struct property_set *pset_copy_set(const struct property_set *pset) +{ + const struct property_entry *entry; + struct property_set *p; + size_t i, n = 0; + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) + return ERR_PTR(-ENOMEM); + + while (pset->properties[n].name) + n++; + + p->properties = kcalloc(n + 1, sizeof(*entry), GFP_KERNEL); + if (!p->properties) { + kfree(p); + return ERR_PTR(-ENOMEM); + } + + for (i = 0; i < n; i++) { + int ret = pset_copy_entry(&p->properties[i], + &pset->properties[i]); + if (ret) { + pset_free_set(p); + return ERR_PTR(ret); + } + } + + return p; +} + +/** + * device_remove_property_set - Remove properties from a device object. + * @dev: Device whose properties to remove. + * + * The function removes properties previously associated to the device + * secondary firmware node with device_add_property_set(). Memory allocated + * to the properties will also be released. + */ +void device_remove_property_set(struct device *dev) +{ + struct fwnode_handle *fwnode; + + fwnode = dev_fwnode(dev); + if (!fwnode) + return; + /* + * Pick either primary or secondary node depending which one holds + * the pset. If there is no real firmware node (ACPI/DT) primary + * will hold the pset. + */ + if (!is_pset_node(fwnode)) + fwnode = fwnode->secondary; + if (!IS_ERR(fwnode) && is_pset_node(fwnode)) + pset_free_set(to_pset_node(fwnode)); + set_secondary_fwnode(dev, NULL); +} +EXPORT_SYMBOL_GPL(device_remove_property_set); + +/** + * device_add_property_set - Add a collection of properties to a device object. + * @dev: Device to add properties to. + * @pset: Collection of properties to add. + * + * Associate a collection of device properties represented by @pset with @dev + * as its secondary firmware node. The function takes a copy of @pset. + */ +int device_add_property_set(struct device *dev, const struct property_set *pset) +{ + struct property_set *p; + + if (!pset) + return -EINVAL; + + p = pset_copy_set(pset); + if (IS_ERR(p)) + return PTR_ERR(p); + + p->fwnode.type = FWNODE_PDATA; + set_secondary_fwnode(dev, &p->fwnode); + return 0; +} +EXPORT_SYMBOL_GPL(device_add_property_set); + /** * device_get_next_child_node - Return the next child node handle for a device * @dev: Device to find the next child node for. diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 6b00061c3746..cbd663deef38 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -134,10 +135,10 @@ static inline int dw_i2c_acpi_configure(struct platform_device *pdev) static int dw_i2c_plat_probe(struct platform_device *pdev) { + struct dw_i2c_platform_data *pdata = dev_get_platdata(&pdev->dev); struct dw_i2c_dev *dev; struct i2c_adapter *adap; struct resource *mem; - struct dw_i2c_platform_data *pdata; int irq, r; u32 clk_freq, ht = 0; @@ -161,33 +162,28 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) /* fast mode by default because of legacy reasons */ clk_freq = 400000; - if (has_acpi_companion(&pdev->dev)) { - dw_i2c_acpi_configure(pdev); - } else if (pdev->dev.of_node) { - of_property_read_u32(pdev->dev.of_node, - "i2c-sda-hold-time-ns", &ht); - - of_property_read_u32(pdev->dev.of_node, - "i2c-sda-falling-time-ns", - &dev->sda_falling_time); - of_property_read_u32(pdev->dev.of_node, - "i2c-scl-falling-time-ns", - &dev->scl_falling_time); - - of_property_read_u32(pdev->dev.of_node, "clock-frequency", - &clk_freq); - - /* Only standard mode at 100kHz and fast mode at 400kHz - * are supported. - */ - if (clk_freq != 100000 && clk_freq != 400000) { - dev_err(&pdev->dev, "Only 100kHz and 400kHz supported"); - return -EINVAL; - } + if (pdata) { + clk_freq = pdata->i2c_scl_freq; } else { - pdata = dev_get_platdata(&pdev->dev); - if (pdata) - clk_freq = pdata->i2c_scl_freq; + device_property_read_u32(&pdev->dev, "i2c-sda-hold-time-ns", + &ht); + device_property_read_u32(&pdev->dev, "i2c-sda-falling-time-ns", + &dev->sda_falling_time); + device_property_read_u32(&pdev->dev, "i2c-scl-falling-time-ns", + &dev->scl_falling_time); + device_property_read_u32(&pdev->dev, "clock-frequency", + &clk_freq); + } + + if (has_acpi_companion(&pdev->dev)) + dw_i2c_acpi_configure(pdev); + + /* + * Only standard mode at 100kHz and fast mode at 400kHz are supported. + */ + if (clk_freq != 100000 && clk_freq != 400000) { + dev_err(&pdev->dev, "Only 100kHz and 400kHz supported"); + return -EINVAL; } r = i2c_dw_eval_lock_support(dev); diff --git a/drivers/mfd/intel-lpss-acpi.c b/drivers/mfd/intel-lpss-acpi.c index b6fd9041f82f..06f00d60be46 100644 --- a/drivers/mfd/intel-lpss-acpi.c +++ b/drivers/mfd/intel-lpss-acpi.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "intel-lpss.h" @@ -25,6 +26,20 @@ static const struct intel_lpss_platform_info spt_info = { .clk_rate = 120000000, }; +static struct property_entry spt_i2c_properties[] = { + PROPERTY_ENTRY_U32("i2c-sda-hold-time-ns", 230), + { }, +}; + +static struct property_set spt_i2c_pset = { + .properties = spt_i2c_properties, +}; + +static const struct intel_lpss_platform_info spt_i2c_info = { + .clk_rate = 120000000, + .pset = &spt_i2c_pset, +}; + static const struct intel_lpss_platform_info bxt_info = { .clk_rate = 100000000, }; @@ -35,8 +50,8 @@ static const struct intel_lpss_platform_info bxt_i2c_info = { static const struct acpi_device_id intel_lpss_acpi_ids[] = { /* SPT */ - { "INT3446", (kernel_ulong_t)&spt_info }, - { "INT3447", (kernel_ulong_t)&spt_info }, + { "INT3446", (kernel_ulong_t)&spt_i2c_info }, + { "INT3447", (kernel_ulong_t)&spt_i2c_info }, /* BXT */ { "80860AAC", (kernel_ulong_t)&bxt_i2c_info }, { "80860ABC", (kernel_ulong_t)&bxt_info }, diff --git a/drivers/mfd/intel-lpss-pci.c b/drivers/mfd/intel-lpss-pci.c index 5bfdfccbb9a1..a7136c7ae9fb 100644 --- a/drivers/mfd/intel-lpss-pci.c +++ b/drivers/mfd/intel-lpss-pci.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "intel-lpss.h" @@ -65,9 +66,35 @@ static const struct intel_lpss_platform_info spt_info = { .clk_rate = 120000000, }; +static struct property_entry spt_i2c_properties[] = { + PROPERTY_ENTRY_U32("i2c-sda-hold-time-ns", 230), + { }, +}; + +static struct property_set spt_i2c_pset = { + .properties = spt_i2c_properties, +}; + +static const struct intel_lpss_platform_info spt_i2c_info = { + .clk_rate = 120000000, + .pset = &spt_i2c_pset, +}; + +static struct property_entry uart_properties[] = { + PROPERTY_ENTRY_U32("reg-io-width", 4), + PROPERTY_ENTRY_U32("reg-shift", 2), + PROPERTY_ENTRY_BOOL("snps,uart-16550-compatible"), + { }, +}; + +static struct property_set uart_pset = { + .properties = uart_properties, +}; + static const struct intel_lpss_platform_info spt_uart_info = { .clk_rate = 120000000, .clk_con_id = "baudclk", + .pset = &uart_pset, }; static const struct intel_lpss_platform_info bxt_info = { @@ -77,6 +104,7 @@ static const struct intel_lpss_platform_info bxt_info = { static const struct intel_lpss_platform_info bxt_uart_info = { .clk_rate = 100000000, .clk_con_id = "baudclk", + .pset = &uart_pset, }; static const struct intel_lpss_platform_info bxt_i2c_info = { @@ -121,20 +149,20 @@ static const struct pci_device_id intel_lpss_pci_ids[] = { { PCI_VDEVICE(INTEL, 0x9d28), (kernel_ulong_t)&spt_uart_info }, { PCI_VDEVICE(INTEL, 0x9d29), (kernel_ulong_t)&spt_info }, { PCI_VDEVICE(INTEL, 0x9d2a), (kernel_ulong_t)&spt_info }, - { PCI_VDEVICE(INTEL, 0x9d60), (kernel_ulong_t)&spt_info }, - { PCI_VDEVICE(INTEL, 0x9d61), (kernel_ulong_t)&spt_info }, - { PCI_VDEVICE(INTEL, 0x9d62), (kernel_ulong_t)&spt_info }, - { PCI_VDEVICE(INTEL, 0x9d63), (kernel_ulong_t)&spt_info }, - { PCI_VDEVICE(INTEL, 0x9d64), (kernel_ulong_t)&spt_info }, - { PCI_VDEVICE(INTEL, 0x9d65), (kernel_ulong_t)&spt_info }, + { PCI_VDEVICE(INTEL, 0x9d60), (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x9d61), (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x9d62), (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x9d63), (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x9d64), (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x9d65), (kernel_ulong_t)&spt_i2c_info }, { PCI_VDEVICE(INTEL, 0x9d66), (kernel_ulong_t)&spt_uart_info }, /* SPT-H */ { PCI_VDEVICE(INTEL, 0xa127), (kernel_ulong_t)&spt_uart_info }, { PCI_VDEVICE(INTEL, 0xa128), (kernel_ulong_t)&spt_uart_info }, { PCI_VDEVICE(INTEL, 0xa129), (kernel_ulong_t)&spt_info }, { PCI_VDEVICE(INTEL, 0xa12a), (kernel_ulong_t)&spt_info }, - { PCI_VDEVICE(INTEL, 0xa160), (kernel_ulong_t)&spt_info }, - { PCI_VDEVICE(INTEL, 0xa161), (kernel_ulong_t)&spt_info }, + { PCI_VDEVICE(INTEL, 0xa160), (kernel_ulong_t)&spt_i2c_info }, + { PCI_VDEVICE(INTEL, 0xa161), (kernel_ulong_t)&spt_i2c_info }, { PCI_VDEVICE(INTEL, 0xa166), (kernel_ulong_t)&spt_uart_info }, { } }; diff --git a/drivers/mfd/intel-lpss.c b/drivers/mfd/intel-lpss.c index 6255513f54c7..1743788f1595 100644 --- a/drivers/mfd/intel-lpss.c +++ b/drivers/mfd/intel-lpss.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -72,7 +73,7 @@ struct intel_lpss { enum intel_lpss_dev_type type; struct clk *clk; struct clk_lookup *clock; - const struct mfd_cell *cell; + struct mfd_cell *cell; struct device *dev; void __iomem *priv; int devid; @@ -217,6 +218,7 @@ static void intel_lpss_ltr_hide(struct intel_lpss *lpss) static int intel_lpss_assign_devs(struct intel_lpss *lpss) { + const struct mfd_cell *cell; unsigned int type; type = lpss->caps & LPSS_PRIV_CAPS_TYPE_MASK; @@ -224,18 +226,22 @@ static int intel_lpss_assign_devs(struct intel_lpss *lpss) switch (type) { case LPSS_DEV_I2C: - lpss->cell = &intel_lpss_i2c_cell; + cell = &intel_lpss_i2c_cell; break; case LPSS_DEV_UART: - lpss->cell = &intel_lpss_uart_cell; + cell = &intel_lpss_uart_cell; break; case LPSS_DEV_SPI: - lpss->cell = &intel_lpss_spi_cell; + cell = &intel_lpss_spi_cell; break; default: return -ENODEV; } + lpss->cell = devm_kmemdup(lpss->dev, cell, sizeof(*cell), GFP_KERNEL); + if (!lpss->cell) + return -ENOMEM; + lpss->type = type; return 0; @@ -401,6 +407,8 @@ int intel_lpss_probe(struct device *dev, if (ret) return ret; + lpss->cell->pset = info->pset; + intel_lpss_init_dev(lpss); lpss->devid = ida_simple_get(&intel_lpss_devid_ida, 0, 0, GFP_KERNEL); diff --git a/drivers/mfd/intel-lpss.h b/drivers/mfd/intel-lpss.h index 2c7f8d7c0595..0dcea9eb2d03 100644 --- a/drivers/mfd/intel-lpss.h +++ b/drivers/mfd/intel-lpss.h @@ -16,12 +16,14 @@ struct device; struct resource; +struct property_set; struct intel_lpss_platform_info { struct resource *mem; int irq; unsigned long clk_rate; const char *clk_con_id; + struct property_set *pset; }; int intel_lpss_probe(struct device *dev, diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index 60b60dc63ddd..88bd1b1e47be 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -192,6 +193,12 @@ static int mfd_add_device(struct device *parent, int id, goto fail_alias; } + if (cell->pset) { + ret = platform_device_add_properties(pdev, cell->pset); + if (ret) + goto fail_alias; + } + ret = mfd_platform_add_cell(pdev, cell, usage_count); if (ret) goto fail_alias; diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h index 27dac3ff18b9..bc6f7e00fb3d 100644 --- a/include/linux/mfd/core.h +++ b/include/linux/mfd/core.h @@ -17,6 +17,7 @@ #include struct irq_domain; +struct property_set; /* Matches ACPI PNP id, either _HID or _CID, or ACPI _ADR */ struct mfd_cell_acpi_match { @@ -44,6 +45,10 @@ struct mfd_cell { /* platform data passed to the sub devices drivers */ void *platform_data; size_t pdata_size; + + /* device properties passed to the sub devices drivers */ + const struct property_set *pset; + /* * Device Tree compatible string * See: Documentation/devicetree/usage-model.txt Chapter 2.2 for details diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h index dc777be5f2e1..dba40b1c41dc 100644 --- a/include/linux/platform_device.h +++ b/include/linux/platform_device.h @@ -18,6 +18,7 @@ #define PLATFORM_DEVID_AUTO (-2) struct mfd_cell; +struct property_set; struct platform_device { const char *name; @@ -70,6 +71,8 @@ struct platform_device_info { const void *data; size_t size_data; u64 dma_mask; + + const struct property_set *pset; }; extern struct platform_device *platform_device_register_full( const struct platform_device_info *pdevinfo); @@ -167,6 +170,8 @@ extern int platform_device_add_resources(struct platform_device *pdev, unsigned int num); extern int platform_device_add_data(struct platform_device *pdev, const void *data, size_t size); +extern int platform_device_add_properties(struct platform_device *pdev, + const struct property_set *pset); extern int platform_device_add(struct platform_device *pdev); extern void platform_device_del(struct platform_device *pdev); extern void platform_device_put(struct platform_device *pdev); diff --git a/include/linux/property.h b/include/linux/property.h index 0a3705a7c9f2..b51fcd36d892 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -73,8 +73,8 @@ int fwnode_property_match_string(struct fwnode_handle *fwnode, struct fwnode_handle *device_get_next_child_node(struct device *dev, struct fwnode_handle *child); -#define device_for_each_child_node(dev, child) \ - for (child = device_get_next_child_node(dev, NULL); child; \ +#define device_for_each_child_node(dev, child) \ + for (child = device_get_next_child_node(dev, NULL); child; \ child = device_get_next_child_node(dev, child)) void fwnode_handle_put(struct fwnode_handle *fwnode); @@ -144,24 +144,100 @@ static inline int fwnode_property_read_u64(struct fwnode_handle *fwnode, /** * struct property_entry - "Built-in" device property representation. * @name: Name of the property. - * @type: Type of the property. - * @nval: Number of items of type @type making up the value. - * @value: Value of the property (an array of @nval items of type @type). + * @length: Length of data making up the value. + * @is_array: True when the property is an array. + * @is_string: True when property is a string. + * @pointer: Pointer to the property (an array of items of the given type). + * @value: Value of the property (when it is a single item of the given type). */ struct property_entry { const char *name; - enum dev_prop_type type; - size_t nval; + size_t length; + bool is_array; + bool is_string; union { - void *raw_data; - u8 *u8_data; - u16 *u16_data; - u32 *u32_data; - u64 *u64_data; - const char **str; - } value; + union { + void *raw_data; + u8 *u8_data; + u16 *u16_data; + u32 *u32_data; + u64 *u64_data; + const char **str; + } pointer; + union { + unsigned long long raw_data; + u8 u8_data; + u16 u16_data; + u32 u32_data; + u64 u64_data; + const char *str; + } value; + }; }; +/* + * Note: the below four initializers for the anonymous union are carefully + * crafted to avoid gcc-4.4.4's problems with initialization of anon unions + * and structs. + */ + +#define PROPERTY_ENTRY_INTEGER_ARRAY(_name_, _type_, _val_) \ +{ \ + .name = _name_, \ + .length = ARRAY_SIZE(_val_) * sizeof(_type_), \ + .is_array = true, \ + .is_string = false, \ + { .pointer = { _type_##_data = _val_ } }, \ +} + +#define PROPERTY_ENTRY_U8_ARRAY(_name_, _val_) \ + PROPERTY_ENTRY_INTEGER_ARRAY(_name_, u8, _val_) +#define PROPERTY_ENTRY_U16_ARRAY(_name_, _val_) \ + PROPERTY_ENTRY_INTEGER_ARRAY(_name_, u16, _val_) +#define PROPERTY_ENTRY_U32_ARRAY(_name_, _val_) \ + PROPERTY_ENTRY_INTEGER_ARRAY(_name_, u32, _val_) +#define PROPERTY_ENTRY_U64_ARRAY(_name_, _val_) \ + PROPERTY_ENTRY_INTEGER_ARRAY(_name_, u64, _val_) + +#define PROPERTY_ENTRY_STRING_ARRAY(_name_, _val_) \ +{ \ + .name = _name_, \ + .length = ARRAY_SIZE(_val_) * sizeof(const char *), \ + .is_array = true, \ + .is_string = true, \ + { .pointer = { .str = _val_ } }, \ +} + +#define PROPERTY_ENTRY_INTEGER(_name_, _type_, _val_) \ +{ \ + .name = _name_, \ + .length = sizeof(_type_), \ + .is_string = false, \ + { .value = { ._type_##_data = _val_ } }, \ +} + +#define PROPERTY_ENTRY_U8(_name_, _val_) \ + PROPERTY_ENTRY_INTEGER(_name_, u8, _val_) +#define PROPERTY_ENTRY_U16(_name_, _val_) \ + PROPERTY_ENTRY_INTEGER(_name_, u16, _val_) +#define PROPERTY_ENTRY_U32(_name_, _val_) \ + PROPERTY_ENTRY_INTEGER(_name_, u32, _val_) +#define PROPERTY_ENTRY_U64(_name_, _val_) \ + PROPERTY_ENTRY_INTEGER(_name_, u64, _val_) + +#define PROPERTY_ENTRY_STRING(_name_, _val_) \ +{ \ + .name = _name_, \ + .length = sizeof(_val_), \ + .is_string = true, \ + { .value = { .str = _val_ } }, \ +} + +#define PROPERTY_ENTRY_BOOL(_name_) \ +{ \ + .name = _name_, \ +} + /** * struct property_set - Collection of "built-in" device properties. * @fwnode: Handle to be pointed to by the fwnode field of struct device. @@ -172,7 +248,8 @@ struct property_set { struct property_entry *properties; }; -void device_add_property_set(struct device *dev, struct property_set *pset); +int device_add_property_set(struct device *dev, const struct property_set *pset); +void device_remove_property_set(struct device *dev); bool device_dma_supported(struct device *dev);