From 139e4a1cbe60de96d4febbc6db5882929801ca46 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Wed, 9 Sep 2020 15:37:03 +0530 Subject: drivers: reset: Add a managed API to get reset controllers from the DT Add managed functions to get a reset_ctl from the device-tree, based on a name or an index. Also add a managed functions to get a reset_ctl_bulk (array of reset_ctl) from the device-tree. When the device is unbound, the reset controllers are automatically released and the data structure is freed. Signed-off-by: Jean-Jacques Hiblot Reviewed-by: Simon Glass Signed-off-by: Pratyush Yadav --- include/reset.h | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 134 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/reset.h b/include/reset.h index 4fac4e6a202..cde2c4b4a8c 100644 --- a/include/reset.h +++ b/include/reset.h @@ -7,7 +7,7 @@ #define _RESET_H #include -#include +#include /** * A reset is a hardware signal indicating that a HW module (or IP block, or @@ -84,6 +84,98 @@ struct reset_ctl_bulk { }; #if CONFIG_IS_ENABLED(DM_RESET) + +/** + * devm_reset_control_get - resource managed reset_get_by_name() + * @dev: device to be reset by the controller + * @id: reset line name + * + * Managed reset_get_by_name(). For reset controllers returned + * from this function, reset_free() is called automatically on driver + * detach. + * + * Returns a struct reset_ctl or IS_ERR() condition containing errno. + */ +struct reset_ctl *devm_reset_control_get(struct udevice *dev, const char *id); + +/** + * devm_reset_control_get_optional - resource managed reset_get_by_name() that + * can fail + * @dev: The client device. + * @id: reset line name + * + * Managed reset_get_by_name(). For reset controllers returned + * from this function, reset_free() is called automatically on driver + * detach. + * + * Returns a struct reset_ctl or a dummy reset controller if it failed. + */ +struct reset_ctl *devm_reset_control_get_optional(struct udevice *dev, + const char *id); + +/** + * devm_reset_control_get - resource managed reset_get_by_index() + * @dev: The client device. + * @index: The index of the reset signal to request, within the client's + * list of reset signals. + * + * Managed reset_get_by_index(). For reset controllers returned + * from this function, reset_free() is called automatically on driver + * detach. + * + * Returns a struct reset_ctl or IS_ERR() condition containing errno. + */ +struct reset_ctl *devm_reset_control_get_by_index(struct udevice *dev, + int index); + +/** + * devm_reset_bulk_get - resource managed reset_get_bulk() + * @dev: device to be reset by the controller + * + * Managed reset_get_bulk(). For reset controllers returned + * from this function, reset_free() is called automatically on driver + * detach. + * + * Returns a struct reset_ctl or IS_ERR() condition containing errno. + */ +struct reset_ctl_bulk *devm_reset_bulk_get(struct udevice *dev); + +/** + * devm_reset_bulk_get_optional - resource managed reset_get_bulk() that + * can fail + * @dev: The client device. + * + * Managed reset_get_bulk(). For reset controllers returned + * from this function, reset_free() is called automatically on driver + * detach. + * + * Returns a struct reset_ctl or NULL if it failed. + */ +struct reset_ctl_bulk *devm_reset_bulk_get_optional(struct udevice *dev); + +/** + * devm_reset_bulk_get_by_node - resource managed reset_get_bulk() + * @dev: device to be reset by the controller + * @node: ofnode where the "resets" property is. Usually a sub-node of + * the dev's node. + * + * see devm_reset_bulk_get() + */ +struct reset_ctl_bulk *devm_reset_bulk_get_by_node(struct udevice *dev, + ofnode node); + +/** + * devm_reset_bulk_get_optional_by_node - resource managed reset_get_bulk() + * that can fail + * @dev: device to be reset by the controller + * @node: ofnode where the "resets" property is. Usually a sub-node of + * the dev's node. + * + * see devm_reset_bulk_get_optional() + */ +struct reset_ctl_bulk *devm_reset_bulk_get_optional_by_node(struct udevice *dev, + ofnode node); + /** * reset_get_by_index - Get/request a reset signal by integer index. * @@ -265,7 +357,48 @@ static inline int reset_release_bulk(struct reset_ctl_bulk *bulk) { return reset_release_all(bulk->resets, bulk->count); } + #else +static inline struct reset_ctl *devm_reset_control_get(struct udevice *dev, + const char *id) +{ + return ERR_PTR(-ENOTSUPP); +} + +static inline struct reset_ctl *devm_reset_control_get_optional(struct udevice *dev, + const char *id) +{ + return NULL; +} + +static inline struct reset_ctl *devm_reset_control_get_by_index(struct udevice *dev, + int index) +{ + return ERR_PTR(-ENOTSUPP); +} + +static inline struct reset_ctl_bulk *devm_reset_bulk_get(struct udevice *dev) +{ + return ERR_PTR(-ENOTSUPP); +} + +static inline struct reset_ctl_bulk *devm_reset_bulk_get_optional(struct udevice *dev) +{ + return NULL; +} + +static inline struct reset_ctl_bulk *devm_reset_bulk_get_by_node(struct udevice *dev, + ofnode node) +{ + return ERR_PTR(-ENOTSUPP); +} + +static inline struct reset_ctl_bulk *devm_reset_bulk_get_optional_by_node(struct udevice *dev, + ofnode node) +{ + return NULL; +} + static inline int reset_get_by_index(struct udevice *dev, int index, struct reset_ctl *reset_ctl) { -- cgit v1.3.1 From d4b722e3a85f3a5704501f3a71faa43db1979209 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Fri, 11 Sep 2020 13:43:34 +0530 Subject: drivers: gpio: Add a managed API to get a GPIO from the device-tree Add managed functions to get a gpio from the devce-tree, based on a property name (minus the '-gpios' suffix) and optionally an index. When the device is unbound, the GPIO is automatically released and the data structure is freed. Signed-off-by: Jean-Jacques Hiblot Reviewed-by: Simon Glass Signed-off-by: Pratyush Yadav --- drivers/gpio/gpio-uclass.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++ include/asm-generic/gpio.h | 47 ++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) (limited to 'include') diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index 9c53299b6a3..0c01413b587 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include #include #include @@ -1209,6 +1211,75 @@ int gpio_dev_request_index(struct udevice *dev, const char *nodename, flags, 0, dev); } +static void devm_gpiod_release(struct udevice *dev, void *res) +{ + dm_gpio_free(dev, res); +} + +static int devm_gpiod_match(struct udevice *dev, void *res, void *data) +{ + return res == data; +} + +struct gpio_desc *devm_gpiod_get_index(struct udevice *dev, const char *id, + unsigned int index, int flags) +{ + int rc; + struct gpio_desc *desc; + char *propname; + static const char suffix[] = "-gpios"; + + propname = malloc(strlen(id) + sizeof(suffix)); + if (!propname) { + rc = -ENOMEM; + goto end; + } + + strcpy(propname, id); + strcat(propname, suffix); + + desc = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc), + __GFP_ZERO); + if (unlikely(!desc)) { + rc = -ENOMEM; + goto end; + } + + rc = gpio_request_by_name(dev, propname, index, desc, flags); + +end: + if (propname) + free(propname); + + if (rc) + return ERR_PTR(rc); + + devres_add(dev, desc); + + return desc; +} + +struct gpio_desc *devm_gpiod_get_index_optional(struct udevice *dev, + const char *id, + unsigned int index, + int flags) +{ + struct gpio_desc *desc = devm_gpiod_get_index(dev, id, index, flags); + + if (IS_ERR(desc)) + return NULL; + + return desc; +} + +void devm_gpiod_put(struct udevice *dev, struct gpio_desc *desc) +{ + int rc; + + rc = devres_release(dev, devm_gpiod_release, devm_gpiod_match, desc); + WARN_ON(rc); +} + static int gpio_post_bind(struct udevice *dev) { struct udevice *child; diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index a57dd2665ce..3ae1894a981 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -701,4 +701,51 @@ int gpio_get_number(const struct gpio_desc *desc); */ int gpio_get_acpi(const struct gpio_desc *desc, struct acpi_gpio *gpio); +/** + * devm_gpiod_get_index - Resource-managed gpiod_get() + * @dev: GPIO consumer + * @con_id: function within the GPIO consumer + * @index: index of the GPIO to obtain in the consumer + * @flags: optional GPIO initialization flags + * + * Managed gpiod_get(). GPIO descriptors returned from this function are + * automatically disposed on device unbind. + * Return the GPIO descriptor corresponding to the function con_id of device + * dev, -ENOENT if no GPIO has been assigned to the requested function, or + * another IS_ERR() code if an error occurred while trying to acquire the GPIO. + */ +struct gpio_desc *devm_gpiod_get_index(struct udevice *dev, const char *id, + unsigned int index, int flags); + +#define devm_gpiod_get(dev, id, flags) devm_gpiod_get_index(dev, id, 0, flags) +/** + * gpiod_get_optional - obtain an optional GPIO for a given GPIO function + * @dev: GPIO consumer, can be NULL for system-global GPIOs + * @con_id: function within the GPIO consumer + * @index: index of the GPIO to obtain in the consumer + * @flags: optional GPIO initialization flags + * + * This is equivalent to devm_gpiod_get(), except that when no GPIO was + * assigned to the requested function it will return NULL. This is convenient + * for drivers that need to handle optional GPIOs. + */ +struct gpio_desc *devm_gpiod_get_index_optional(struct udevice *dev, + const char *id, + unsigned int index, + int flags); + +#define devm_gpiod_get_optional(dev, id, flags) \ + devm_gpiod_get_index_optional(dev, id, 0, flags) + +/** + * devm_gpiod_put - Resource-managed gpiod_put() + * @dev: GPIO consumer + * @desc: GPIO descriptor to dispose of + * + * Dispose of a GPIO descriptor obtained with devm_gpiod_get() or + * devm_gpiod_get_index(). Normally this function will not be called as the GPIO + * will be disposed of by the resource management code. + */ +void devm_gpiod_put(struct udevice *dev, struct gpio_desc *desc); + #endif /* _ASM_GENERIC_GPIO_H_ */ -- cgit v1.3.1 From ffb22f6b847d21b30831c91294ec21d6a5e80ed4 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 24 Sep 2020 10:04:10 +0530 Subject: regmap: Add devm_regmap_init() Most of new linux drivers are using managed-API to allocate resources. To ease porting drivers from linux to U-Boot, introduce devm_regmap_init() as a managed API to get a regmap from the device tree. Signed-off-by: Jean-Jacques Hiblot Reviewed-by: Simon Glass Signed-off-by: Pratyush Yadav --- drivers/core/regmap.c | 29 +++++++++++++++++++++++++++++ include/regmap.h | 18 ++++++++++++++++++ 2 files changed, 47 insertions(+) (limited to 'include') diff --git a/drivers/core/regmap.c b/drivers/core/regmap.c index a67a237b88f..74225361fd6 100644 --- a/drivers/core/regmap.c +++ b/drivers/core/regmap.c @@ -14,7 +14,10 @@ #include #include #include +#include #include +#include +#include DECLARE_GLOBAL_DATA_PTR; @@ -228,6 +231,32 @@ err: return ret; } + +static void devm_regmap_release(struct udevice *dev, void *res) +{ + regmap_uninit(*(struct regmap **)res); +} + +struct regmap *devm_regmap_init(struct udevice *dev, + const struct regmap_bus *bus, + void *bus_context, + const struct regmap_config *config) +{ + int rc; + struct regmap **mapp; + + mapp = devres_alloc(devm_regmap_release, sizeof(struct regmap *), + __GFP_ZERO); + if (unlikely(!mapp)) + return ERR_PTR(-ENOMEM); + + rc = regmap_init_mem(dev_ofnode(dev), mapp); + if (rc) + return ERR_PTR(rc); + + devres_add(dev, mapp); + return *mapp; +} #endif void *regmap_get_range(struct regmap *map, unsigned int range_num) diff --git a/include/regmap.h b/include/regmap.h index 30183c5e711..c7dd240a74f 100644 --- a/include/regmap.h +++ b/include/regmap.h @@ -75,6 +75,9 @@ struct regmap_range { ulong size; }; +struct regmap_bus; +struct regmap_config; + /** * struct regmap - a way of accessing hardware/bus registers * @@ -335,6 +338,21 @@ int regmap_init_mem_platdata(struct udevice *dev, fdt_val_t *reg, int count, int regmap_init_mem_index(ofnode node, struct regmap **mapp, int index); +/** + * devm_regmap_init() - Initialise register map (device managed) + * + * @dev: Device that will be interacted with + * @bus: Bus-specific callbacks to use with device (IGNORED) + * @bus_context: Data passed to bus-specific callbacks (IGNORED) + * @config: Configuration for register map (IGNORED) + * + * @Return a valid pointer to a struct regmap or a ERR_PTR() on error. + * The structure is automatically freed when the device is unbound + */ +struct regmap *devm_regmap_init(struct udevice *dev, + const struct regmap_bus *bus, + void *bus_context, + const struct regmap_config *config); /** * regmap_get_range() - Obtain the base memory address of a regmap range * -- cgit v1.3.1 From 78aaedba9f1b92335a4f0ce8344f6abf7a63dccb Mon Sep 17 00:00:00 2001 From: Pratyush Yadav Date: Thu, 24 Sep 2020 10:04:12 +0530 Subject: regmap: Allow specifying read/write width Right now, regmap_read() and regmap_write() read/write a 32-bit value only. To write other lengths, regmap_raw_read() and regmap_raw_write() need to be used. This means that any driver ported from Linux that relies on regmap_{read,write}() to know the size already has to be updated at each callsite. This makes the port harder to maintain. So, allow specifying the read/write width to make it easier to port the drivers, since now the only change needed is when initializing the regmap. Signed-off-by: Pratyush Yadav Reviewed-by: Simon Glass --- drivers/core/regmap.c | 15 ++++++++++++--- include/regmap.h | 38 ++++++++++++++++++++++---------------- 2 files changed, 34 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/core/regmap.c b/drivers/core/regmap.c index 809f58489f0..c71b961234a 100644 --- a/drivers/core/regmap.c +++ b/drivers/core/regmap.c @@ -25,6 +25,10 @@ DECLARE_GLOBAL_DATA_PTR; * regmap_alloc() - Allocate a regmap with a given number of ranges. * * @count: Number of ranges to be allocated for the regmap. + * + * The default regmap width is set to REGMAP_SIZE_32. Callers can override it + * if they need. + * * Return: A pointer to the newly allocated regmap, or NULL on error. */ static struct regmap *regmap_alloc(int count) @@ -36,6 +40,7 @@ static struct regmap *regmap_alloc(int count) if (!map) return NULL; map->range_count = count; + map->width = REGMAP_SIZE_32; return map; } @@ -244,7 +249,7 @@ struct regmap *devm_regmap_init(struct udevice *dev, const struct regmap_config *config) { int rc; - struct regmap **mapp; + struct regmap **mapp, *map; mapp = devres_alloc(devm_regmap_release, sizeof(struct regmap *), __GFP_ZERO); @@ -255,6 +260,10 @@ struct regmap *devm_regmap_init(struct udevice *dev, if (rc) return ERR_PTR(rc); + map = *mapp; + if (config) + map->width = config->width; + devres_add(dev, mapp); return *mapp; } @@ -377,7 +386,7 @@ int regmap_raw_read(struct regmap *map, uint offset, void *valp, size_t val_len) int regmap_read(struct regmap *map, uint offset, uint *valp) { - return regmap_raw_read(map, offset, valp, REGMAP_SIZE_32); + return regmap_raw_read(map, offset, valp, map->width); } static inline void __write_8(u8 *addr, const u8 *val, @@ -487,7 +496,7 @@ int regmap_raw_write(struct regmap *map, uint offset, const void *val, int regmap_write(struct regmap *map, uint offset, uint val) { - return regmap_raw_write(map, offset, &val, REGMAP_SIZE_32); + return regmap_raw_write(map, offset, &val, map->width); } int regmap_update_bits(struct regmap *map, uint offset, uint mask, uint val) diff --git a/include/regmap.h b/include/regmap.h index c7dd240a74f..19474e6de12 100644 --- a/include/regmap.h +++ b/include/regmap.h @@ -76,16 +76,28 @@ struct regmap_range { }; struct regmap_bus; -struct regmap_config; + +/** + * struct regmap_config - Configure the behaviour of a regmap + * + * @width: Width of the read/write operations. Defaults to + * REGMAP_SIZE_32 if set to 0. + */ +struct regmap_config { + enum regmap_size_t width; +}; /** * struct regmap - a way of accessing hardware/bus registers * + * @width: Width of the read/write operations. Defaults to + * REGMAP_SIZE_32 if set to 0. * @range_count: Number of ranges available within the map * @ranges: Array of ranges */ struct regmap { enum regmap_endianness_t endianness; + enum regmap_size_t width; int range_count; struct regmap_range ranges[0]; }; @@ -96,32 +108,24 @@ struct regmap { */ /** - * regmap_write() - Write a 32-bit value to a regmap + * regmap_write() - Write a value to a regmap * * @map: Regmap to write to * @offset: Offset in the regmap to write to * @val: Data to write to the regmap at the specified offset * - * Note that this function will only write values of 32 bit width to the - * regmap; if the size of data to be read is different, the regmap_raw_write - * function can be used. - * * Return: 0 if OK, -ve on error */ int regmap_write(struct regmap *map, uint offset, uint val); /** - * regmap_read() - Read a 32-bit value from a regmap + * regmap_read() - Read a value from a regmap * * @map: Regmap to read from * @offset: Offset in the regmap to read from * @valp: Pointer to the buffer to receive the data read from the regmap * at the specified offset * - * Note that this function will only read values of 32 bit width from the - * regmap; if the size of data to be read is different, the regmap_raw_read - * function can be used. - * * Return: 0 if OK, -ve on error */ int regmap_read(struct regmap *map, uint offset, uint *valp); @@ -135,8 +139,9 @@ int regmap_read(struct regmap *map, uint offset, uint *valp); * @val_len: Length of the data to be written to the regmap * * Note that this function will, as opposed to regmap_write, write data of - * arbitrary length to the regmap, and not just 32-bit values, and is thus a - * generalized version of regmap_write. + * arbitrary length to the regmap, and not just the size configured in the + * regmap (defaults to 32-bit) and is thus a generalized version of + * regmap_write. * * Return: 0 if OK, -ve on error */ @@ -153,8 +158,9 @@ int regmap_raw_write(struct regmap *map, uint offset, const void *val, * @val_len: Length of the data to be read from the regmap * * Note that this function will, as opposed to regmap_read, read data of - * arbitrary length from the regmap, and not just 32-bit values, and is thus a - * generalized version of regmap_read. + * arbitrary length from the regmap, and not just the size configured in the + * regmap (defaults to 32-bit) and is thus a generalized version of + * regmap_read. * * Return: 0 if OK, -ve on error */ @@ -344,7 +350,7 @@ int regmap_init_mem_index(ofnode node, struct regmap **mapp, int index); * @dev: Device that will be interacted with * @bus: Bus-specific callbacks to use with device (IGNORED) * @bus_context: Data passed to bus-specific callbacks (IGNORED) - * @config: Configuration for register map (IGNORED) + * @config: Configuration for register map * * @Return a valid pointer to a struct regmap or a ERR_PTR() on error. * The structure is automatically freed when the device is unbound -- cgit v1.3.1 From 7aa5ddffe7fbafe7ef828088ba1dbc76270300c5 Mon Sep 17 00:00:00 2001 From: Pratyush Yadav Date: Thu, 24 Sep 2020 10:04:13 +0530 Subject: regmap: Allow left shifting register offset before access Drivers can configure it to adjust the final read/write location. Signed-off-by: Pratyush Yadav Reviewed-by: Simon Glass --- drivers/core/regmap.c | 6 +++++- include/regmap.h | 6 ++++++ 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/core/regmap.c b/drivers/core/regmap.c index c71b961234a..173ae808909 100644 --- a/drivers/core/regmap.c +++ b/drivers/core/regmap.c @@ -261,8 +261,10 @@ struct regmap *devm_regmap_init(struct udevice *dev, return ERR_PTR(rc); map = *mapp; - if (config) + if (config) { map->width = config->width; + map->reg_offset_shift = config->reg_offset_shift; + } devres_add(dev, mapp); return *mapp; @@ -349,6 +351,7 @@ int regmap_raw_read_range(struct regmap *map, uint range_num, uint offset, } range = &map->ranges[range_num]; + offset <<= map->reg_offset_shift; if (offset + val_len > range->size) { debug("%s: offset/size combination invalid\n", __func__); return -ERANGE; @@ -458,6 +461,7 @@ int regmap_raw_write_range(struct regmap *map, uint range_num, uint offset, } range = &map->ranges[range_num]; + offset <<= map->reg_offset_shift; if (offset + val_len > range->size) { debug("%s: offset/size combination invalid\n", __func__); return -ERANGE; diff --git a/include/regmap.h b/include/regmap.h index 19474e6de12..e6c59dfbced 100644 --- a/include/regmap.h +++ b/include/regmap.h @@ -82,9 +82,12 @@ struct regmap_bus; * * @width: Width of the read/write operations. Defaults to * REGMAP_SIZE_32 if set to 0. + * @reg_offset_shift Left shift the register offset by this value before + * performing read or write. */ struct regmap_config { enum regmap_size_t width; + u32 reg_offset_shift; }; /** @@ -92,12 +95,15 @@ struct regmap_config { * * @width: Width of the read/write operations. Defaults to * REGMAP_SIZE_32 if set to 0. + * @reg_offset_shift Left shift the register offset by this value before + * performing read or write. * @range_count: Number of ranges available within the map * @ranges: Array of ranges */ struct regmap { enum regmap_endianness_t endianness; enum regmap_size_t width; + u32 reg_offset_shift; int range_count; struct regmap_range ranges[0]; }; -- cgit v1.3.1 From 0e01a7c3f4b6a42f768a19f7fc1df92d3e3b5d37 Mon Sep 17 00:00:00 2001 From: Pratyush Yadav Date: Thu, 24 Sep 2020 10:04:14 +0530 Subject: regmap: Add regmap_init_mem_range() Right now, the base of a regmap can only be obtained from the device tree. This makes it impossible for devices which calculate the base at runtime to use a regmap. An example of such a device is the Cadence Sierra PHY. Allow creating a regmap with one range whose start and size can be specified by the driver based on calculations at runtime. Signed-off-by: Pratyush Yadav Reviewed-by: Simon Glass --- drivers/core/regmap.c | 27 +++++++++++++++++++++++++++ include/regmap.h | 19 +++++++++++++++++++ 2 files changed, 46 insertions(+) (limited to 'include') diff --git a/drivers/core/regmap.c b/drivers/core/regmap.c index 173ae808909..a9087df32b9 100644 --- a/drivers/core/regmap.c +++ b/drivers/core/regmap.c @@ -164,6 +164,33 @@ err: return ret; } +int regmap_init_mem_range(ofnode node, ulong r_start, ulong r_size, + struct regmap **mapp) +{ + struct regmap *map; + struct regmap_range *range; + + map = regmap_alloc(1); + if (!map) + return -ENOMEM; + + range = &map->ranges[0]; + range->start = r_start; + range->size = r_size; + + if (ofnode_read_bool(node, "little-endian")) + map->endianness = REGMAP_LITTLE_ENDIAN; + else if (ofnode_read_bool(node, "big-endian")) + map->endianness = REGMAP_BIG_ENDIAN; + else if (ofnode_read_bool(node, "native-endian")) + map->endianness = REGMAP_NATIVE_ENDIAN; + else /* Default: native endianness */ + map->endianness = REGMAP_NATIVE_ENDIAN; + + *mapp = map; + return 0; +} + int regmap_init_mem(ofnode node, struct regmap **mapp) { struct regmap_range *range; diff --git a/include/regmap.h b/include/regmap.h index e6c59dfbced..7c8ad047599 100644 --- a/include/regmap.h +++ b/include/regmap.h @@ -350,6 +350,25 @@ int regmap_init_mem_platdata(struct udevice *dev, fdt_val_t *reg, int count, int regmap_init_mem_index(ofnode node, struct regmap **mapp, int index); +/** + * regmap_init_mem_range() - Set up a new memory region for ofnode with the + * specified range. + * + * @node: The ofnode for the map. + * @r_start: Start of the range. + * @r_size: Size of the range. + * @mapp: Returns allocated map. + * + * Return: 0 in success, -errno otherwise + * + * This creates a regmap with one range where instead of extracting the range + * from 'node', it is created based on the parameters specified. This is + * useful when a driver needs to calculate the base of the regmap at runtime, + * and can't specify it in device tree. + */ +int regmap_init_mem_range(ofnode node, ulong r_start, ulong r_size, + struct regmap **mapp); + /** * devm_regmap_init() - Initialise register map (device managed) * -- cgit v1.3.1 From d8babb9598ce237ffb1feccb576c66a21c52e5f7 Mon Sep 17 00:00:00 2001 From: Pratyush Yadav Date: Thu, 24 Sep 2020 10:04:15 +0530 Subject: regmap: Allow devices to specify regmap range start and size in config Some devices need to calculate the regmap base address at runtime. This makes it impossible to use device tree to get the regmap base. Instead, allow devices to specify it in the regmap config. This will create a regmap with a single range that corresponds to the start and size given by the driver. Signed-off-by: Pratyush Yadav Reviewed-by: Simon Glass --- drivers/core/regmap.c | 6 +++++- include/regmap.h | 6 ++++++ 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/core/regmap.c b/drivers/core/regmap.c index a9087df32b9..a3da0cf7c3f 100644 --- a/drivers/core/regmap.c +++ b/drivers/core/regmap.c @@ -283,7 +283,11 @@ struct regmap *devm_regmap_init(struct udevice *dev, if (unlikely(!mapp)) return ERR_PTR(-ENOMEM); - rc = regmap_init_mem(dev_ofnode(dev), mapp); + if (config && config->r_size != 0) + rc = regmap_init_mem_range(dev_ofnode(dev), config->r_start, + config->r_size, mapp); + else + rc = regmap_init_mem(dev_ofnode(dev), mapp); if (rc) return ERR_PTR(rc); diff --git a/include/regmap.h b/include/regmap.h index 7c8ad047599..7a6fcc7f53b 100644 --- a/include/regmap.h +++ b/include/regmap.h @@ -84,10 +84,16 @@ struct regmap_bus; * REGMAP_SIZE_32 if set to 0. * @reg_offset_shift Left shift the register offset by this value before * performing read or write. + * @r_start: If specified, the regmap is created with one range + * which starts at this address, instead of finding the + * start from device tree. + * @r_size: Same as above for the range size */ struct regmap_config { enum regmap_size_t width; u32 reg_offset_shift; + ulong r_start; + ulong r_size; }; /** -- cgit v1.3.1 From 1c4db59d9bf711e7f8902eaa145959429d659679 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Thu, 24 Sep 2020 10:04:16 +0530 Subject: regmap: Add support for regmap fields A regmap field is an abstraction available in Linux. It provides to access bitfields in a regmap without having to worry about shifts and masks. Signed-off-by: Jean-Jacques Hiblot Reviewed-by: Simon Glass Signed-off-by: Pratyush Yadav --- drivers/core/regmap.c | 83 ++++++++++++++++++++++++++++++++++ include/regmap.h | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 205 insertions(+) (limited to 'include') diff --git a/drivers/core/regmap.c b/drivers/core/regmap.c index a3da0cf7c3f..c2bed88eac4 100644 --- a/drivers/core/regmap.c +++ b/drivers/core/regmap.c @@ -18,6 +18,20 @@ #include #include #include +#include + +/* + * Internal representation of a regmap field. Instead of storing the MSB and + * LSB, store the shift and mask. This makes the code a bit cleaner and faster + * because the shift and mask don't have to be calculated every time. + */ +struct regmap_field { + struct regmap *regmap; + unsigned int mask; + /* lsb */ + unsigned int shift; + unsigned int reg; +}; DECLARE_GLOBAL_DATA_PTR; @@ -547,3 +561,72 @@ int regmap_update_bits(struct regmap *map, uint offset, uint mask, uint val) return regmap_write(map, offset, reg | (val & mask)); } + +int regmap_field_read(struct regmap_field *field, unsigned int *val) +{ + int ret; + unsigned int reg_val; + + ret = regmap_read(field->regmap, field->reg, ®_val); + if (ret != 0) + return ret; + + reg_val &= field->mask; + reg_val >>= field->shift; + *val = reg_val; + + return ret; +} + +int regmap_field_write(struct regmap_field *field, unsigned int val) +{ + return regmap_update_bits(field->regmap, field->reg, field->mask, + val << field->shift); +} + +static void regmap_field_init(struct regmap_field *rm_field, + struct regmap *regmap, + struct reg_field reg_field) +{ + rm_field->regmap = regmap; + rm_field->reg = reg_field.reg; + rm_field->shift = reg_field.lsb; + rm_field->mask = GENMASK(reg_field.msb, reg_field.lsb); +} + +struct regmap_field *devm_regmap_field_alloc(struct udevice *dev, + struct regmap *regmap, + struct reg_field reg_field) +{ + struct regmap_field *rm_field = devm_kzalloc(dev, sizeof(*rm_field), + GFP_KERNEL); + if (!rm_field) + return ERR_PTR(-ENOMEM); + + regmap_field_init(rm_field, regmap, reg_field); + + return rm_field; +} + +void devm_regmap_field_free(struct udevice *dev, struct regmap_field *field) +{ + devm_kfree(dev, field); +} + +struct regmap_field *regmap_field_alloc(struct regmap *regmap, + struct reg_field reg_field) +{ + struct regmap_field *rm_field = kzalloc(sizeof(*rm_field), GFP_KERNEL); + + if (!rm_field) + return ERR_PTR(-ENOMEM); + + regmap_field_init(rm_field, regmap, reg_field); + + return rm_field; +} + +void regmap_field_free(struct regmap_field *field) +{ + kfree(field); +} diff --git a/include/regmap.h b/include/regmap.h index 7a6fcc7f53b..c6258face30 100644 --- a/include/regmap.h +++ b/include/regmap.h @@ -312,6 +312,43 @@ int regmap_raw_read_range(struct regmap *map, uint range_num, uint offset, regmap_read_poll_timeout_test(map, addr, val, cond, sleep_us, \ timeout_ms, 0) \ +/** + * regmap_field_read_poll_timeout - Poll until a condition is met or a timeout + * occurs + * + * @field: Regmap field to read from + * @val: Unsigned integer variable to read the value into + * @cond: Break condition (usually involving @val) + * @sleep_us: Maximum time to sleep between reads in us (0 tight-loops). + * @timeout_ms: Timeout in ms, 0 means never timeout + * + * Returns 0 on success and -ETIMEDOUT upon a timeout or the regmap_field_read + * error return value in case of a error read. In the two former cases, + * the last read value at @addr is stored in @val. + * + * This is modelled after the regmap_read_poll_timeout macros in linux but + * with millisecond timeout. + */ +#define regmap_field_read_poll_timeout(field, val, cond, sleep_us, timeout_ms) \ +({ \ + unsigned long __start = get_timer(0); \ + int __ret; \ + for (;;) { \ + __ret = regmap_field_read((field), &(val)); \ + if (__ret) \ + break; \ + if (cond) \ + break; \ + if ((timeout_ms) && get_timer(__start) > (timeout_ms)) { \ + __ret = regmap_field_read((field), &(val)); \ + break; \ + } \ + if ((sleep_us)) \ + udelay((sleep_us)); \ + } \ + __ret ?: ((cond) ? 0 : -ETIMEDOUT); \ +}) + /** * regmap_update_bits() - Perform a read/modify/write using a mask * @@ -407,4 +444,89 @@ void *regmap_get_range(struct regmap *map, unsigned int range_num); */ int regmap_uninit(struct regmap *map); +/** + * struct reg_field - Description of an register field + * + * @reg: Offset of the register within the regmap bank + * @lsb: lsb of the register field. + * @msb: msb of the register field. + */ +struct reg_field { + unsigned int reg; + unsigned int lsb; + unsigned int msb; +}; + +struct regmap_field; + +/** + * REG_FIELD() - A convenient way to initialize a 'struct reg_feild'. + * + * @_reg: Offset of the register within the regmap bank + * @_lsb: lsb of the register field. + * @_msb: msb of the register field. + * + * Register fields are often described in terms of 3 things: the register it + * belongs to, its LSB, and its MSB. This macro can be used by drivers to + * clearly and easily initialize a 'struct regmap_field'. + * + * For example, say a device has a register at offset DEV_REG1 (0x100) and a + * field of DEV_REG1 is on bits [7:3]. So a driver can initialize a regmap + * field for this by doing: + * struct reg_field field = REG_FIELD(DEV_REG1, 3, 7); + */ +#define REG_FIELD(_reg, _lsb, _msb) { \ + .reg = _reg, \ + .lsb = _lsb, \ + .msb = _msb, \ + } + +/** + * devm_regmap_field_alloc() - Allocate and initialise a register field. + * + * @dev: Device that will be interacted with + * @regmap: regmap bank in which this register field is located. + * @reg_field: Register field with in the bank. + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap_field. The regmap_field will be automatically freed + * by the device management code. + */ +struct regmap_field *devm_regmap_field_alloc(struct udevice *dev, + struct regmap *regmap, + struct reg_field reg_field); +/** + * devm_regmap_field_free() - Free a register field allocated using + * devm_regmap_field_alloc. + * + * @dev: Device that will be interacted with + * @field: regmap field which should be freed. + * + * Free register field allocated using devm_regmap_field_alloc(). Usually + * drivers need not call this function, as the memory allocated via devm + * will be freed as per device-driver life-cyle. + */ +void devm_regmap_field_free(struct udevice *dev, struct regmap_field *field); + +/** + * regmap_field_write() - Write a value to a regmap field + * + * @field: Regmap field to write to + * @val: Data to write to the regmap at the specified offset + * + * Return: 0 if OK, -ve on error + */ +int regmap_field_write(struct regmap_field *field, unsigned int val); + +/** + * regmap_read() - Read a 32-bit value from a regmap + * + * @field: Regmap field to write to + * @valp: Pointer to the buffer to receive the data read from the regmap + * field + * + * Return: 0 if OK, -ve on error + */ +int regmap_field_read(struct regmap_field *field, unsigned int *val); + #endif -- cgit v1.3.1 From 358599efd827b0ee48af864537cc86facc9167c0 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Wed, 9 Sep 2020 18:44:00 +0200 Subject: firmware: add SCMI agent uclass This change introduces SCMI agent uclass to interact with a firmware using the SCMI protocols [1]. SCMI agent uclass currently supports a single method to request processing of the SCMI message by an identified server. A SCMI message is made of a byte payload associated to a protocol ID and a message ID, all defined by the SCMI specification [1]. On return from process_msg() method, the caller gets the service response. SCMI agent uclass defines a post bind generic sequence for all devices. The sequence binds all the SCMI protocols listed in the FDT for that SCMI agent device. Currently none, but later change will introduce protocols. This change implements a simple sandbox device for the SCMI agent uclass. The sandbox nicely answers SCMI_NOT_SUPPORTED to SCMI messages. To prepare for further test support, the sandbox exposes a architecture function for test application to read the sandbox emulated devices state. Currently supports 2 SCMI agents, identified by an ID in the FDT device name. The simplistic DM test does nothing yet. SCMI agent uclass is designed for platforms that embed a SCMI server in a firmware hosted somewhere, for example in a companion co-processor or in the secure world of the executing processor. SCMI protocols allow an SCMI agent to discover and access external resources as clock, reset controllers and more. SCMI agent and server communicate following the SCMI specification [1]. This SCMI agent implementation complies with the DT bindings defined in the Linux kernel source tree regarding SCMI agent description since v5.8. Links: [1] https://developer.arm.com/architectures/system-architectures/software-standards/scmi Signed-off-by: Etienne Carriere Cc: Simon Glass Cc: Peng Fan Cc: Sudeep Holla Reviewed-by: Simon Glass --- arch/sandbox/dts/test.dts | 16 ++++ arch/sandbox/include/asm/scmi_test.h | 43 +++++++++ configs/sandbox_defconfig | 2 + drivers/firmware/Kconfig | 2 + drivers/firmware/Makefile | 1 + drivers/firmware/scmi/Kconfig | 17 ++++ drivers/firmware/scmi/Makefile | 2 + drivers/firmware/scmi/sandbox-scmi_agent.c | 142 +++++++++++++++++++++++++++++ drivers/firmware/scmi/scmi_agent-uclass.c | 107 ++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/scmi_agent-uclass.h | 24 +++++ include/scmi_agent.h | 68 ++++++++++++++ include/scmi_protocols.h | 41 +++++++++ test/dm/Makefile | 1 + test/dm/scmi.c | 38 ++++++++ 15 files changed, 505 insertions(+) create mode 100644 arch/sandbox/include/asm/scmi_test.h create mode 100644 drivers/firmware/scmi/Kconfig create mode 100644 drivers/firmware/scmi/Makefile create mode 100644 drivers/firmware/scmi/sandbox-scmi_agent.c create mode 100644 drivers/firmware/scmi/scmi_agent-uclass.c create mode 100644 include/scmi_agent-uclass.h create mode 100644 include/scmi_agent.h create mode 100644 include/scmi_protocols.h create mode 100644 test/dm/scmi.c (limited to 'include') diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 2523e597237..4769ec08669 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -366,6 +366,22 @@ sandbox_firmware: sandbox-firmware { compatible = "sandbox,firmware"; }; + + sandbox-scmi-agent@0 { + compatible = "sandbox,scmi-agent"; + #address-cells = <1>; + #size-cells = <0>; + }; + + sandbox-scmi-agent@1 { + compatible = "sandbox,scmi-agent"; + #address-cells = <1>; + #size-cells = <0>; + + protocol@10 { + reg = <0x10>; + }; + }; }; pinctrl-gpio { diff --git a/arch/sandbox/include/asm/scmi_test.h b/arch/sandbox/include/asm/scmi_test.h new file mode 100644 index 00000000000..a811fe19c35 --- /dev/null +++ b/arch/sandbox/include/asm/scmi_test.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020, Linaro Limited + */ + +#ifndef __SANDBOX_SCMI_TEST_H +#define __SANDBOX_SCMI_TEST_H + +struct udevice; +struct sandbox_scmi_agent; +struct sandbox_scmi_service; + +/** + * struct sandbox_scmi_agent - Simulated SCMI service seen by SCMI agent + * @idx: Identifier for the SCMI agent, its index + */ +struct sandbox_scmi_agent { + uint idx; +}; + +/** + * struct sandbox_scmi_service - Reference to simutaed SCMI agents/services + * @agent: Pointer to SCMI sandbox agent pointers array + * @agent_count: Number of emulated agents exposed in array @agent. + */ +struct sandbox_scmi_service { + struct sandbox_scmi_agent **agent; + size_t agent_count; +}; + +#ifdef CONFIG_SCMI_FIRMWARE +/** + * sandbox_scmi_service_context - Get the simulated SCMI services context + * @return: Reference to backend simulated resources state + */ +struct sandbox_scmi_service *sandbox_scmi_service_ctx(void); +#else +static inline struct sandbox_scmi_service *sandbox_scmi_service_ctx(void) +{ + return NULL; +} +#endif /* CONFIG_SCMI_FIRMWARE */ +#endif /* __SANDBOX_SCMI_TEST_H */ diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 6e9f029cc96..2c130c01f07 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -132,6 +132,8 @@ CONFIG_BOARD_SANDBOX=y CONFIG_DMA=y CONFIG_DMA_CHANNELS=y CONFIG_SANDBOX_DMA=y +CONFIG_FIRMWARE=y +CONFIG_SCMI_FIRMWARE=y CONFIG_GPIO_HOG=y CONFIG_DM_GPIO_LOOKUP_LABEL=y CONFIG_PM8916_GPIO=y diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index b70a2063551..ef958b3a7a4 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -36,3 +36,5 @@ config ZYNQMP_FIRMWARE various platform management services. Say yes to enable ZynqMP firmware interface driver. If in doubt, say N. + +source "drivers/firmware/scmi/Kconfig" diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index a0c250a473e..7ce83d72bd3 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_$(SPL_)ARM_PSCI_FW) += psci.o obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o obj-$(CONFIG_SANDBOX) += firmware-sandbox.o obj-$(CONFIG_ZYNQMP_FIRMWARE) += firmware-zynqmp.o +obj-$(CONFIG_SCMI_FIRMWARE) += scmi/ diff --git a/drivers/firmware/scmi/Kconfig b/drivers/firmware/scmi/Kconfig new file mode 100644 index 00000000000..57e2ebbe428 --- /dev/null +++ b/drivers/firmware/scmi/Kconfig @@ -0,0 +1,17 @@ +config SCMI_FIRMWARE + bool "Enable SCMI support" + select FIRMWARE + select OF_TRANSLATE + depends on SANDBOX + help + System Control and Management Interface (SCMI) is a communication + protocol that defines standard interfaces for power, performance + and system management. The SCMI specification is available at + https://developer.arm.com/architectures/system-architectures/software-standards/scmi + + An SCMI agent communicates with a related SCMI server firmware + located in another sub-system, as a companion micro controller + or a companion host in the CPU system. + + Communications between agent (client) and the SCMI server are + based on message exchange. diff --git a/drivers/firmware/scmi/Makefile b/drivers/firmware/scmi/Makefile new file mode 100644 index 00000000000..336ea1f2a32 --- /dev/null +++ b/drivers/firmware/scmi/Makefile @@ -0,0 +1,2 @@ +obj-y += scmi_agent-uclass.o +obj-$(CONFIG_SANDBOX) += sandbox-scmi_agent.o diff --git a/drivers/firmware/scmi/sandbox-scmi_agent.c b/drivers/firmware/scmi/sandbox-scmi_agent.c new file mode 100644 index 00000000000..3179438aabf --- /dev/null +++ b/drivers/firmware/scmi/sandbox-scmi_agent.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020, Linaro Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The sandbox SCMI agent driver simulates to some extend a SCMI message + * processing. It simulates few of the SCMI services for some of the + * SCMI protocols embedded in U-Boot. Currently none. + * + * This driver simulates 2 SCMI agents for test purpose. + * + * This Driver exports sandbox_scmi_service_ct() for the test sequence to + * get the state of the simulated services (clock state, rate, ...) and + * check back-end device state reflects the request send through the + * various uclass devices, currently nothing. + */ + +#define SANDBOX_SCMI_AGENT_COUNT 2 + +/* The list saves to simulted end devices references for test purpose */ +struct sandbox_scmi_agent *sandbox_scmi_agent_list[SANDBOX_SCMI_AGENT_COUNT]; + +static struct sandbox_scmi_service sandbox_scmi_service_state = { + .agent = sandbox_scmi_agent_list, + .agent_count = SANDBOX_SCMI_AGENT_COUNT, +}; + +struct sandbox_scmi_service *sandbox_scmi_service_ctx(void) +{ + return &sandbox_scmi_service_state; +} + +static void debug_print_agent_state(struct udevice *dev, char *str) +{ + struct sandbox_scmi_agent *agent = dev_get_priv(dev); + + dev_dbg(dev, "Dump sandbox_scmi_agent %u: %s\n", agent->idx, str); +}; + +static int sandbox_scmi_test_process_msg(struct udevice *dev, + struct scmi_msg *msg) +{ + switch (msg->protocol_id) { + case SCMI_PROTOCOL_ID_BASE: + case SCMI_PROTOCOL_ID_POWER_DOMAIN: + case SCMI_PROTOCOL_ID_SYSTEM: + case SCMI_PROTOCOL_ID_PERF: + case SCMI_PROTOCOL_ID_CLOCK: + case SCMI_PROTOCOL_ID_SENSOR: + case SCMI_PROTOCOL_ID_RESET_DOMAIN: + *(u32 *)msg->out_msg = SCMI_NOT_SUPPORTED; + return 0; + default: + break; + } + + dev_err(dev, "%s(%s): Unhandled protocol_id %#x/message_id %#x\n", + __func__, dev->name, msg->protocol_id, msg->message_id); + + if (msg->out_msg_sz < sizeof(u32)) + return -EINVAL; + + /* Intentionnaly report unhandled IDs through the SCMI return code */ + *(u32 *)msg->out_msg = SCMI_PROTOCOL_ERROR; + return 0; +} + +static int sandbox_scmi_test_remove(struct udevice *dev) +{ + struct sandbox_scmi_agent *agent = dev_get_priv(dev); + + debug_print_agent_state(dev, "removed"); + + /* We only need to dereference the agent in the context */ + sandbox_scmi_service_ctx()->agent[agent->idx] = NULL; + + return 0; +} + +static int sandbox_scmi_test_probe(struct udevice *dev) +{ + static const char basename[] = "sandbox-scmi-agent@"; + struct sandbox_scmi_agent *agent = dev_get_priv(dev); + const size_t basename_size = sizeof(basename) - 1; + + if (strncmp(basename, dev->name, basename_size)) + return -ENOENT; + + switch (dev->name[basename_size]) { + case '0': + *agent = (struct sandbox_scmi_agent){ + .idx = 0, + }; + break; + case '1': + *agent = (struct sandbox_scmi_agent){ + .idx = 1, + }; + break; + default: + dev_err(dev, "%s(): Unexpected agent ID %s\n", + __func__, dev->name + basename_size); + return -ENOENT; + } + + debug_print_agent_state(dev, "probed"); + + /* Save reference for tests purpose */ + sandbox_scmi_service_ctx()->agent[agent->idx] = agent; + + return 0; +}; + +static const struct udevice_id sandbox_scmi_test_ids[] = { + { .compatible = "sandbox,scmi-agent" }, + { } +}; + +struct scmi_agent_ops sandbox_scmi_test_ops = { + .process_msg = sandbox_scmi_test_process_msg, +}; + +U_BOOT_DRIVER(sandbox_scmi_agent) = { + .name = "sandbox-scmi_agent", + .id = UCLASS_SCMI_AGENT, + .of_match = sandbox_scmi_test_ids, + .priv_auto_alloc_size = sizeof(struct sandbox_scmi_agent), + .probe = sandbox_scmi_test_probe, + .remove = sandbox_scmi_test_remove, + .ops = &sandbox_scmi_test_ops, +}; diff --git a/drivers/firmware/scmi/scmi_agent-uclass.c b/drivers/firmware/scmi/scmi_agent-uclass.c new file mode 100644 index 00000000000..67a6f907c99 --- /dev/null +++ b/drivers/firmware/scmi/scmi_agent-uclass.c @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020 Linaro Limited. + */ + +#include +#include +#include +#include +#include + +#include +#include + +/** + * struct error_code - Helper structure for SCMI error code conversion + * @scmi: SCMI error code + * @errno: Related standard error number + */ +struct error_code { + int scmi; + int errno; +}; + +static const struct error_code scmi_linux_errmap[] = { + { .scmi = SCMI_NOT_SUPPORTED, .errno = -EOPNOTSUPP, }, + { .scmi = SCMI_INVALID_PARAMETERS, .errno = -EINVAL, }, + { .scmi = SCMI_DENIED, .errno = -EACCES, }, + { .scmi = SCMI_NOT_FOUND, .errno = -ENOENT, }, + { .scmi = SCMI_OUT_OF_RANGE, .errno = -ERANGE, }, + { .scmi = SCMI_BUSY, .errno = -EBUSY, }, + { .scmi = SCMI_COMMS_ERROR, .errno = -ECOMM, }, + { .scmi = SCMI_GENERIC_ERROR, .errno = -EIO, }, + { .scmi = SCMI_HARDWARE_ERROR, .errno = -EREMOTEIO, }, + { .scmi = SCMI_PROTOCOL_ERROR, .errno = -EPROTO, }, +}; + +int scmi_to_linux_errno(s32 scmi_code) +{ + int n; + + if (!scmi_code) + return 0; + + for (n = 0; n < ARRAY_SIZE(scmi_linux_errmap); n++) + if (scmi_code == scmi_linux_errmap[n].scmi) + return scmi_linux_errmap[1].errno; + + return -EPROTO; +} + +/* + * SCMI agent devices binds devices of various uclasses depeding on + * the FDT description. scmi_bind_protocol() is a generic bind sequence + * called by the uclass at bind stage, that is uclass post_bind. + */ +static int scmi_bind_protocols(struct udevice *dev) +{ + int ret = 0; + ofnode node; + struct driver *drv; + + dev_for_each_subnode(node, dev) { + u32 protocol_id; + + if (!ofnode_is_available(node)) + continue; + + if (ofnode_read_u32(node, "reg", &protocol_id)) + continue; + + switch (protocol_id) { + default: + dev_info(dev, "Ignore unsupported SCMI protocol %#x\n", + protocol_id); + continue; + } + + ret = device_bind_ofnode(dev, drv, ofnode_get_name(node), + NULL, node, NULL); + if (ret) + break; + } + + return ret; +} + +static const struct scmi_agent_ops *transport_dev_ops(struct udevice *dev) +{ + return (const struct scmi_agent_ops *)dev->driver->ops; +} + +int devm_scmi_process_msg(struct udevice *dev, struct scmi_msg *msg) +{ + const struct scmi_agent_ops *ops = transport_dev_ops(dev); + + if (ops->process_msg) + return ops->process_msg(dev, msg); + + return -EPROTONOSUPPORT; +} + +UCLASS_DRIVER(scmi_agent) = { + .id = UCLASS_SCMI_AGENT, + .name = "scmi_agent", + .post_bind = scmi_bind_protocols, +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 4ec5fa6670a..88f10c46221 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -94,6 +94,7 @@ enum uclass_id { UCLASS_RESET, /* Reset controller device */ UCLASS_RNG, /* Random Number Generator */ UCLASS_RTC, /* Real time clock device */ + UCLASS_SCMI_AGENT, /* Interface with an SCMI server */ UCLASS_SCSI, /* SCSI device */ UCLASS_SERIAL, /* Serial UART */ UCLASS_SIMPLE_BUS, /* Bus with child devices */ diff --git a/include/scmi_agent-uclass.h b/include/scmi_agent-uclass.h new file mode 100644 index 00000000000..a501d1b4825 --- /dev/null +++ b/include/scmi_agent-uclass.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2019-2020 Linaro Limited. + */ +#ifndef _SCMI_AGENT_UCLASS_H +#define _SCMI_AGENT_UCLASS_H + +struct udevice; +struct scmi_msg; + +/** + * struct scmi_transport_ops - The functions that a SCMI transport layer must implement. + */ +struct scmi_agent_ops { + /* + * process_msg - Request transport to get the SCMI message processed + * + * @agent: Agent using the transport + * @msg: SCMI message to be transmitted + */ + int (*process_msg)(struct udevice *dev, struct scmi_msg *msg); +}; + +#endif /* _SCMI_TRANSPORT_UCLASS_H */ diff --git a/include/scmi_agent.h b/include/scmi_agent.h new file mode 100644 index 00000000000..f1be9ff2091 --- /dev/null +++ b/include/scmi_agent.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved. + * Copyright (C) 2019-2020, Linaro Limited + * + * An SCMI agent device represent on communication path from a + * device driver to the remote SCMI server which driver sends + * messages to and receives response messages from. + */ +#ifndef SCMI_AGENT_H +#define SCMI_AGENT_H + +#include + +struct udevice; + +/* + * struct scmi_msg - Context of a SCMI message sent and the response received + * + * @protocol_id: SCMI protocol ID + * @message_id: SCMI message ID for a defined protocol ID + * @in_msg: Pointer to the message payload sent by the driver + * @in_msg_sz: Byte size of the message payload sent + * @out_msg: Pointer to buffer to store response message payload + * @out_msg_sz: Byte size of the response buffer and response payload + */ +struct scmi_msg { + unsigned int protocol_id; + unsigned int message_id; + u8 *in_msg; + size_t in_msg_sz; + u8 *out_msg; + size_t out_msg_sz; +}; + +/* Helper macro to match a message on input/output array references */ +#define SCMI_MSG_IN(_protocol, _message, _in_array, _out_array) \ + (struct scmi_msg){ \ + .protocol_id = (_protocol), \ + .message_id = (_message), \ + .in_msg = (uint8_t *)&(_in_array), \ + .in_msg_sz = sizeof(_in_array), \ + .out_msg = (uint8_t *)&(_out_array), \ + .out_msg_sz = sizeof(_out_array), \ + } + +/** + * scmi_send_and_process_msg() - send and process a SCMI message + * + * Send a message to a SCMI server through a target SCMI agent device. + * Caller sets scmi_msg::out_msg_sz to the output message buffer size. + * On return, scmi_msg::out_msg_sz stores the response payload size. + * + * @dev: SCMI agent device + * @msg: Message structure reference + * @return 0 on success and a negative errno on failure + */ +int devm_scmi_process_msg(struct udevice *dev, struct scmi_msg *msg); + +/** + * scmi_to_linux_errno() - Convert an SCMI error code into a Linux errno code + * + * @scmi_errno: SCMI error code value + * @return 0 for successful status and a negative errno otherwise + */ +int scmi_to_linux_errno(s32 scmi_errno); + +#endif /* SCMI_H */ diff --git a/include/scmi_protocols.h b/include/scmi_protocols.h new file mode 100644 index 00000000000..86a2d109c8c --- /dev/null +++ b/include/scmi_protocols.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved. + * Copyright (C) 2019-2020, Linaro Limited + */ +#ifndef _SCMI_PROTOCOLS_H +#define _SCMI_PROTOCOLS_H + +#include + +/* + * Subset the SCMI protocols definition + * based on SCMI specification v2.0 (DEN0056B) + * https://developer.arm.com/docs/den0056/b + */ + +enum scmi_std_protocol { + SCMI_PROTOCOL_ID_BASE = 0x10, + SCMI_PROTOCOL_ID_POWER_DOMAIN = 0x11, + SCMI_PROTOCOL_ID_SYSTEM = 0x12, + SCMI_PROTOCOL_ID_PERF = 0x13, + SCMI_PROTOCOL_ID_CLOCK = 0x14, + SCMI_PROTOCOL_ID_SENSOR = 0x15, + SCMI_PROTOCOL_ID_RESET_DOMAIN = 0x16, +}; + +enum scmi_status_code { + SCMI_SUCCESS = 0, + SCMI_NOT_SUPPORTED = -1, + SCMI_INVALID_PARAMETERS = -2, + SCMI_DENIED = -3, + SCMI_NOT_FOUND = -4, + SCMI_OUT_OF_RANGE = -5, + SCMI_BUSY = -6, + SCMI_COMMS_ERROR = -7, + SCMI_GENERIC_ERROR = -8, + SCMI_HARDWARE_ERROR = -9, + SCMI_PROTOCOL_ERROR = -10, +}; + +#endif /* _SCMI_PROTOCOLS_H */ diff --git a/test/dm/Makefile b/test/dm/Makefile index 864c8d0b4ce..70ba1b66953 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -80,4 +80,5 @@ obj-$(CONFIG_DM_RNG) += rng.o obj-$(CONFIG_CLK_K210_SET_RATE) += k210_pll.o obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o obj-$(CONFIG_RESET_SYSCON) += syscon-reset.o +obj-$(CONFIG_SCMI_FIRMWARE) += scmi.o endif diff --git a/test/dm/scmi.c b/test/dm/scmi.c new file mode 100644 index 00000000000..d8c1e71f127 --- /dev/null +++ b/test/dm/scmi.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020, Linaro Limited + * + * Tests scmi_agent uclass and the SCMI drivers implemented in other + * uclass devices probe when a SCMI server exposes resources. + * + * Note in test.dts the protocol@10 node in agent 1. Protocol 0x10 is not + * implemented in U-Boot SCMI components but the implementation is exepected + * to not complain on unknown protocol IDs, as long as it is not used. Note + * in test.dts tests that SCMI drivers probing does not fail for such an + * unknown SCMI protocol ID. + */ + +#include +#include +#include +#include +#include +#include + +/* + * Test SCMI states when loading and releasing resources + * related to SCMI drivers. + */ +static int dm_test_scmi_sandbox_agent(struct unit_test_state *uts) +{ + struct sandbox_scmi_service *scmi_ctx = sandbox_scmi_service_ctx(); + + ut_assertnonnull(scmi_ctx); + ut_asserteq(2, scmi_ctx->agent_count); + ut_assertnull(scmi_ctx->agent[0]); + ut_assertnull(scmi_ctx->agent[1]); + + return 0; +} + +DM_TEST(dm_test_scmi_sandbox_agent, UT_TESTF_SCAN_FDT); -- cgit v1.3.1 From 60388844836f5639e6c9a4331335ff22298128da Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Wed, 9 Sep 2020 18:44:04 +0200 Subject: clk: add clock driver for SCMI agents This change introduces a clock driver for SCMI agent devices. When SCMI agent and SCMI clock drivers are enabled, SCMI agent binds a clock device for each SCMI clock protocol devices enabled in the FDT. SCMI clock driver is embedded upon CONFIG_CLK_SCMI=y. If enabled, CONFIG_SCMI_AGENT is also enabled. SCMI Clock protocol is defined in the SCMI specification [1]. Links: [1] https://developer.arm.com/architectures/system-architectures/software-standards/scmi Signed-off-by: Etienne Carriere Cc: Lukasz Majewski Cc: Simon Glass Cc: Peng Fan Cc: Sudeep Holla Reviewed-by: Simon Glass --- drivers/clk/Kconfig | 8 +++ drivers/clk/Makefile | 1 + drivers/clk/clk_scmi.c | 99 +++++++++++++++++++++++++++++++ drivers/firmware/scmi/scmi_agent-uclass.c | 14 ++++- include/scmi_protocols.h | 78 ++++++++++++++++++++++++ 5 files changed, 197 insertions(+), 3 deletions(-) create mode 100644 drivers/clk/clk_scmi.c (limited to 'include') diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 6003e140b58..4dfbad7986b 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -159,6 +159,14 @@ config CLK_CDCE9XX Enable the clock synthesizer driver for CDCE913/925/937/949 series of chips. +config CLK_SCMI + bool "Enable SCMI clock driver" + depends on SCMI_FIRMWARE + help + Enable this option if you want to support clock devices exposed + by a SCMI agent based on SCMI clock protocol communication + with a SCMI server. + source "drivers/clk/analogbits/Kconfig" source "drivers/clk/at91/Kconfig" source "drivers/clk/exynos/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index cda4b4b605f..d1e295ac7c1 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_CLK_MPC83XX) += mpc83xx_clk.o obj-$(CONFIG_CLK_OCTEON) += clk_octeon.o obj-$(CONFIG_CLK_OWL) += owl/ obj-$(CONFIG_CLK_RENESAS) += renesas/ +obj-$(CONFIG_CLK_SCMI) += clk_scmi.o obj-$(CONFIG_CLK_SIFIVE) += sifive/ obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-$(CONFIG_CLK_STM32F) += clk_stm32f.o diff --git a/drivers/clk/clk_scmi.c b/drivers/clk/clk_scmi.c new file mode 100644 index 00000000000..93a4819501c --- /dev/null +++ b/drivers/clk/clk_scmi.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019-2020 Linaro Limited + */ +#include +#include +#include +#include +#include +#include + +static int scmi_clk_gate(struct clk *clk, int enable) +{ + struct scmi_clk_state_in in = { + .clock_id = clk->id, + .attributes = enable, + }; + struct scmi_clk_state_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK, + SCMI_CLOCK_CONFIG_SET, + in, out); + int ret; + + ret = devm_scmi_process_msg(clk->dev->parent, &msg); + if (ret) + return ret; + + return scmi_to_linux_errno(out.status); +} + +static int scmi_clk_enable(struct clk *clk) +{ + return scmi_clk_gate(clk, 1); +} + +static int scmi_clk_disable(struct clk *clk) +{ + return scmi_clk_gate(clk, 0); +} + +static ulong scmi_clk_get_rate(struct clk *clk) +{ + struct scmi_clk_rate_get_in in = { + .clock_id = clk->id, + }; + struct scmi_clk_rate_get_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK, + SCMI_CLOCK_RATE_GET, + in, out); + int ret; + + ret = devm_scmi_process_msg(clk->dev->parent, &msg); + if (ret < 0) + return ret; + + ret = scmi_to_linux_errno(out.status); + if (ret < 0) + return ret; + + return (ulong)(((u64)out.rate_msb << 32) | out.rate_lsb); +} + +static ulong scmi_clk_set_rate(struct clk *clk, ulong rate) +{ + struct scmi_clk_rate_set_in in = { + .clock_id = clk->id, + .flags = SCMI_CLK_RATE_ROUND_CLOSEST, + .rate_lsb = (u32)rate, + .rate_msb = (u32)((u64)rate >> 32), + }; + struct scmi_clk_rate_set_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK, + SCMI_CLOCK_RATE_SET, + in, out); + int ret; + + ret = devm_scmi_process_msg(clk->dev->parent, &msg); + if (ret < 0) + return ret; + + ret = scmi_to_linux_errno(out.status); + if (ret < 0) + return ret; + + return scmi_clk_get_rate(clk); +} + +static const struct clk_ops scmi_clk_ops = { + .enable = scmi_clk_enable, + .disable = scmi_clk_disable, + .get_rate = scmi_clk_get_rate, + .set_rate = scmi_clk_set_rate, +}; + +U_BOOT_DRIVER(scmi_clock) = { + .name = "scmi_clk", + .id = UCLASS_CLK, + .ops = &scmi_clk_ops, +}; diff --git a/drivers/firmware/scmi/scmi_agent-uclass.c b/drivers/firmware/scmi/scmi_agent-uclass.c index 67a6f907c99..1f36f23b6d8 100644 --- a/drivers/firmware/scmi/scmi_agent-uclass.c +++ b/drivers/firmware/scmi/scmi_agent-uclass.c @@ -58,9 +58,9 @@ static int scmi_bind_protocols(struct udevice *dev) { int ret = 0; ofnode node; - struct driver *drv; dev_for_each_subnode(node, dev) { + struct driver *drv = NULL; u32 protocol_id; if (!ofnode_is_available(node)) @@ -70,9 +70,17 @@ static int scmi_bind_protocols(struct udevice *dev) continue; switch (protocol_id) { + case SCMI_PROTOCOL_ID_CLOCK: + if (IS_ENABLED(CONFIG_CLK_SCMI)) + drv = DM_GET_DRIVER(scmi_clock); + break; default: - dev_info(dev, "Ignore unsupported SCMI protocol %#x\n", - protocol_id); + break; + } + + if (!drv) { + dev_dbg(dev, "Ignore unsupported SCMI protocol %#x\n", + protocol_id); continue; } diff --git a/include/scmi_protocols.h b/include/scmi_protocols.h index 86a2d109c8c..4778bcfc474 100644 --- a/include/scmi_protocols.h +++ b/include/scmi_protocols.h @@ -7,6 +7,7 @@ #define _SCMI_PROTOCOLS_H #include +#include /* * Subset the SCMI protocols definition @@ -38,4 +39,81 @@ enum scmi_status_code { SCMI_PROTOCOL_ERROR = -10, }; +/* + * SCMI Clock Protocol + */ + +enum scmi_clock_message_id { + SCMI_CLOCK_RATE_SET = 0x5, + SCMI_CLOCK_RATE_GET = 0x6, + SCMI_CLOCK_CONFIG_SET = 0x7, +}; + +#define SCMI_CLK_RATE_ASYNC_NOTIFY BIT(0) +#define SCMI_CLK_RATE_ASYNC_NORESP (BIT(0) | BIT(1)) +#define SCMI_CLK_RATE_ROUND_DOWN 0 +#define SCMI_CLK_RATE_ROUND_UP BIT(2) +#define SCMI_CLK_RATE_ROUND_CLOSEST BIT(3) + +/** + * struct scmi_clk_state_in - Message payload for CLOCK_CONFIG_SET command + * @clock_id: SCMI clock ID + * @attributes: Attributes of the targets clock state + */ +struct scmi_clk_state_in { + u32 clock_id; + u32 attributes; +}; + +/** + * struct scmi_clk_state_out - Response payload for CLOCK_CONFIG_SET command + * @status: SCMI command status + */ +struct scmi_clk_state_out { + s32 status; +}; + +/** + * struct scmi_clk_state_in - Message payload for CLOCK_RATE_GET command + * @clock_id: SCMI clock ID + * @attributes: Attributes of the targets clock state + */ +struct scmi_clk_rate_get_in { + u32 clock_id; +}; + +/** + * struct scmi_clk_rate_get_out - Response payload for CLOCK_RATE_GET command + * @status: SCMI command status + * @rate_lsb: 32bit LSB of the clock rate in Hertz + * @rate_msb: 32bit MSB of the clock rate in Hertz + */ +struct scmi_clk_rate_get_out { + s32 status; + u32 rate_lsb; + u32 rate_msb; +}; + +/** + * struct scmi_clk_state_in - Message payload for CLOCK_RATE_SET command + * @clock_id: SCMI clock ID + * @flags: Flags for the clock rate set request + * @rate_lsb: 32bit LSB of the clock rate in Hertz + * @rate_msb: 32bit MSB of the clock rate in Hertz + */ +struct scmi_clk_rate_set_in { + u32 clock_id; + u32 flags; + u32 rate_lsb; + u32 rate_msb; +}; + +/** + * struct scmi_clk_rate_set_out - Response payload for CLOCK_RATE_SET command + * @status: SCMI command status + */ +struct scmi_clk_rate_set_out { + s32 status; +}; + #endif /* _SCMI_PROTOCOLS_H */ -- cgit v1.3.1 From 34d76fefb2667d0ca138ff4fcf8bc8443032449f Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Wed, 9 Sep 2020 18:44:06 +0200 Subject: reset: add reset controller driver for SCMI agents This change introduces a reset controller driver for SCMI agent devices. When SCMI agent and SCMI reset domain drivers are enabled, SCMI agent binds a reset controller device for each SCMI reset domain protocol devices enabled in the FDT. SCMI reset driver is embedded upon CONFIG_RESET_SCMI=y. If enabled, CONFIG_SCMI_AGENT is also enabled. SCMI Reset Domain protocol is defined in the SCMI specification [1]. Links: [1] https://developer.arm.com/architectures/system-architectures/software-standards/scmi Signed-off-by: Etienne Carriere Cc: Simon Glass Cc: Peng Fan Cc: Sudeep Holla Reviewed-by: Simon Glass --- drivers/firmware/scmi/scmi_agent-uclass.c | 4 ++ drivers/reset/Kconfig | 8 +++ drivers/reset/Makefile | 1 + drivers/reset/reset-scmi.c | 81 +++++++++++++++++++++++++++++++ include/scmi_protocols.h | 60 +++++++++++++++++++++++ 5 files changed, 154 insertions(+) create mode 100644 drivers/reset/reset-scmi.c (limited to 'include') diff --git a/drivers/firmware/scmi/scmi_agent-uclass.c b/drivers/firmware/scmi/scmi_agent-uclass.c index 1f36f23b6d8..77160b19995 100644 --- a/drivers/firmware/scmi/scmi_agent-uclass.c +++ b/drivers/firmware/scmi/scmi_agent-uclass.c @@ -74,6 +74,10 @@ static int scmi_bind_protocols(struct udevice *dev) if (IS_ENABLED(CONFIG_CLK_SCMI)) drv = DM_GET_DRIVER(scmi_clock); break; + case SCMI_PROTOCOL_ID_RESET_DOMAIN: + if (IS_ENABLED(CONFIG_RESET_SCMI)) + drv = DM_GET_DRIVER(scmi_reset_domain); + break; default: break; } diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 3fdfe4a6cb0..b60e11f98b2 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -181,4 +181,12 @@ config RESET_RASPBERRYPI relevant. This driver provides a reset controller capable of interfacing with RPi4's co-processor and model these firmware initialization routines as reset lines. + +config RESET_SCMI + bool "Enable SCMI reset domain driver" + select SCMI_FIRMWARE + help + Enable this option if you want to support reset controller + devices exposed by a SCMI agent based on SCMI reset domain + protocol communication with a SCMI server. endmenu diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 5176da58853..10a7973f823 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -27,3 +27,4 @@ obj-$(CONFIG_RESET_IPQ419) += reset-ipq4019.o obj-$(CONFIG_RESET_SIFIVE) += reset-sifive.o obj-$(CONFIG_RESET_SYSCON) += reset-syscon.o obj-$(CONFIG_RESET_RASPBERRYPI) += reset-raspberrypi.o +obj-$(CONFIG_RESET_SCMI) += reset-scmi.o diff --git a/drivers/reset/reset-scmi.c b/drivers/reset/reset-scmi.c new file mode 100644 index 00000000000..1bff8075ee3 --- /dev/null +++ b/drivers/reset/reset-scmi.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019-2020 Linaro Limited + */ +#include +#include +#include +#include +#include +#include +#include + +static int scmi_reset_set_level(struct reset_ctl *rst, bool assert_not_deassert) +{ + struct scmi_rd_reset_in in = { + .domain_id = rst->id, + .flags = assert_not_deassert ? SCMI_RD_RESET_FLAG_ASSERT : 0, + .reset_state = 0, + }; + struct scmi_rd_reset_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_RESET_DOMAIN, + SCMI_RESET_DOMAIN_RESET, + in, out); + int ret; + + ret = devm_scmi_process_msg(rst->dev->parent, &msg); + if (ret) + return ret; + + return scmi_to_linux_errno(out.status); +} + +static int scmi_reset_assert(struct reset_ctl *rst) +{ + return scmi_reset_set_level(rst, true); +} + +static int scmi_reset_deassert(struct reset_ctl *rst) +{ + return scmi_reset_set_level(rst, false); +} + +static int scmi_reset_request(struct reset_ctl *rst) +{ + struct scmi_rd_attr_in in = { + .domain_id = rst->id, + }; + struct scmi_rd_attr_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_RESET_DOMAIN, + SCMI_RESET_DOMAIN_ATTRIBUTES, + in, out); + int ret; + + /* + * We don't really care about the attribute, just check + * the reset domain exists. + */ + ret = devm_scmi_process_msg(rst->dev->parent, &msg); + if (ret) + return ret; + + return scmi_to_linux_errno(out.status); +} + +static int scmi_reset_rfree(struct reset_ctl *rst) +{ + return 0; +} + +static const struct reset_ops scmi_reset_domain_ops = { + .request = scmi_reset_request, + .rfree = scmi_reset_rfree, + .rst_assert = scmi_reset_assert, + .rst_deassert = scmi_reset_deassert, +}; + +U_BOOT_DRIVER(scmi_reset_domain) = { + .name = "scmi_reset_domain", + .id = UCLASS_RESET, + .ops = &scmi_reset_domain_ops, +}; diff --git a/include/scmi_protocols.h b/include/scmi_protocols.h index 4778bcfc474..ccab97c96c9 100644 --- a/include/scmi_protocols.h +++ b/include/scmi_protocols.h @@ -116,4 +116,64 @@ struct scmi_clk_rate_set_out { s32 status; }; +/* + * SCMI Reset Domain Protocol + */ + +enum scmi_reset_domain_message_id { + SCMI_RESET_DOMAIN_ATTRIBUTES = 0x3, + SCMI_RESET_DOMAIN_RESET = 0x4, +}; + +#define SCMI_RD_NAME_LEN 16 + +#define SCMI_RD_ATTRIBUTES_FLAG_ASYNC BIT(31) +#define SCMI_RD_ATTRIBUTES_FLAG_NOTIF BIT(30) + +#define SCMI_RD_RESET_FLAG_ASYNC BIT(2) +#define SCMI_RD_RESET_FLAG_ASSERT BIT(1) +#define SCMI_RD_RESET_FLAG_CYCLE BIT(0) + +/** + * struct scmi_rd_attr_in - Payload for RESET_DOMAIN_ATTRIBUTES message + * @domain_id: SCMI reset domain ID + */ +struct scmi_rd_attr_in { + u32 domain_id; +}; + +/** + * struct scmi_rd_attr_out - Payload for RESET_DOMAIN_ATTRIBUTES response + * @status: SCMI command status + * @attributes: Retrieved attributes of the reset domain + * @latency: Reset cycle max lantency + * @name: Reset domain name + */ +struct scmi_rd_attr_out { + s32 status; + u32 attributes; + u32 latency; + char name[SCMI_RD_NAME_LEN]; +}; + +/** + * struct scmi_rd_reset_in - Message payload for RESET command + * @domain_id: SCMI reset domain ID + * @flags: Flags for the reset request + * @reset_state: Reset target state + */ +struct scmi_rd_reset_in { + u32 domain_id; + u32 flags; + u32 reset_state; +}; + +/** + * struct scmi_rd_reset_out - Response payload for RESET command + * @status: SCMI command status + */ +struct scmi_rd_reset_out { + s32 status; +}; + #endif /* _SCMI_PROTOCOLS_H */ -- cgit v1.3.1