diff options
| -rw-r--r-- | Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt | 52 | ||||
| -rw-r--r-- | Documentation/remoteproc.txt | 6 | ||||
| -rw-r--r-- | drivers/remoteproc/Kconfig | 13 | ||||
| -rw-r--r-- | drivers/remoteproc/Makefile | 1 | ||||
| -rw-r--r-- | drivers/remoteproc/da8xx_remoteproc.c | 3 | ||||
| -rw-r--r-- | drivers/remoteproc/remoteproc_core.c | 115 | ||||
| -rw-r--r-- | drivers/remoteproc/remoteproc_internal.h | 2 | ||||
| -rw-r--r-- | drivers/remoteproc/ste_modem_rproc.c | 4 | ||||
| -rw-r--r-- | drivers/remoteproc/wkup_m3_rproc.c | 257 | ||||
| -rw-r--r-- | include/linux/platform_data/wkup_m3.h | 30 | ||||
| -rw-r--r-- | include/linux/remoteproc.h | 9 |
11 files changed, 460 insertions, 32 deletions
diff --git a/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt b/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt new file mode 100644 index 000000000000..3a70073797eb --- /dev/null +++ b/Documentation/devicetree/bindings/remoteproc/wkup_m3_rproc.txt | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | TI Wakeup M3 Remoteproc Driver | ||
| 2 | ============================== | ||
| 3 | |||
| 4 | The TI AM33xx and AM43xx family of devices use a small Cortex M3 co-processor | ||
| 5 | (commonly referred to as Wakeup M3 or CM3) to help with various low power tasks | ||
| 6 | that cannot be controlled from the MPU. This CM3 processor requires a firmware | ||
| 7 | binary to accomplish this. The wkup_m3 remoteproc driver handles the loading of | ||
| 8 | the firmware and booting of the CM3. | ||
| 9 | |||
| 10 | Wkup M3 Device Node: | ||
| 11 | ==================== | ||
| 12 | A wkup_m3 device node is used to represent the Wakeup M3 processor instance | ||
| 13 | within the SoC. It is added as a child node of the parent interconnect bus | ||
| 14 | (l4_wkup) through which it is accessible to the MPU. | ||
| 15 | |||
| 16 | Required properties: | ||
| 17 | -------------------- | ||
| 18 | - compatible: Should be one of, | ||
| 19 | "ti,am3352-wkup-m3" for AM33xx SoCs | ||
| 20 | "ti,am4372-wkup-m3" for AM43xx SoCs | ||
| 21 | - reg: Should contain the address ranges for the two internal | ||
| 22 | memory regions, UMEM and DMEM. The parent node should | ||
| 23 | provide an appropriate ranges property for properly | ||
| 24 | translating these into bus addresses. | ||
| 25 | - reg-names: Contains the corresponding names for the two memory | ||
| 26 | regions. These should be named "umem" & "dmem". | ||
| 27 | - ti,hwmods: Name of the hwmod associated with the wkupm3 device. | ||
| 28 | - ti,pm-firmware: Name of firmware file to be used for loading and | ||
| 29 | booting the wkup_m3 remote processor. | ||
| 30 | |||
| 31 | Example: | ||
| 32 | -------- | ||
| 33 | /* AM33xx */ | ||
| 34 | ocp { | ||
| 35 | l4_wkup: l4_wkup@44c00000 { | ||
| 36 | compatible = "am335-l4-wkup", "simple-bus"; | ||
| 37 | ranges = <0 0x44c00000 0x400000>; | ||
| 38 | #address-cells = <1>; | ||
| 39 | #size-cells = <1>; | ||
| 40 | |||
| 41 | wkup_m3: wkup_m3@100000 { | ||
| 42 | compatible = "ti,am3352-wkup-m3"; | ||
| 43 | reg = <0x100000 0x4000>, | ||
| 44 | <0x180000 0x2000>; | ||
| 45 | reg-names = "umem", "dmem"; | ||
| 46 | ti,hwmods = "wkup_m3"; | ||
| 47 | ti,pm-firmware = "am335x-pm-firmware.elf"; | ||
| 48 | }; | ||
| 49 | }; | ||
| 50 | |||
| 51 | ... | ||
| 52 | }; | ||
diff --git a/Documentation/remoteproc.txt b/Documentation/remoteproc.txt index e6469fdcf89a..ef0219fa4bb4 100644 --- a/Documentation/remoteproc.txt +++ b/Documentation/remoteproc.txt | |||
| @@ -51,6 +51,12 @@ cost. | |||
| 51 | rproc_shutdown() returns, and users can still use it with a subsequent | 51 | rproc_shutdown() returns, and users can still use it with a subsequent |
| 52 | rproc_boot(), if needed. | 52 | rproc_boot(), if needed. |
| 53 | 53 | ||
| 54 | struct rproc *rproc_get_by_phandle(phandle phandle) | ||
| 55 | - Find an rproc handle using a device tree phandle. Returns the rproc | ||
| 56 | handle on success, and NULL on failure. This function increments | ||
| 57 | the remote processor's refcount, so always use rproc_put() to | ||
| 58 | decrement it back once rproc isn't needed anymore. | ||
| 59 | |||
| 54 | 3. Typical usage | 60 | 3. Typical usage |
| 55 | 61 | ||
| 56 | #include <linux/remoteproc.h> | 62 | #include <linux/remoteproc.h> |
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 5e343bab9458..28c711f0ac6b 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig | |||
| @@ -41,6 +41,19 @@ config STE_MODEM_RPROC | |||
| 41 | This can be either built-in or a loadable module. | 41 | This can be either built-in or a loadable module. |
| 42 | If unsure say N. | 42 | If unsure say N. |
| 43 | 43 | ||
| 44 | config WKUP_M3_RPROC | ||
| 45 | tristate "AMx3xx Wakeup M3 remoteproc support" | ||
| 46 | depends on SOC_AM33XX || SOC_AM43XX | ||
| 47 | select REMOTEPROC | ||
| 48 | help | ||
| 49 | Say y here to support Wakeup M3 remote processor on TI AM33xx | ||
| 50 | and AM43xx family of SoCs. | ||
| 51 | |||
| 52 | Required for Suspend-to-RAM on AM33xx and AM43xx SoCs. Also needed | ||
| 53 | for deep CPUIdle states on AM33xx SoCs. Allows for loading of the | ||
| 54 | firmware onto these remote processors. | ||
| 55 | If unsure say N. | ||
| 56 | |||
| 44 | config DA8XX_REMOTEPROC | 57 | config DA8XX_REMOTEPROC |
| 45 | tristate "DA8xx/OMAP-L13x remoteproc support" | 58 | tristate "DA8xx/OMAP-L13x remoteproc support" |
| 46 | depends on ARCH_DAVINCI_DA8XX | 59 | depends on ARCH_DAVINCI_DA8XX |
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index ac2ff75686d2..81b04d1e2e58 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile | |||
| @@ -9,4 +9,5 @@ remoteproc-y += remoteproc_virtio.o | |||
| 9 | remoteproc-y += remoteproc_elf_loader.o | 9 | remoteproc-y += remoteproc_elf_loader.o |
| 10 | obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o | 10 | obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o |
| 11 | obj-$(CONFIG_STE_MODEM_RPROC) += ste_modem_rproc.o | 11 | obj-$(CONFIG_STE_MODEM_RPROC) += ste_modem_rproc.o |
| 12 | obj-$(CONFIG_WKUP_M3_RPROC) += wkup_m3_rproc.o | ||
| 12 | obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o | 13 | obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o |
diff --git a/drivers/remoteproc/da8xx_remoteproc.c b/drivers/remoteproc/da8xx_remoteproc.c index f8d6a0661c14..009e56f67de2 100644 --- a/drivers/remoteproc/da8xx_remoteproc.c +++ b/drivers/remoteproc/da8xx_remoteproc.c | |||
| @@ -26,8 +26,7 @@ | |||
| 26 | static char *da8xx_fw_name; | 26 | static char *da8xx_fw_name; |
| 27 | module_param(da8xx_fw_name, charp, S_IRUGO); | 27 | module_param(da8xx_fw_name, charp, S_IRUGO); |
| 28 | MODULE_PARM_DESC(da8xx_fw_name, | 28 | MODULE_PARM_DESC(da8xx_fw_name, |
| 29 | "\n\t\tName of DSP firmware file in /lib/firmware" | 29 | "Name of DSP firmware file in /lib/firmware (if not specified defaults to 'rproc-dsp-fw')"); |
| 30 | " (if not specified defaults to 'rproc-dsp-fw')"); | ||
| 31 | 30 | ||
| 32 | /* | 31 | /* |
| 33 | * OMAP-L138 Technical References: | 32 | * OMAP-L138 Technical References: |
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 11cdb119e4f3..8b3130f22b42 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c | |||
| @@ -44,6 +44,9 @@ | |||
| 44 | 44 | ||
| 45 | #include "remoteproc_internal.h" | 45 | #include "remoteproc_internal.h" |
| 46 | 46 | ||
| 47 | static DEFINE_MUTEX(rproc_list_mutex); | ||
| 48 | static LIST_HEAD(rproc_list); | ||
| 49 | |||
| 47 | typedef int (*rproc_handle_resources_t)(struct rproc *rproc, | 50 | typedef int (*rproc_handle_resources_t)(struct rproc *rproc, |
| 48 | struct resource_table *table, int len); | 51 | struct resource_table *table, int len); |
| 49 | typedef int (*rproc_handle_resource_t)(struct rproc *rproc, | 52 | typedef int (*rproc_handle_resource_t)(struct rproc *rproc, |
| @@ -132,32 +135,48 @@ static void rproc_disable_iommu(struct rproc *rproc) | |||
| 132 | 135 | ||
| 133 | iommu_detach_device(domain, dev); | 136 | iommu_detach_device(domain, dev); |
| 134 | iommu_domain_free(domain); | 137 | iommu_domain_free(domain); |
| 135 | |||
| 136 | return; | ||
| 137 | } | 138 | } |
| 138 | 139 | ||
| 139 | /* | 140 | /** |
| 141 | * rproc_da_to_va() - lookup the kernel virtual address for a remoteproc address | ||
| 142 | * @rproc: handle of a remote processor | ||
| 143 | * @da: remoteproc device address to translate | ||
| 144 | * @len: length of the memory region @da is pointing to | ||
| 145 | * | ||
| 140 | * Some remote processors will ask us to allocate them physically contiguous | 146 | * Some remote processors will ask us to allocate them physically contiguous |
| 141 | * memory regions (which we call "carveouts"), and map them to specific | 147 | * memory regions (which we call "carveouts"), and map them to specific |
| 142 | * device addresses (which are hardcoded in the firmware). | 148 | * device addresses (which are hardcoded in the firmware). They may also have |
| 149 | * dedicated memory regions internal to the processors, and use them either | ||
| 150 | * exclusively or alongside carveouts. | ||
| 143 | * | 151 | * |
| 144 | * They may then ask us to copy objects into specific device addresses (e.g. | 152 | * They may then ask us to copy objects into specific device addresses (e.g. |
| 145 | * code/data sections) or expose us certain symbols in other device address | 153 | * code/data sections) or expose us certain symbols in other device address |
| 146 | * (e.g. their trace buffer). | 154 | * (e.g. their trace buffer). |
| 147 | * | 155 | * |
| 148 | * This function is an internal helper with which we can go over the allocated | 156 | * This function is a helper function with which we can go over the allocated |
| 149 | * carveouts and translate specific device address to kernel virtual addresses | 157 | * carveouts and translate specific device addresses to kernel virtual addresses |
| 150 | * so we can access the referenced memory. | 158 | * so we can access the referenced memory. This function also allows to perform |
| 159 | * translations on the internal remoteproc memory regions through a platform | ||
| 160 | * implementation specific da_to_va ops, if present. | ||
| 161 | * | ||
| 162 | * The function returns a valid kernel address on success or NULL on failure. | ||
| 151 | * | 163 | * |
| 152 | * Note: phys_to_virt(iommu_iova_to_phys(rproc->domain, da)) will work too, | 164 | * Note: phys_to_virt(iommu_iova_to_phys(rproc->domain, da)) will work too, |
| 153 | * but only on kernel direct mapped RAM memory. Instead, we're just using | 165 | * but only on kernel direct mapped RAM memory. Instead, we're just using |
| 154 | * here the output of the DMA API, which should be more correct. | 166 | * here the output of the DMA API for the carveouts, which should be more |
| 167 | * correct. | ||
| 155 | */ | 168 | */ |
| 156 | void *rproc_da_to_va(struct rproc *rproc, u64 da, int len) | 169 | void *rproc_da_to_va(struct rproc *rproc, u64 da, int len) |
| 157 | { | 170 | { |
| 158 | struct rproc_mem_entry *carveout; | 171 | struct rproc_mem_entry *carveout; |
| 159 | void *ptr = NULL; | 172 | void *ptr = NULL; |
| 160 | 173 | ||
| 174 | if (rproc->ops->da_to_va) { | ||
| 175 | ptr = rproc->ops->da_to_va(rproc, da, len); | ||
| 176 | if (ptr) | ||
| 177 | goto out; | ||
| 178 | } | ||
| 179 | |||
| 161 | list_for_each_entry(carveout, &rproc->carveouts, node) { | 180 | list_for_each_entry(carveout, &rproc->carveouts, node) { |
| 162 | int offset = da - carveout->da; | 181 | int offset = da - carveout->da; |
| 163 | 182 | ||
| @@ -174,6 +193,7 @@ void *rproc_da_to_va(struct rproc *rproc, u64 da, int len) | |||
| 174 | break; | 193 | break; |
| 175 | } | 194 | } |
| 176 | 195 | ||
| 196 | out: | ||
| 177 | return ptr; | 197 | return ptr; |
| 178 | } | 198 | } |
| 179 | EXPORT_SYMBOL(rproc_da_to_va); | 199 | EXPORT_SYMBOL(rproc_da_to_va); |
| @@ -411,10 +431,8 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc, | |||
| 411 | } | 431 | } |
| 412 | 432 | ||
| 413 | trace = kzalloc(sizeof(*trace), GFP_KERNEL); | 433 | trace = kzalloc(sizeof(*trace), GFP_KERNEL); |
| 414 | if (!trace) { | 434 | if (!trace) |
| 415 | dev_err(dev, "kzalloc trace failed\n"); | ||
| 416 | return -ENOMEM; | 435 | return -ENOMEM; |
| 417 | } | ||
| 418 | 436 | ||
| 419 | /* set the trace buffer dma properties */ | 437 | /* set the trace buffer dma properties */ |
| 420 | trace->len = rsc->len; | 438 | trace->len = rsc->len; |
| @@ -489,10 +507,8 @@ static int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc, | |||
| 489 | } | 507 | } |
| 490 | 508 | ||
| 491 | mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); | 509 | mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); |
| 492 | if (!mapping) { | 510 | if (!mapping) |
| 493 | dev_err(dev, "kzalloc mapping failed\n"); | ||
| 494 | return -ENOMEM; | 511 | return -ENOMEM; |
| 495 | } | ||
| 496 | 512 | ||
| 497 | ret = iommu_map(rproc->domain, rsc->da, rsc->pa, rsc->len, rsc->flags); | 513 | ret = iommu_map(rproc->domain, rsc->da, rsc->pa, rsc->len, rsc->flags); |
| 498 | if (ret) { | 514 | if (ret) { |
| @@ -565,10 +581,8 @@ static int rproc_handle_carveout(struct rproc *rproc, | |||
| 565 | rsc->da, rsc->pa, rsc->len, rsc->flags); | 581 | rsc->da, rsc->pa, rsc->len, rsc->flags); |
| 566 | 582 | ||
| 567 | carveout = kzalloc(sizeof(*carveout), GFP_KERNEL); | 583 | carveout = kzalloc(sizeof(*carveout), GFP_KERNEL); |
| 568 | if (!carveout) { | 584 | if (!carveout) |
| 569 | dev_err(dev, "kzalloc carveout failed\n"); | ||
| 570 | return -ENOMEM; | 585 | return -ENOMEM; |
| 571 | } | ||
| 572 | 586 | ||
| 573 | va = dma_alloc_coherent(dev->parent, rsc->len, &dma, GFP_KERNEL); | 587 | va = dma_alloc_coherent(dev->parent, rsc->len, &dma, GFP_KERNEL); |
| 574 | if (!va) { | 588 | if (!va) { |
| @@ -768,7 +782,8 @@ static void rproc_resource_cleanup(struct rproc *rproc) | |||
| 768 | 782 | ||
| 769 | /* clean up carveout allocations */ | 783 | /* clean up carveout allocations */ |
| 770 | list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) { | 784 | list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) { |
| 771 | dma_free_coherent(dev->parent, entry->len, entry->va, entry->dma); | 785 | dma_free_coherent(dev->parent, entry->len, entry->va, |
| 786 | entry->dma); | ||
| 772 | list_del(&entry->node); | 787 | list_del(&entry->node); |
| 773 | kfree(entry); | 788 | kfree(entry); |
| 774 | } | 789 | } |
| @@ -808,9 +823,8 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw) | |||
| 808 | 823 | ||
| 809 | /* look for the resource table */ | 824 | /* look for the resource table */ |
| 810 | table = rproc_find_rsc_table(rproc, fw, &tablesz); | 825 | table = rproc_find_rsc_table(rproc, fw, &tablesz); |
| 811 | if (!table) { | 826 | if (!table) |
| 812 | goto clean_up; | 827 | goto clean_up; |
| 813 | } | ||
| 814 | 828 | ||
| 815 | /* Verify that resource table in loaded fw is unchanged */ | 829 | /* Verify that resource table in loaded fw is unchanged */ |
| 816 | if (rproc->table_csum != crc32(0, table, tablesz)) { | 830 | if (rproc->table_csum != crc32(0, table, tablesz)) { |
| @@ -911,7 +925,8 @@ static void rproc_fw_config_virtio(const struct firmware *fw, void *context) | |||
| 911 | 925 | ||
| 912 | /* count the number of notify-ids */ | 926 | /* count the number of notify-ids */ |
| 913 | rproc->max_notifyid = -1; | 927 | rproc->max_notifyid = -1; |
| 914 | ret = rproc_handle_resources(rproc, tablesz, rproc_count_vrings_handler); | 928 | ret = rproc_handle_resources(rproc, tablesz, |
| 929 | rproc_count_vrings_handler); | ||
| 915 | if (ret) | 930 | if (ret) |
| 916 | goto out; | 931 | goto out; |
| 917 | 932 | ||
| @@ -1152,6 +1167,50 @@ out: | |||
| 1152 | EXPORT_SYMBOL(rproc_shutdown); | 1167 | EXPORT_SYMBOL(rproc_shutdown); |
| 1153 | 1168 | ||
| 1154 | /** | 1169 | /** |
| 1170 | * rproc_get_by_phandle() - find a remote processor by phandle | ||
| 1171 | * @phandle: phandle to the rproc | ||
| 1172 | * | ||
| 1173 | * Finds an rproc handle using the remote processor's phandle, and then | ||
| 1174 | * return a handle to the rproc. | ||
| 1175 | * | ||
| 1176 | * This function increments the remote processor's refcount, so always | ||
| 1177 | * use rproc_put() to decrement it back once rproc isn't needed anymore. | ||
| 1178 | * | ||
| 1179 | * Returns the rproc handle on success, and NULL on failure. | ||
| 1180 | */ | ||
| 1181 | #ifdef CONFIG_OF | ||
| 1182 | struct rproc *rproc_get_by_phandle(phandle phandle) | ||
| 1183 | { | ||
| 1184 | struct rproc *rproc = NULL, *r; | ||
| 1185 | struct device_node *np; | ||
| 1186 | |||
| 1187 | np = of_find_node_by_phandle(phandle); | ||
| 1188 | if (!np) | ||
| 1189 | return NULL; | ||
| 1190 | |||
| 1191 | mutex_lock(&rproc_list_mutex); | ||
| 1192 | list_for_each_entry(r, &rproc_list, node) { | ||
| 1193 | if (r->dev.parent && r->dev.parent->of_node == np) { | ||
| 1194 | rproc = r; | ||
| 1195 | get_device(&rproc->dev); | ||
| 1196 | break; | ||
| 1197 | } | ||
| 1198 | } | ||
| 1199 | mutex_unlock(&rproc_list_mutex); | ||
| 1200 | |||
| 1201 | of_node_put(np); | ||
| 1202 | |||
| 1203 | return rproc; | ||
| 1204 | } | ||
| 1205 | #else | ||
| 1206 | struct rproc *rproc_get_by_phandle(phandle phandle) | ||
| 1207 | { | ||
| 1208 | return NULL; | ||
| 1209 | } | ||
| 1210 | #endif | ||
| 1211 | EXPORT_SYMBOL(rproc_get_by_phandle); | ||
| 1212 | |||
| 1213 | /** | ||
| 1155 | * rproc_add() - register a remote processor | 1214 | * rproc_add() - register a remote processor |
| 1156 | * @rproc: the remote processor handle to register | 1215 | * @rproc: the remote processor handle to register |
| 1157 | * | 1216 | * |
| @@ -1180,6 +1239,11 @@ int rproc_add(struct rproc *rproc) | |||
| 1180 | if (ret < 0) | 1239 | if (ret < 0) |
| 1181 | return ret; | 1240 | return ret; |
| 1182 | 1241 | ||
| 1242 | /* expose to rproc_get_by_phandle users */ | ||
| 1243 | mutex_lock(&rproc_list_mutex); | ||
| 1244 | list_add(&rproc->node, &rproc_list); | ||
| 1245 | mutex_unlock(&rproc_list_mutex); | ||
| 1246 | |||
| 1183 | dev_info(dev, "%s is available\n", rproc->name); | 1247 | dev_info(dev, "%s is available\n", rproc->name); |
| 1184 | 1248 | ||
| 1185 | dev_info(dev, "Note: remoteproc is still under development and considered experimental.\n"); | 1249 | dev_info(dev, "Note: remoteproc is still under development and considered experimental.\n"); |
| @@ -1268,10 +1332,8 @@ struct rproc *rproc_alloc(struct device *dev, const char *name, | |||
| 1268 | name_len = strlen(name) + strlen(template) - 2 + 1; | 1332 | name_len = strlen(name) + strlen(template) - 2 + 1; |
| 1269 | 1333 | ||
| 1270 | rproc = kzalloc(sizeof(struct rproc) + len + name_len, GFP_KERNEL); | 1334 | rproc = kzalloc(sizeof(struct rproc) + len + name_len, GFP_KERNEL); |
| 1271 | if (!rproc) { | 1335 | if (!rproc) |
| 1272 | dev_err(dev, "%s: kzalloc failed\n", __func__); | ||
| 1273 | return NULL; | 1336 | return NULL; |
| 1274 | } | ||
| 1275 | 1337 | ||
| 1276 | if (!firmware) { | 1338 | if (!firmware) { |
| 1277 | p = (char *)rproc + sizeof(struct rproc) + len; | 1339 | p = (char *)rproc + sizeof(struct rproc) + len; |
| @@ -1369,6 +1431,11 @@ int rproc_del(struct rproc *rproc) | |||
| 1369 | /* Free the copy of the resource table */ | 1431 | /* Free the copy of the resource table */ |
| 1370 | kfree(rproc->cached_table); | 1432 | kfree(rproc->cached_table); |
| 1371 | 1433 | ||
| 1434 | /* the rproc is downref'ed as soon as it's removed from the klist */ | ||
| 1435 | mutex_lock(&rproc_list_mutex); | ||
| 1436 | list_del(&rproc->node); | ||
| 1437 | mutex_unlock(&rproc_list_mutex); | ||
| 1438 | |||
| 1372 | device_del(&rproc->dev); | 1439 | device_del(&rproc->dev); |
| 1373 | 1440 | ||
| 1374 | return 0; | 1441 | return 0; |
diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h index 70701a50ddfa..8041b95cb058 100644 --- a/drivers/remoteproc/remoteproc_internal.h +++ b/drivers/remoteproc/remoteproc_internal.h | |||
| @@ -35,7 +35,7 @@ struct rproc; | |||
| 35 | * @get_boot_addr: get boot address to entry point specified in firmware | 35 | * @get_boot_addr: get boot address to entry point specified in firmware |
| 36 | */ | 36 | */ |
| 37 | struct rproc_fw_ops { | 37 | struct rproc_fw_ops { |
| 38 | struct resource_table *(*find_rsc_table) (struct rproc *rproc, | 38 | struct resource_table *(*find_rsc_table)(struct rproc *rproc, |
| 39 | const struct firmware *fw, | 39 | const struct firmware *fw, |
| 40 | int *tablesz); | 40 | int *tablesz); |
| 41 | struct resource_table *(*find_loaded_rsc_table)(struct rproc *rproc, | 41 | struct resource_table *(*find_loaded_rsc_table)(struct rproc *rproc, |
diff --git a/drivers/remoteproc/ste_modem_rproc.c b/drivers/remoteproc/ste_modem_rproc.c index dd193f35a1ff..53dc17bdd54e 100644 --- a/drivers/remoteproc/ste_modem_rproc.c +++ b/drivers/remoteproc/ste_modem_rproc.c | |||
| @@ -67,8 +67,7 @@ static int sproc_load_segments(struct rproc *rproc, const struct firmware *fw) | |||
| 67 | static const struct ste_toc_entry *sproc_find_rsc_entry(const void *data) | 67 | static const struct ste_toc_entry *sproc_find_rsc_entry(const void *data) |
| 68 | { | 68 | { |
| 69 | int i; | 69 | int i; |
| 70 | const struct ste_toc *toc; | 70 | const struct ste_toc *toc = data; |
| 71 | toc = data; | ||
| 72 | 71 | ||
| 73 | /* Search the table for the resource table */ | 72 | /* Search the table for the resource table */ |
| 74 | for (i = 0; i < SPROC_MAX_TOC_ENTRIES && | 73 | for (i = 0; i < SPROC_MAX_TOC_ENTRIES && |
| @@ -230,6 +229,7 @@ static int sproc_start(struct rproc *rproc) | |||
| 230 | static int sproc_stop(struct rproc *rproc) | 229 | static int sproc_stop(struct rproc *rproc) |
| 231 | { | 230 | { |
| 232 | struct sproc *sproc = rproc->priv; | 231 | struct sproc *sproc = rproc->priv; |
| 232 | |||
| 233 | sproc_dbg(sproc, "stop ste-modem\n"); | 233 | sproc_dbg(sproc, "stop ste-modem\n"); |
| 234 | 234 | ||
| 235 | return sproc->mdev->ops.power(sproc->mdev, false); | 235 | return sproc->mdev->ops.power(sproc->mdev, false); |
diff --git a/drivers/remoteproc/wkup_m3_rproc.c b/drivers/remoteproc/wkup_m3_rproc.c new file mode 100644 index 000000000000..edf81819cce1 --- /dev/null +++ b/drivers/remoteproc/wkup_m3_rproc.c | |||
| @@ -0,0 +1,257 @@ | |||
| 1 | /* | ||
| 2 | * TI AMx3 Wakeup M3 Remote Processor driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2014-2015 Texas Instruments, Inc. | ||
| 5 | * | ||
| 6 | * Dave Gerlach <d-gerlach@ti.com> | ||
| 7 | * Suman Anna <s-anna@ti.com> | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License | ||
| 11 | * version 2 as published by the Free Software Foundation. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, | ||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | * GNU General Public License for more details. | ||
| 17 | */ | ||
| 18 | |||
| 19 | #include <linux/err.h> | ||
| 20 | #include <linux/interrupt.h> | ||
| 21 | #include <linux/kernel.h> | ||
| 22 | #include <linux/module.h> | ||
| 23 | #include <linux/of_device.h> | ||
| 24 | #include <linux/of_address.h> | ||
| 25 | #include <linux/platform_device.h> | ||
| 26 | #include <linux/pm_runtime.h> | ||
| 27 | #include <linux/remoteproc.h> | ||
| 28 | |||
| 29 | #include <linux/platform_data/wkup_m3.h> | ||
| 30 | |||
| 31 | #include "remoteproc_internal.h" | ||
| 32 | |||
| 33 | #define WKUPM3_MEM_MAX 2 | ||
| 34 | |||
| 35 | /** | ||
| 36 | * struct wkup_m3_mem - WkupM3 internal memory structure | ||
| 37 | * @cpu_addr: MPU virtual address of the memory region | ||
| 38 | * @bus_addr: Bus address used to access the memory region | ||
| 39 | * @dev_addr: Device address from Wakeup M3 view | ||
| 40 | * @size: Size of the memory region | ||
| 41 | */ | ||
| 42 | struct wkup_m3_mem { | ||
| 43 | void __iomem *cpu_addr; | ||
| 44 | phys_addr_t bus_addr; | ||
| 45 | u32 dev_addr; | ||
| 46 | size_t size; | ||
| 47 | }; | ||
| 48 | |||
| 49 | /** | ||
| 50 | * struct wkup_m3_rproc - WkupM3 remote processor state | ||
| 51 | * @rproc: rproc handle | ||
| 52 | * @pdev: pointer to platform device | ||
| 53 | * @mem: WkupM3 memory information | ||
| 54 | */ | ||
| 55 | struct wkup_m3_rproc { | ||
| 56 | struct rproc *rproc; | ||
| 57 | struct platform_device *pdev; | ||
| 58 | struct wkup_m3_mem mem[WKUPM3_MEM_MAX]; | ||
| 59 | }; | ||
| 60 | |||
| 61 | static int wkup_m3_rproc_start(struct rproc *rproc) | ||
| 62 | { | ||
| 63 | struct wkup_m3_rproc *wkupm3 = rproc->priv; | ||
| 64 | struct platform_device *pdev = wkupm3->pdev; | ||
| 65 | struct device *dev = &pdev->dev; | ||
| 66 | struct wkup_m3_platform_data *pdata = dev_get_platdata(dev); | ||
| 67 | |||
| 68 | if (pdata->deassert_reset(pdev, pdata->reset_name)) { | ||
| 69 | dev_err(dev, "Unable to reset wkup_m3!\n"); | ||
| 70 | return -ENODEV; | ||
| 71 | } | ||
| 72 | |||
| 73 | return 0; | ||
| 74 | } | ||
| 75 | |||
| 76 | static int wkup_m3_rproc_stop(struct rproc *rproc) | ||
| 77 | { | ||
| 78 | struct wkup_m3_rproc *wkupm3 = rproc->priv; | ||
| 79 | struct platform_device *pdev = wkupm3->pdev; | ||
| 80 | struct device *dev = &pdev->dev; | ||
| 81 | struct wkup_m3_platform_data *pdata = dev_get_platdata(dev); | ||
| 82 | |||
| 83 | if (pdata->assert_reset(pdev, pdata->reset_name)) { | ||
| 84 | dev_err(dev, "Unable to assert reset of wkup_m3!\n"); | ||
| 85 | return -ENODEV; | ||
| 86 | } | ||
| 87 | |||
| 88 | return 0; | ||
| 89 | } | ||
| 90 | |||
| 91 | static void *wkup_m3_rproc_da_to_va(struct rproc *rproc, u64 da, int len) | ||
| 92 | { | ||
| 93 | struct wkup_m3_rproc *wkupm3 = rproc->priv; | ||
| 94 | void *va = NULL; | ||
| 95 | int i; | ||
| 96 | u32 offset; | ||
| 97 | |||
| 98 | if (len <= 0) | ||
| 99 | return NULL; | ||
| 100 | |||
| 101 | for (i = 0; i < WKUPM3_MEM_MAX; i++) { | ||
| 102 | if (da >= wkupm3->mem[i].dev_addr && da + len <= | ||
| 103 | wkupm3->mem[i].dev_addr + wkupm3->mem[i].size) { | ||
| 104 | offset = da - wkupm3->mem[i].dev_addr; | ||
| 105 | /* __force to make sparse happy with type conversion */ | ||
| 106 | va = (__force void *)(wkupm3->mem[i].cpu_addr + offset); | ||
| 107 | break; | ||
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 111 | return va; | ||
| 112 | } | ||
| 113 | |||
| 114 | static struct rproc_ops wkup_m3_rproc_ops = { | ||
| 115 | .start = wkup_m3_rproc_start, | ||
| 116 | .stop = wkup_m3_rproc_stop, | ||
| 117 | .da_to_va = wkup_m3_rproc_da_to_va, | ||
| 118 | }; | ||
| 119 | |||
| 120 | static const struct of_device_id wkup_m3_rproc_of_match[] = { | ||
| 121 | { .compatible = "ti,am3352-wkup-m3", }, | ||
| 122 | { .compatible = "ti,am4372-wkup-m3", }, | ||
| 123 | {}, | ||
| 124 | }; | ||
| 125 | |||
| 126 | static int wkup_m3_rproc_probe(struct platform_device *pdev) | ||
| 127 | { | ||
| 128 | struct device *dev = &pdev->dev; | ||
| 129 | struct wkup_m3_platform_data *pdata = dev->platform_data; | ||
| 130 | /* umem always needs to be processed first */ | ||
| 131 | const char *mem_names[WKUPM3_MEM_MAX] = { "umem", "dmem" }; | ||
| 132 | struct wkup_m3_rproc *wkupm3; | ||
| 133 | const char *fw_name; | ||
| 134 | struct rproc *rproc; | ||
| 135 | struct resource *res; | ||
| 136 | const __be32 *addrp; | ||
| 137 | u32 l4_offset = 0; | ||
| 138 | u64 size; | ||
| 139 | int ret; | ||
| 140 | int i; | ||
| 141 | |||
| 142 | if (!(pdata && pdata->deassert_reset && pdata->assert_reset && | ||
| 143 | pdata->reset_name)) { | ||
| 144 | dev_err(dev, "Platform data missing!\n"); | ||
| 145 | return -ENODEV; | ||
| 146 | } | ||
| 147 | |||
| 148 | ret = of_property_read_string(dev->of_node, "ti,pm-firmware", | ||
| 149 | &fw_name); | ||
| 150 | if (ret) { | ||
| 151 | dev_err(dev, "No firmware filename given\n"); | ||
| 152 | return -ENODEV; | ||
| 153 | } | ||
| 154 | |||
| 155 | pm_runtime_enable(&pdev->dev); | ||
| 156 | ret = pm_runtime_get_sync(&pdev->dev); | ||
| 157 | if (ret < 0) { | ||
| 158 | dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n"); | ||
| 159 | goto err; | ||
| 160 | } | ||
| 161 | |||
| 162 | rproc = rproc_alloc(dev, "wkup_m3", &wkup_m3_rproc_ops, | ||
| 163 | fw_name, sizeof(*wkupm3)); | ||
| 164 | if (!rproc) { | ||
| 165 | ret = -ENOMEM; | ||
| 166 | goto err; | ||
| 167 | } | ||
| 168 | |||
| 169 | wkupm3 = rproc->priv; | ||
| 170 | wkupm3->rproc = rproc; | ||
| 171 | wkupm3->pdev = pdev; | ||
| 172 | |||
| 173 | for (i = 0; i < ARRAY_SIZE(mem_names); i++) { | ||
| 174 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, | ||
| 175 | mem_names[i]); | ||
| 176 | wkupm3->mem[i].cpu_addr = devm_ioremap_resource(dev, res); | ||
| 177 | if (IS_ERR(wkupm3->mem[i].cpu_addr)) { | ||
| 178 | dev_err(&pdev->dev, "devm_ioremap_resource failed for resource %d\n", | ||
| 179 | i); | ||
| 180 | ret = PTR_ERR(wkupm3->mem[i].cpu_addr); | ||
| 181 | goto err; | ||
| 182 | } | ||
| 183 | wkupm3->mem[i].bus_addr = res->start; | ||
| 184 | wkupm3->mem[i].size = resource_size(res); | ||
| 185 | addrp = of_get_address(dev->of_node, i, &size, NULL); | ||
| 186 | /* | ||
| 187 | * The wkupm3 has umem at address 0 in its view, so the device | ||
| 188 | * addresses for each memory region is computed as a relative | ||
| 189 | * offset of the bus address for umem, and therefore needs to be | ||
| 190 | * processed first. | ||
| 191 | */ | ||
| 192 | if (!strcmp(mem_names[i], "umem")) | ||
| 193 | l4_offset = be32_to_cpu(*addrp); | ||
| 194 | wkupm3->mem[i].dev_addr = be32_to_cpu(*addrp) - l4_offset; | ||
| 195 | } | ||
| 196 | |||
| 197 | dev_set_drvdata(dev, rproc); | ||
| 198 | |||
| 199 | ret = rproc_add(rproc); | ||
| 200 | if (ret) { | ||
| 201 | dev_err(dev, "rproc_add failed\n"); | ||
| 202 | goto err_put_rproc; | ||
| 203 | } | ||
| 204 | |||
| 205 | return 0; | ||
| 206 | |||
| 207 | err_put_rproc: | ||
| 208 | rproc_put(rproc); | ||
| 209 | err: | ||
| 210 | pm_runtime_put_noidle(dev); | ||
| 211 | pm_runtime_disable(dev); | ||
| 212 | return ret; | ||
| 213 | } | ||
| 214 | |||
| 215 | static int wkup_m3_rproc_remove(struct platform_device *pdev) | ||
| 216 | { | ||
| 217 | struct rproc *rproc = platform_get_drvdata(pdev); | ||
| 218 | |||
| 219 | rproc_del(rproc); | ||
| 220 | rproc_put(rproc); | ||
| 221 | pm_runtime_put_sync(&pdev->dev); | ||
| 222 | pm_runtime_disable(&pdev->dev); | ||
| 223 | |||
| 224 | return 0; | ||
| 225 | } | ||
| 226 | |||
| 227 | #ifdef CONFIG_PM | ||
| 228 | static int wkup_m3_rpm_suspend(struct device *dev) | ||
| 229 | { | ||
| 230 | return -EBUSY; | ||
| 231 | } | ||
| 232 | |||
| 233 | static int wkup_m3_rpm_resume(struct device *dev) | ||
| 234 | { | ||
| 235 | return 0; | ||
| 236 | } | ||
| 237 | #endif | ||
| 238 | |||
| 239 | static const struct dev_pm_ops wkup_m3_rproc_pm_ops = { | ||
| 240 | SET_RUNTIME_PM_OPS(wkup_m3_rpm_suspend, wkup_m3_rpm_resume, NULL) | ||
| 241 | }; | ||
| 242 | |||
| 243 | static struct platform_driver wkup_m3_rproc_driver = { | ||
| 244 | .probe = wkup_m3_rproc_probe, | ||
| 245 | .remove = wkup_m3_rproc_remove, | ||
| 246 | .driver = { | ||
| 247 | .name = "wkup_m3_rproc", | ||
| 248 | .of_match_table = wkup_m3_rproc_of_match, | ||
| 249 | .pm = &wkup_m3_rproc_pm_ops, | ||
| 250 | }, | ||
| 251 | }; | ||
| 252 | |||
| 253 | module_platform_driver(wkup_m3_rproc_driver); | ||
| 254 | |||
| 255 | MODULE_LICENSE("GPL v2"); | ||
| 256 | MODULE_DESCRIPTION("TI Wakeup M3 remote processor control driver"); | ||
| 257 | MODULE_AUTHOR("Dave Gerlach <d-gerlach@ti.com>"); | ||
diff --git a/include/linux/platform_data/wkup_m3.h b/include/linux/platform_data/wkup_m3.h new file mode 100644 index 000000000000..3f1d77effd71 --- /dev/null +++ b/include/linux/platform_data/wkup_m3.h | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | /* | ||
| 2 | * TI Wakeup M3 remote processor platform data | ||
| 3 | * | ||
| 4 | * Copyright (C) 2014-2015 Texas Instruments, Inc. | ||
| 5 | * | ||
| 6 | * Dave Gerlach <d-gerlach@ti.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | */ | ||
| 17 | |||
| 18 | #ifndef _LINUX_PLATFORM_DATA_WKUP_M3_H | ||
| 19 | #define _LINUX_PLATFORM_DATA_WKUP_M3_H | ||
| 20 | |||
| 21 | struct platform_device; | ||
| 22 | |||
| 23 | struct wkup_m3_platform_data { | ||
| 24 | const char *reset_name; | ||
| 25 | |||
| 26 | int (*assert_reset)(struct platform_device *pdev, const char *name); | ||
| 27 | int (*deassert_reset)(struct platform_device *pdev, const char *name); | ||
| 28 | }; | ||
| 29 | |||
| 30 | #endif /* _LINUX_PLATFORM_DATA_WKUP_M3_H */ | ||
diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index 78b8a9b9d40a..9c4e1384f636 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h | |||
| @@ -36,11 +36,11 @@ | |||
| 36 | #define REMOTEPROC_H | 36 | #define REMOTEPROC_H |
| 37 | 37 | ||
| 38 | #include <linux/types.h> | 38 | #include <linux/types.h> |
| 39 | #include <linux/klist.h> | ||
| 40 | #include <linux/mutex.h> | 39 | #include <linux/mutex.h> |
| 41 | #include <linux/virtio.h> | 40 | #include <linux/virtio.h> |
| 42 | #include <linux/completion.h> | 41 | #include <linux/completion.h> |
| 43 | #include <linux/idr.h> | 42 | #include <linux/idr.h> |
| 43 | #include <linux/of.h> | ||
| 44 | 44 | ||
| 45 | /** | 45 | /** |
| 46 | * struct resource_table - firmware resource table header | 46 | * struct resource_table - firmware resource table header |
| @@ -330,11 +330,13 @@ struct rproc; | |||
| 330 | * @start: power on the device and boot it | 330 | * @start: power on the device and boot it |
| 331 | * @stop: power off the device | 331 | * @stop: power off the device |
| 332 | * @kick: kick a virtqueue (virtqueue id given as a parameter) | 332 | * @kick: kick a virtqueue (virtqueue id given as a parameter) |
| 333 | * @da_to_va: optional platform hook to perform address translations | ||
| 333 | */ | 334 | */ |
| 334 | struct rproc_ops { | 335 | struct rproc_ops { |
| 335 | int (*start)(struct rproc *rproc); | 336 | int (*start)(struct rproc *rproc); |
| 336 | int (*stop)(struct rproc *rproc); | 337 | int (*stop)(struct rproc *rproc); |
| 337 | void (*kick)(struct rproc *rproc, int vqid); | 338 | void (*kick)(struct rproc *rproc, int vqid); |
| 339 | void * (*da_to_va)(struct rproc *rproc, u64 da, int len); | ||
| 338 | }; | 340 | }; |
| 339 | 341 | ||
| 340 | /** | 342 | /** |
| @@ -375,7 +377,7 @@ enum rproc_crash_type { | |||
| 375 | 377 | ||
| 376 | /** | 378 | /** |
| 377 | * struct rproc - represents a physical remote processor device | 379 | * struct rproc - represents a physical remote processor device |
| 378 | * @node: klist node of this rproc object | 380 | * @node: list node of this rproc object |
| 379 | * @domain: iommu domain | 381 | * @domain: iommu domain |
| 380 | * @name: human readable name of the rproc | 382 | * @name: human readable name of the rproc |
| 381 | * @firmware: name of firmware file to be loaded | 383 | * @firmware: name of firmware file to be loaded |
| @@ -407,7 +409,7 @@ enum rproc_crash_type { | |||
| 407 | * @has_iommu: flag to indicate if remote processor is behind an MMU | 409 | * @has_iommu: flag to indicate if remote processor is behind an MMU |
| 408 | */ | 410 | */ |
| 409 | struct rproc { | 411 | struct rproc { |
| 410 | struct klist_node node; | 412 | struct list_head node; |
| 411 | struct iommu_domain *domain; | 413 | struct iommu_domain *domain; |
| 412 | const char *name; | 414 | const char *name; |
| 413 | const char *firmware; | 415 | const char *firmware; |
| @@ -481,6 +483,7 @@ struct rproc_vdev { | |||
| 481 | u32 rsc_offset; | 483 | u32 rsc_offset; |
| 482 | }; | 484 | }; |
| 483 | 485 | ||
| 486 | struct rproc *rproc_get_by_phandle(phandle phandle); | ||
| 484 | struct rproc *rproc_alloc(struct device *dev, const char *name, | 487 | struct rproc *rproc_alloc(struct device *dev, const char *name, |
| 485 | const struct rproc_ops *ops, | 488 | const struct rproc_ops *ops, |
| 486 | const char *firmware, int len); | 489 | const char *firmware, int len); |
