The reset IDs exposed to consumers of the StarFive JHB100 reset controllers do not map one-to-one to the hardware reset bits.
Add struct jh71x0_reset_info, which describes the number of resets, the assert/status register offsets and, for discontiguous controllers, a table mapping reset IDs to hardware bits. Add reset_starfive_jh71x0_register_with_info() to register such a controller, and translate the reset ID in the update and status callbacks when a map is provided. Existing users of reset_starfive_jh71x0_register() are unaffected. Signed-off-by: Changhuang Liang <[email protected]> --- .../reset/starfive/reset-starfive-jh71x0.c | 99 ++++++++++++++++--- .../reset/starfive/reset-starfive-jh71x0.h | 19 ++++ 2 files changed, 107 insertions(+), 11 deletions(-) diff --git a/drivers/reset/starfive/reset-starfive-jh71x0.c b/drivers/reset/starfive/reset-starfive-jh71x0.c index 29ce3486752f..fd1c755a0017 100644 --- a/drivers/reset/starfive/reset-starfive-jh71x0.c +++ b/drivers/reset/starfive/reset-starfive-jh71x0.c @@ -14,6 +14,8 @@ #include "reset-starfive-jh71x0.h" +#define JH71X0_RESET_ID_INVALID ULONG_MAX + struct jh71x0_reset { struct reset_controller_dev rcdev; /* protect registers against concurrent read-modify-write */ @@ -21,6 +23,11 @@ struct jh71x0_reset { void __iomem *assert; void __iomem *status; const u32 *asserted; + + /* Only exists in reset controllers that use the + * reset_starfive_jh71x0_register_with_info helper. + */ + const struct jh71x0_reset_info *info; }; static inline struct jh71x0_reset * @@ -29,19 +36,40 @@ jh71x0_reset_from(struct reset_controller_dev *rcdev) return container_of(rcdev, struct jh71x0_reset, rcdev); } +static unsigned long +jh71x0_reset_id_to_hw_id(const struct jh71x0_reset_map *map, unsigned int nr_resets, + unsigned long reset_id) +{ + for (u32 i = 0; i < nr_resets; i++) { + if (map[i].reset_id == reset_id) + return map[i].hw_id; + } + + return JH71X0_RESET_ID_INVALID; +} + static int jh71x0_reset_update(struct reset_controller_dev *rcdev, unsigned long id, bool assert) { struct jh71x0_reset *data = jh71x0_reset_from(rcdev); - unsigned long offset = id / 32; - u32 mask = BIT(id % 32); - void __iomem *reg_assert = data->assert + offset * sizeof(u32); - void __iomem *reg_status = data->status + offset * sizeof(u32); - u32 done = data->asserted ? data->asserted[offset] & mask : 0; - u32 value; - unsigned long flags; + unsigned long offset, flags; + void __iomem *reg_assert; + void __iomem *reg_status; + u32 mask, done, value; int ret; + if (data->info && data->info->discontiguous) { + id = jh71x0_reset_id_to_hw_id(data->info->map, data->info->nr_resets, id); + if (id == JH71X0_RESET_ID_INVALID) + return -EINVAL; + } + + offset = id / 32; + mask = BIT(id % 32); + reg_assert = data->assert + offset * sizeof(u32); + reg_status = data->status + offset * sizeof(u32); + done = data->asserted ? data->asserted[offset] & mask : 0; + if (!assert) done ^= mask; @@ -89,10 +117,20 @@ static int jh71x0_reset_status(struct reset_controller_dev *rcdev, unsigned long id) { struct jh71x0_reset *data = jh71x0_reset_from(rcdev); - unsigned long offset = id / 32; - u32 mask = BIT(id % 32); - void __iomem *reg_status = data->status + offset * sizeof(u32); - u32 value = readl(reg_status); + void __iomem *reg_status; + unsigned long offset; + u32 mask, value; + + if (data->info && data->info->discontiguous) { + id = jh71x0_reset_id_to_hw_id(data->info->map, data->info->nr_resets, id); + if (id == JH71X0_RESET_ID_INVALID) + return -EINVAL; + } + + offset = id / 32; + mask = BIT(id % 32); + reg_status = data->status + offset * sizeof(u32); + value = readl(reg_status); if (!data->asserted) return !(value & mask); @@ -132,3 +170,42 @@ int reset_starfive_jh71x0_register(struct device *dev, struct device_node *of_no return devm_reset_controller_register(dev, &data->rcdev); } EXPORT_SYMBOL_GPL(reset_starfive_jh71x0_register); + +int reset_starfive_jh71x0_register_with_info(struct device *dev, struct device_node *of_node, + void __iomem *assert, void __iomem *status, + const u32 *asserted, + const struct jh71x0_reset_info *info, + struct module *owner) +{ + struct jh71x0_reset *data; + int ret; + + if (info->discontiguous && (!info->map || !info->nr_resets)) + return dev_err_probe(dev, -EINVAL, + "discontiguous controller without a valid ID map\n"); + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->rcdev.ops = &jh71x0_reset_ops; + data->rcdev.owner = owner; + data->rcdev.nr_resets = info->nr_resets; + data->rcdev.dev = dev; + data->rcdev.of_node = of_node; + + spin_lock_init(&data->lock); + data->assert = assert; + data->status = status; + data->asserted = asserted; + data->info = info; + + ret = devm_reset_controller_register(dev, &data->rcdev); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to register reset controller\n"); + + dev_info(dev, "Registered %u resets\n", data->rcdev.nr_resets); + + return 0; +} +EXPORT_SYMBOL_GPL(reset_starfive_jh71x0_register_with_info); diff --git a/drivers/reset/starfive/reset-starfive-jh71x0.h b/drivers/reset/starfive/reset-starfive-jh71x0.h index db7d39a87f87..36f198139602 100644 --- a/drivers/reset/starfive/reset-starfive-jh71x0.h +++ b/drivers/reset/starfive/reset-starfive-jh71x0.h @@ -6,9 +6,28 @@ #ifndef __RESET_STARFIVE_JH71X0_H #define __RESET_STARFIVE_JH71X0_H +struct jh71x0_reset_map { + unsigned long reset_id; + unsigned long hw_id; +}; + +struct jh71x0_reset_info { + unsigned int nr_resets; + unsigned int assert_offset; + unsigned int status_offset; + bool discontiguous; + const struct jh71x0_reset_map *map; +}; + int reset_starfive_jh71x0_register(struct device *dev, struct device_node *of_node, void __iomem *assert, void __iomem *status, const u32 *asserted, unsigned int nr_resets, struct module *owner); +int reset_starfive_jh71x0_register_with_info(struct device *dev, struct device_node *of_node, + void __iomem *assert, void __iomem *status, + const u32 *asserted, + const struct jh71x0_reset_info *info, + struct module *owner); + #endif /* __RESET_STARFIVE_JH71X0_H */ -- 2.25.1

