diff options
Diffstat (limited to 'drivers/reset/core.c')
-rw-r--r-- | drivers/reset/core.c | 96 |
1 files changed, 95 insertions, 1 deletions
diff --git a/drivers/reset/core.c b/drivers/reset/core.c index da4292e9de97..6488292e129c 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c | |||
@@ -23,6 +23,9 @@ | |||
23 | static DEFINE_MUTEX(reset_list_mutex); | 23 | static DEFINE_MUTEX(reset_list_mutex); |
24 | static LIST_HEAD(reset_controller_list); | 24 | static LIST_HEAD(reset_controller_list); |
25 | 25 | ||
26 | static DEFINE_MUTEX(reset_lookup_mutex); | ||
27 | static LIST_HEAD(reset_lookup_list); | ||
28 | |||
26 | /** | 29 | /** |
27 | * struct reset_control - a reset control | 30 | * struct reset_control - a reset control |
28 | * @rcdev: a pointer to the reset controller device | 31 | * @rcdev: a pointer to the reset controller device |
@@ -148,6 +151,33 @@ int devm_reset_controller_register(struct device *dev, | |||
148 | } | 151 | } |
149 | EXPORT_SYMBOL_GPL(devm_reset_controller_register); | 152 | EXPORT_SYMBOL_GPL(devm_reset_controller_register); |
150 | 153 | ||
154 | /** | ||
155 | * reset_controller_add_lookup - register a set of lookup entries | ||
156 | * @lookup: array of reset lookup entries | ||
157 | * @num_entries: number of entries in the lookup array | ||
158 | */ | ||
159 | void reset_controller_add_lookup(struct reset_control_lookup *lookup, | ||
160 | unsigned int num_entries) | ||
161 | { | ||
162 | struct reset_control_lookup *entry; | ||
163 | unsigned int i; | ||
164 | |||
165 | mutex_lock(&reset_lookup_mutex); | ||
166 | for (i = 0; i < num_entries; i++) { | ||
167 | entry = &lookup[i]; | ||
168 | |||
169 | if (!entry->dev_id || !entry->provider) { | ||
170 | pr_warn("%s(): reset lookup entry badly specified, skipping\n", | ||
171 | __func__); | ||
172 | continue; | ||
173 | } | ||
174 | |||
175 | list_add_tail(&entry->list, &reset_lookup_list); | ||
176 | } | ||
177 | mutex_unlock(&reset_lookup_mutex); | ||
178 | } | ||
179 | EXPORT_SYMBOL_GPL(reset_controller_add_lookup); | ||
180 | |||
151 | static inline struct reset_control_array * | 181 | static inline struct reset_control_array * |
152 | rstc_to_array(struct reset_control *rstc) { | 182 | rstc_to_array(struct reset_control *rstc) { |
153 | return container_of(rstc, struct reset_control_array, base); | 183 | return container_of(rstc, struct reset_control_array, base); |
@@ -493,6 +523,70 @@ struct reset_control *__of_reset_control_get(struct device_node *node, | |||
493 | } | 523 | } |
494 | EXPORT_SYMBOL_GPL(__of_reset_control_get); | 524 | EXPORT_SYMBOL_GPL(__of_reset_control_get); |
495 | 525 | ||
526 | static struct reset_controller_dev * | ||
527 | __reset_controller_by_name(const char *name) | ||
528 | { | ||
529 | struct reset_controller_dev *rcdev; | ||
530 | |||
531 | lockdep_assert_held(&reset_list_mutex); | ||
532 | |||
533 | list_for_each_entry(rcdev, &reset_controller_list, list) { | ||
534 | if (!rcdev->dev) | ||
535 | continue; | ||
536 | |||
537 | if (!strcmp(name, dev_name(rcdev->dev))) | ||
538 | return rcdev; | ||
539 | } | ||
540 | |||
541 | return NULL; | ||
542 | } | ||
543 | |||
544 | static struct reset_control * | ||
545 | __reset_control_get_from_lookup(struct device *dev, const char *con_id, | ||
546 | bool shared, bool optional) | ||
547 | { | ||
548 | const struct reset_control_lookup *lookup; | ||
549 | struct reset_controller_dev *rcdev; | ||
550 | const char *dev_id = dev_name(dev); | ||
551 | struct reset_control *rstc = NULL; | ||
552 | |||
553 | if (!dev) | ||
554 | return ERR_PTR(-EINVAL); | ||
555 | |||
556 | mutex_lock(&reset_lookup_mutex); | ||
557 | |||
558 | list_for_each_entry(lookup, &reset_lookup_list, list) { | ||
559 | if (strcmp(lookup->dev_id, dev_id)) | ||
560 | continue; | ||
561 | |||
562 | if ((!con_id && !lookup->con_id) || | ||
563 | ((con_id && lookup->con_id) && | ||
564 | !strcmp(con_id, lookup->con_id))) { | ||
565 | mutex_lock(&reset_list_mutex); | ||
566 | rcdev = __reset_controller_by_name(lookup->provider); | ||
567 | if (!rcdev) { | ||
568 | mutex_unlock(&reset_list_mutex); | ||
569 | mutex_unlock(&reset_lookup_mutex); | ||
570 | /* Reset provider may not be ready yet. */ | ||
571 | return ERR_PTR(-EPROBE_DEFER); | ||
572 | } | ||
573 | |||
574 | rstc = __reset_control_get_internal(rcdev, | ||
575 | lookup->index, | ||
576 | shared); | ||
577 | mutex_unlock(&reset_list_mutex); | ||
578 | break; | ||
579 | } | ||
580 | } | ||
581 | |||
582 | mutex_unlock(&reset_lookup_mutex); | ||
583 | |||
584 | if (!rstc) | ||
585 | return optional ? NULL : ERR_PTR(-ENOENT); | ||
586 | |||
587 | return rstc; | ||
588 | } | ||
589 | |||
496 | struct reset_control *__reset_control_get(struct device *dev, const char *id, | 590 | struct reset_control *__reset_control_get(struct device *dev, const char *id, |
497 | int index, bool shared, bool optional) | 591 | int index, bool shared, bool optional) |
498 | { | 592 | { |
@@ -500,7 +594,7 @@ struct reset_control *__reset_control_get(struct device *dev, const char *id, | |||
500 | return __of_reset_control_get(dev->of_node, id, index, shared, | 594 | return __of_reset_control_get(dev->of_node, id, index, shared, |
501 | optional); | 595 | optional); |
502 | 596 | ||
503 | return optional ? NULL : ERR_PTR(-EINVAL); | 597 | return __reset_control_get_from_lookup(dev, id, shared, optional); |
504 | } | 598 | } |
505 | EXPORT_SYMBOL_GPL(__reset_control_get); | 599 | EXPORT_SYMBOL_GPL(__reset_control_get); |
506 | 600 | ||