mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 18:51:56 -04:00
remoteproc: Move qcom_mdt_loader into drivers/soc/qcom
With the remoteproc parts cleaned out of the MDT loader we can move it to drivers/soc/qcom. Acked-by: Andy Gross <[email protected]> Signed-off-by: Bjorn Andersson <[email protected]>
This commit is contained in:
parent
7f0dd07a9b
commit
2aad40d911
9 changed files with 14 additions and 9 deletions
|
|
@ -10,6 +10,10 @@ config QCOM_GSBI
|
|||
functions for connecting the underlying serial UART, SPI, and I2C
|
||||
devices to the output pins.
|
||||
|
||||
config QCOM_MDT_LOADER
|
||||
tristate
|
||||
select QCOM_SCM
|
||||
|
||||
config QCOM_PM
|
||||
bool "Qualcomm Power Management"
|
||||
depends on ARCH_QCOM && !ARM64
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o
|
||||
obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o
|
||||
obj-$(CONFIG_QCOM_PM) += spm.o
|
||||
obj-$(CONFIG_QCOM_SMD) += smd.o
|
||||
obj-$(CONFIG_QCOM_SMD_RPM) += smd-rpm.o
|
||||
|
|
|
|||
204
drivers/soc/qcom/mdt_loader.c
Normal file
204
drivers/soc/qcom/mdt_loader.c
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
* Qualcomm Peripheral Image Loader
|
||||
*
|
||||
* Copyright (C) 2016 Linaro Ltd
|
||||
* Copyright (C) 2015 Sony Mobile Communications Inc
|
||||
* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/elf.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/qcom_scm.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/soc/qcom/mdt_loader.h>
|
||||
|
||||
static bool mdt_phdr_valid(const struct elf32_phdr *phdr)
|
||||
{
|
||||
if (phdr->p_type != PT_LOAD)
|
||||
return false;
|
||||
|
||||
if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH)
|
||||
return false;
|
||||
|
||||
if (!phdr->p_memsz)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* qcom_mdt_get_size() - acquire size of the memory region needed to load mdt
|
||||
* @fw: firmware object for the mdt file
|
||||
*
|
||||
* Returns size of the loaded firmware blob, or -EINVAL on failure.
|
||||
*/
|
||||
ssize_t qcom_mdt_get_size(const struct firmware *fw)
|
||||
{
|
||||
const struct elf32_phdr *phdrs;
|
||||
const struct elf32_phdr *phdr;
|
||||
const struct elf32_hdr *ehdr;
|
||||
phys_addr_t min_addr = (phys_addr_t)ULLONG_MAX;
|
||||
phys_addr_t max_addr = 0;
|
||||
int i;
|
||||
|
||||
ehdr = (struct elf32_hdr *)fw->data;
|
||||
phdrs = (struct elf32_phdr *)(ehdr + 1);
|
||||
|
||||
for (i = 0; i < ehdr->e_phnum; i++) {
|
||||
phdr = &phdrs[i];
|
||||
|
||||
if (!mdt_phdr_valid(phdr))
|
||||
continue;
|
||||
|
||||
if (phdr->p_paddr < min_addr)
|
||||
min_addr = phdr->p_paddr;
|
||||
|
||||
if (phdr->p_paddr + phdr->p_memsz > max_addr)
|
||||
max_addr = ALIGN(phdr->p_paddr + phdr->p_memsz, SZ_4K);
|
||||
}
|
||||
|
||||
return min_addr < max_addr ? max_addr - min_addr : -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_mdt_get_size);
|
||||
|
||||
/**
|
||||
* qcom_mdt_load() - load the firmware which header is loaded as fw
|
||||
* @dev: device handle to associate resources with
|
||||
* @fw: firmware object for the mdt file
|
||||
* @firmware: name of the firmware, for construction of segment file names
|
||||
* @pas_id: PAS identifier
|
||||
* @mem_region: allocated memory region to load firmware into
|
||||
* @mem_phys: physical address of allocated memory region
|
||||
* @mem_size: size of the allocated memory region
|
||||
*
|
||||
* Returns 0 on success, negative errno otherwise.
|
||||
*/
|
||||
int qcom_mdt_load(struct device *dev, const struct firmware *fw,
|
||||
const char *firmware, int pas_id, void *mem_region,
|
||||
phys_addr_t mem_phys, size_t mem_size)
|
||||
{
|
||||
const struct elf32_phdr *phdrs;
|
||||
const struct elf32_phdr *phdr;
|
||||
const struct elf32_hdr *ehdr;
|
||||
const struct firmware *seg_fw;
|
||||
phys_addr_t mem_reloc;
|
||||
phys_addr_t min_addr = (phys_addr_t)ULLONG_MAX;
|
||||
phys_addr_t max_addr = 0;
|
||||
size_t fw_name_len;
|
||||
size_t offset;
|
||||
char *fw_name;
|
||||
bool relocate = false;
|
||||
void *ptr;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
if (!fw || !mem_region || !mem_phys || !mem_size)
|
||||
return -EINVAL;
|
||||
|
||||
ehdr = (struct elf32_hdr *)fw->data;
|
||||
phdrs = (struct elf32_phdr *)(ehdr + 1);
|
||||
|
||||
fw_name_len = strlen(firmware);
|
||||
if (fw_name_len <= 4)
|
||||
return -EINVAL;
|
||||
|
||||
fw_name = kstrdup(firmware, GFP_KERNEL);
|
||||
if (!fw_name)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = qcom_scm_pas_init_image(pas_id, fw->data, fw->size);
|
||||
if (ret) {
|
||||
dev_err(dev, "invalid firmware metadata\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < ehdr->e_phnum; i++) {
|
||||
phdr = &phdrs[i];
|
||||
|
||||
if (!mdt_phdr_valid(phdr))
|
||||
continue;
|
||||
|
||||
if (phdr->p_flags & QCOM_MDT_RELOCATABLE)
|
||||
relocate = true;
|
||||
|
||||
if (phdr->p_paddr < min_addr)
|
||||
min_addr = phdr->p_paddr;
|
||||
|
||||
if (phdr->p_paddr + phdr->p_memsz > max_addr)
|
||||
max_addr = ALIGN(phdr->p_paddr + phdr->p_memsz, SZ_4K);
|
||||
}
|
||||
|
||||
if (relocate) {
|
||||
ret = qcom_scm_pas_mem_setup(pas_id, mem_phys, max_addr - min_addr);
|
||||
if (ret) {
|
||||
dev_err(dev, "unable to setup relocation\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* The image is relocatable, so offset each segment based on
|
||||
* the lowest segment address.
|
||||
*/
|
||||
mem_reloc = min_addr;
|
||||
} else {
|
||||
/*
|
||||
* Image is not relocatable, so offset each segment based on
|
||||
* the allocated physical chunk of memory.
|
||||
*/
|
||||
mem_reloc = mem_phys;
|
||||
}
|
||||
|
||||
for (i = 0; i < ehdr->e_phnum; i++) {
|
||||
phdr = &phdrs[i];
|
||||
|
||||
if (!mdt_phdr_valid(phdr))
|
||||
continue;
|
||||
|
||||
offset = phdr->p_paddr - mem_reloc;
|
||||
if (offset < 0 || offset + phdr->p_memsz > mem_size) {
|
||||
dev_err(dev, "segment outside memory range\n");
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
ptr = mem_region + offset;
|
||||
|
||||
if (phdr->p_filesz) {
|
||||
sprintf(fw_name + fw_name_len - 3, "b%02d", i);
|
||||
ret = request_firmware(&seg_fw, fw_name, dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to load %s\n", fw_name);
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(ptr, seg_fw->data, seg_fw->size);
|
||||
|
||||
release_firmware(seg_fw);
|
||||
}
|
||||
|
||||
if (phdr->p_memsz > phdr->p_filesz)
|
||||
memset(ptr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz);
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(fw_name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_mdt_load);
|
||||
|
||||
MODULE_DESCRIPTION("Firmware parser for Qualcomm MDT format");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
Loading…
Reference in a new issue