diff options
-rw-r--r-- | drivers/base/firmware_class.c | 64 |
1 files changed, 64 insertions, 0 deletions
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index a47266ccfc60..65c60666685b 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c | |||
@@ -114,6 +114,11 @@ struct firmware_priv { | |||
114 | struct firmware *fw; | 114 | struct firmware *fw; |
115 | }; | 115 | }; |
116 | 116 | ||
117 | struct fw_name_devm { | ||
118 | unsigned long magic; | ||
119 | char name[]; | ||
120 | }; | ||
121 | |||
117 | #define to_fwbuf(d) container_of(d, struct firmware_buf, ref) | 122 | #define to_fwbuf(d) container_of(d, struct firmware_buf, ref) |
118 | 123 | ||
119 | /* fw_lock could be moved to 'struct firmware_priv' but since it is just | 124 | /* fw_lock could be moved to 'struct firmware_priv' but since it is just |
@@ -590,6 +595,55 @@ static void fw_set_page_data(struct firmware_buf *buf, struct firmware *fw) | |||
590 | (unsigned int)buf->size); | 595 | (unsigned int)buf->size); |
591 | } | 596 | } |
592 | 597 | ||
598 | static void fw_name_devm_release(struct device *dev, void *res) | ||
599 | { | ||
600 | struct fw_name_devm *fwn = res; | ||
601 | |||
602 | if (fwn->magic == (unsigned long)&fw_cache) | ||
603 | pr_debug("%s: fw_name-%s devm-%p released\n", | ||
604 | __func__, fwn->name, res); | ||
605 | } | ||
606 | |||
607 | static int fw_devm_match(struct device *dev, void *res, | ||
608 | void *match_data) | ||
609 | { | ||
610 | struct fw_name_devm *fwn = res; | ||
611 | |||
612 | return (fwn->magic == (unsigned long)&fw_cache) && | ||
613 | !strcmp(fwn->name, match_data); | ||
614 | } | ||
615 | |||
616 | static struct fw_name_devm *fw_find_devm_name(struct device *dev, | ||
617 | const char *name) | ||
618 | { | ||
619 | struct fw_name_devm *fwn; | ||
620 | |||
621 | fwn = devres_find(dev, fw_name_devm_release, | ||
622 | fw_devm_match, (void *)name); | ||
623 | return fwn; | ||
624 | } | ||
625 | |||
626 | /* add firmware name into devres list */ | ||
627 | static int fw_add_devm_name(struct device *dev, const char *name) | ||
628 | { | ||
629 | struct fw_name_devm *fwn; | ||
630 | |||
631 | fwn = fw_find_devm_name(dev, name); | ||
632 | if (fwn) | ||
633 | return 1; | ||
634 | |||
635 | fwn = devres_alloc(fw_name_devm_release, sizeof(struct fw_name_devm) + | ||
636 | strlen(name) + 1, GFP_KERNEL); | ||
637 | if (!fwn) | ||
638 | return -ENOMEM; | ||
639 | |||
640 | fwn->magic = (unsigned long)&fw_cache; | ||
641 | strcpy(fwn->name, name); | ||
642 | devres_add(dev, fwn); | ||
643 | |||
644 | return 0; | ||
645 | } | ||
646 | |||
593 | static void _request_firmware_cleanup(const struct firmware **firmware_p) | 647 | static void _request_firmware_cleanup(const struct firmware **firmware_p) |
594 | { | 648 | { |
595 | release_firmware(*firmware_p); | 649 | release_firmware(*firmware_p); |
@@ -709,6 +763,16 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, | |||
709 | if (!buf->size || test_bit(FW_STATUS_ABORT, &buf->status)) | 763 | if (!buf->size || test_bit(FW_STATUS_ABORT, &buf->status)) |
710 | retval = -ENOENT; | 764 | retval = -ENOENT; |
711 | 765 | ||
766 | /* | ||
767 | * add firmware name into devres list so that we can auto cache | ||
768 | * and uncache firmware for device. | ||
769 | * | ||
770 | * f_dev->parent may has been deleted already, but the problem | ||
771 | * should be fixed in devres or driver core. | ||
772 | */ | ||
773 | if (!retval && f_dev->parent) | ||
774 | fw_add_devm_name(f_dev->parent, buf->fw_id); | ||
775 | |||
712 | if (!retval) | 776 | if (!retval) |
713 | retval = fw_map_pages_buf(buf); | 777 | retval = fw_map_pages_buf(buf); |
714 | 778 | ||