More Qualcomm driver updates for 5.13
This improves the Qualcomm SCM driver logic related to detecting the calling convention, in particular on SC7180, and fixes a few small issues in the same. It introduces additonal sanity checks of the size of loaded segments in the MDT loader and adds a missing error in the return path of pdr_register_listener(). It makes it possible to specify the OEM specific firmware path in the wcn36xx control (and WiFi) driver. Lastly it adds a missing path specifier in the MAINTAINERS' entry and fixes a bunch of kerneldoc issues in various drivers. -----BEGIN PGP SIGNATURE----- iQJPBAABCAA5FiEEBd4DzF816k8JZtUlCx85Pw2ZrcUFAmBwfnAbHGJqb3JuLmFu ZGVyc3NvbkBsaW5hcm8ub3JnAAoJEAsfOT8Nma3F1CEP/R8ZxRwzXdKUpS+SCzLp lJoqE+bhNt7IbK4hVfw1poA+UW8iQN0xkHS/JIbAvEBefRxJ/6dikrli/6oqQM9u 0kIpWHp30QyFOt+i8VEzhLZrNZblFmlHu+AtYjicoGftNdsF1p6TKESzoEUXM+Bo /m0c9Wuf00CQl+6E6/GrV04MnAJXehWum+c1BsD9drs1Er9A2BxHo/8KQmrzmagl cOsEh1k1PDSUhUSbbCFSqzGweShXY1R9K+UKz0KKJPW3/Wduzd+wJ6eXCiDJEZvY yq7Yim7hS3C8JjSoWipaNkUZFlnwgJs3BZnYcTPKv8esX+XxGUAp/fAE2/qizswv D6rKc876xyHMcFvh+9eXPYnXQgE3vg5Jt0Yqf/oc8iuvYncqYtf+SdrLvD5o8jEY GW7gIxeGRQ4cnQWoArj33usevxqLdiPp2tAliztxfEjUhNDJwm2MWPJzLgUW0BJ8 grEl5kfkU6Q90F3NW5gObq1uVFbfF0nTxeyEbC1mIJ8/78xINh4SLnSy8fmcjNT4 ZfPCX6sb45baYqpe7N5AeuXWXDMkl+pjQXU5UGyp2ZLDisCQlXmo1fj6hOk6tL5P uC1UYYaZaBBMpqEyCDyhk1Xe0POZSlHjx2nVhDL0JrZic7KbjlRhz9/RNO+Wonhs ypQ51qoAXeJu1PISHGmhRWwA =C6IS -----END PGP SIGNATURE----- Merge tag 'qcom-drivers-for-5.13-2' of git://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux into arm/drivers More Qualcomm driver updates for 5.13 This improves the Qualcomm SCM driver logic related to detecting the calling convention, in particular on SC7180, and fixes a few small issues in the same. It introduces additonal sanity checks of the size of loaded segments in the MDT loader and adds a missing error in the return path of pdr_register_listener(). It makes it possible to specify the OEM specific firmware path in the wcn36xx control (and WiFi) driver. Lastly it adds a missing path specifier in the MAINTAINERS' entry and fixes a bunch of kerneldoc issues in various drivers. * tag 'qcom-drivers-for-5.13-2' of git://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux: soc: qcom: mdt_loader: Detect truncated read of segments soc: qcom: mdt_loader: Validate that p_filesz < p_memsz soc: qcom: pdr: Fix error return code in pdr_register_listener firmware: qcom_scm: Fix kernel-doc function names to match firmware: qcom_scm: Suppress sysfs bind attributes firmware: qcom_scm: Workaround lack of "is available" call on SC7180 firmware: qcom_scm: Reduce locking section for __get_convention() firmware: qcom_scm: Make __qcom_scm_is_call_available() return bool soc: qcom: wcnss_ctrl: Allow reading firmware-name from DT soc: qcom: wcnss_ctrl: Introduce local variable "dev" dt-bindings: soc: qcom: wcnss: Add firmware-name property soc: qcom: address kernel-doc warnings MAINTAINERS: add another entry for ARM/QUALCOMM SUPPORT Link: https://lore.kernel.org/r/20210409162001.775851-1-bjorn.andersson@linaro.org Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
Коммит
30be8446db
|
@ -24,6 +24,13 @@ block and a BT, WiFi and FM radio block, all using SMD as command channels.
|
|||
"qcom,riva",
|
||||
"qcom,pronto"
|
||||
|
||||
- firmware-name:
|
||||
Usage: optional
|
||||
Value type: <string>
|
||||
Definition: specifies the relative firmware image path for the WLAN NV
|
||||
blob. Defaults to "wlan/prima/WCNSS_qcom_wlan_nv.bin" if
|
||||
not specified.
|
||||
|
||||
= SUBNODES
|
||||
The subnodes of the wcnss node are optional and describe the individual blocks in
|
||||
the WCNSS.
|
||||
|
|
|
@ -2296,6 +2296,7 @@ F: drivers/tty/serial/msm_serial.c
|
|||
F: drivers/usb/dwc3/dwc3-qcom.c
|
||||
F: include/dt-bindings/*/qcom*
|
||||
F: include/linux/*/qcom*
|
||||
F: include/linux/soc/qcom/
|
||||
|
||||
ARM/RADISYS ENP2611 MACHINE SUPPORT
|
||||
M: Lennert Buytenhek <kernel@wantstofly.org>
|
||||
|
|
|
@ -118,7 +118,7 @@ static void __scm_legacy_do(const struct arm_smccc_args *smc,
|
|||
}
|
||||
|
||||
/**
|
||||
* qcom_scm_call() - Sends a command to the SCM and waits for the command to
|
||||
* scm_legacy_call() - Sends a command to the SCM and waits for the command to
|
||||
* finish processing.
|
||||
*
|
||||
* A note on cache maintenance:
|
||||
|
@ -209,7 +209,7 @@ out:
|
|||
(n & 0xf))
|
||||
|
||||
/**
|
||||
* qcom_scm_call_atomic() - Send an atomic SCM command with up to 5 arguments
|
||||
* scm_legacy_call_atomic() - Send an atomic SCM command with up to 5 arguments
|
||||
* and 3 return values
|
||||
* @desc: SCM call descriptor containing arguments
|
||||
* @res: SCM call return values
|
||||
|
|
|
@ -77,8 +77,10 @@ static void __scm_smc_do(const struct arm_smccc_args *smc,
|
|||
} while (res->a0 == QCOM_SCM_V2_EBUSY);
|
||||
}
|
||||
|
||||
int scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc,
|
||||
struct qcom_scm_res *res, bool atomic)
|
||||
|
||||
int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc,
|
||||
enum qcom_scm_convention qcom_convention,
|
||||
struct qcom_scm_res *res, bool atomic)
|
||||
{
|
||||
int arglen = desc->arginfo & 0xf;
|
||||
int i;
|
||||
|
@ -87,9 +89,8 @@ int scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc,
|
|||
size_t alloc_len;
|
||||
gfp_t flag = atomic ? GFP_ATOMIC : GFP_KERNEL;
|
||||
u32 smccc_call_type = atomic ? ARM_SMCCC_FAST_CALL : ARM_SMCCC_STD_CALL;
|
||||
u32 qcom_smccc_convention =
|
||||
(qcom_scm_convention == SMC_CONVENTION_ARM_32) ?
|
||||
ARM_SMCCC_SMC_32 : ARM_SMCCC_SMC_64;
|
||||
u32 qcom_smccc_convention = (qcom_convention == SMC_CONVENTION_ARM_32) ?
|
||||
ARM_SMCCC_SMC_32 : ARM_SMCCC_SMC_64;
|
||||
struct arm_smccc_res smc_res;
|
||||
struct arm_smccc_args smc = {0};
|
||||
|
||||
|
@ -148,4 +149,5 @@ int scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc,
|
|||
}
|
||||
|
||||
return (long)smc_res.a0 ? qcom_scm_remap_error(smc_res.a0) : 0;
|
||||
|
||||
}
|
||||
|
|
|
@ -113,14 +113,10 @@ static void qcom_scm_clk_disable(void)
|
|||
clk_disable_unprepare(__scm->bus_clk);
|
||||
}
|
||||
|
||||
static int __qcom_scm_is_call_available(struct device *dev, u32 svc_id,
|
||||
u32 cmd_id);
|
||||
enum qcom_scm_convention qcom_scm_convention = SMC_CONVENTION_UNKNOWN;
|
||||
static DEFINE_SPINLOCK(scm_query_lock);
|
||||
|
||||
enum qcom_scm_convention qcom_scm_convention;
|
||||
static bool has_queried __read_mostly;
|
||||
static DEFINE_SPINLOCK(query_lock);
|
||||
|
||||
static void __query_convention(void)
|
||||
static enum qcom_scm_convention __get_convention(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct qcom_scm_desc desc = {
|
||||
|
@ -133,36 +129,50 @@ static void __query_convention(void)
|
|||
.owner = ARM_SMCCC_OWNER_SIP,
|
||||
};
|
||||
struct qcom_scm_res res;
|
||||
enum qcom_scm_convention probed_convention;
|
||||
int ret;
|
||||
bool forced = false;
|
||||
|
||||
spin_lock_irqsave(&query_lock, flags);
|
||||
if (has_queried)
|
||||
goto out;
|
||||
if (likely(qcom_scm_convention != SMC_CONVENTION_UNKNOWN))
|
||||
return qcom_scm_convention;
|
||||
|
||||
qcom_scm_convention = SMC_CONVENTION_ARM_64;
|
||||
// Device isn't required as there is only one argument - no device
|
||||
// needed to dma_map_single to secure world
|
||||
ret = scm_smc_call(NULL, &desc, &res, true);
|
||||
/*
|
||||
* Device isn't required as there is only one argument - no device
|
||||
* needed to dma_map_single to secure world
|
||||
*/
|
||||
probed_convention = SMC_CONVENTION_ARM_64;
|
||||
ret = __scm_smc_call(NULL, &desc, probed_convention, &res, true);
|
||||
if (!ret && res.result[0] == 1)
|
||||
goto out;
|
||||
goto found;
|
||||
|
||||
qcom_scm_convention = SMC_CONVENTION_ARM_32;
|
||||
ret = scm_smc_call(NULL, &desc, &res, true);
|
||||
/*
|
||||
* Some SC7180 firmwares didn't implement the
|
||||
* QCOM_SCM_INFO_IS_CALL_AVAIL call, so we fallback to forcing ARM_64
|
||||
* calling conventions on these firmwares. Luckily we don't make any
|
||||
* early calls into the firmware on these SoCs so the device pointer
|
||||
* will be valid here to check if the compatible matches.
|
||||
*/
|
||||
if (of_device_is_compatible(__scm ? __scm->dev->of_node : NULL, "qcom,scm-sc7180")) {
|
||||
forced = true;
|
||||
goto found;
|
||||
}
|
||||
|
||||
probed_convention = SMC_CONVENTION_ARM_32;
|
||||
ret = __scm_smc_call(NULL, &desc, probed_convention, &res, true);
|
||||
if (!ret && res.result[0] == 1)
|
||||
goto out;
|
||||
goto found;
|
||||
|
||||
qcom_scm_convention = SMC_CONVENTION_LEGACY;
|
||||
out:
|
||||
has_queried = true;
|
||||
spin_unlock_irqrestore(&query_lock, flags);
|
||||
pr_info("qcom_scm: convention: %s\n",
|
||||
qcom_scm_convention_names[qcom_scm_convention]);
|
||||
}
|
||||
probed_convention = SMC_CONVENTION_LEGACY;
|
||||
found:
|
||||
spin_lock_irqsave(&scm_query_lock, flags);
|
||||
if (probed_convention != qcom_scm_convention) {
|
||||
qcom_scm_convention = probed_convention;
|
||||
pr_info("qcom_scm: convention: %s%s\n",
|
||||
qcom_scm_convention_names[qcom_scm_convention],
|
||||
forced ? " (forced)" : "");
|
||||
}
|
||||
spin_unlock_irqrestore(&scm_query_lock, flags);
|
||||
|
||||
static inline enum qcom_scm_convention __get_convention(void)
|
||||
{
|
||||
if (unlikely(!has_queried))
|
||||
__query_convention();
|
||||
return qcom_scm_convention;
|
||||
}
|
||||
|
||||
|
@ -219,8 +229,8 @@ static int qcom_scm_call_atomic(struct device *dev,
|
|||
}
|
||||
}
|
||||
|
||||
static int __qcom_scm_is_call_available(struct device *dev, u32 svc_id,
|
||||
u32 cmd_id)
|
||||
static bool __qcom_scm_is_call_available(struct device *dev, u32 svc_id,
|
||||
u32 cmd_id)
|
||||
{
|
||||
int ret;
|
||||
struct qcom_scm_desc desc = {
|
||||
|
@ -247,7 +257,7 @@ static int __qcom_scm_is_call_available(struct device *dev, u32 svc_id,
|
|||
|
||||
ret = qcom_scm_call(dev, &desc, &res);
|
||||
|
||||
return ret ? : res.result[0];
|
||||
return ret ? false : !!res.result[0];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -585,9 +595,8 @@ bool qcom_scm_pas_supported(u32 peripheral)
|
|||
};
|
||||
struct qcom_scm_res res;
|
||||
|
||||
ret = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL,
|
||||
QCOM_SCM_PIL_PAS_IS_SUPPORTED);
|
||||
if (ret <= 0)
|
||||
if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL,
|
||||
QCOM_SCM_PIL_PAS_IS_SUPPORTED))
|
||||
return false;
|
||||
|
||||
ret = qcom_scm_call(__scm->dev, &desc, &res);
|
||||
|
@ -1060,17 +1069,18 @@ EXPORT_SYMBOL(qcom_scm_ice_set_key);
|
|||
*/
|
||||
bool qcom_scm_hdcp_available(void)
|
||||
{
|
||||
bool avail;
|
||||
int ret = qcom_scm_clk_enable();
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_HDCP,
|
||||
avail = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_HDCP,
|
||||
QCOM_SCM_HDCP_INVOKE);
|
||||
|
||||
qcom_scm_clk_disable();
|
||||
|
||||
return ret > 0;
|
||||
return avail;
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_scm_hdcp_available);
|
||||
|
||||
|
@ -1242,7 +1252,7 @@ static int qcom_scm_probe(struct platform_device *pdev)
|
|||
__scm = scm;
|
||||
__scm->dev = &pdev->dev;
|
||||
|
||||
__query_convention();
|
||||
__get_convention();
|
||||
|
||||
/*
|
||||
* If requested enable "download mode", from this point on warmboot
|
||||
|
@ -1291,6 +1301,7 @@ static struct platform_driver qcom_scm_driver = {
|
|||
.driver = {
|
||||
.name = "qcom_scm",
|
||||
.of_match_table = qcom_scm_dt_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = qcom_scm_probe,
|
||||
.shutdown = qcom_scm_shutdown,
|
||||
|
|
|
@ -61,8 +61,11 @@ struct qcom_scm_res {
|
|||
};
|
||||
|
||||
#define SCM_SMC_FNID(s, c) ((((s) & 0xFF) << 8) | ((c) & 0xFF))
|
||||
extern int scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc,
|
||||
struct qcom_scm_res *res, bool atomic);
|
||||
extern int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc,
|
||||
enum qcom_scm_convention qcom_convention,
|
||||
struct qcom_scm_res *res, bool atomic);
|
||||
#define scm_smc_call(dev, desc, res, atomic) \
|
||||
__scm_smc_call((dev), (desc), qcom_scm_convention, (res), (atomic))
|
||||
|
||||
#define SCM_LEGACY_FNID(s, c) (((s) << 10) | ((c) & 0x3ff))
|
||||
extern int scm_legacy_call_atomic(struct device *dev,
|
||||
|
|
|
@ -230,6 +230,14 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw,
|
|||
break;
|
||||
}
|
||||
|
||||
if (phdr->p_filesz > phdr->p_memsz) {
|
||||
dev_err(dev,
|
||||
"refusing to load segment %d with p_filesz > p_memsz\n",
|
||||
i);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
ptr = mem_region + offset;
|
||||
|
||||
if (phdr->p_filesz && phdr->p_offset < fw->size) {
|
||||
|
@ -253,6 +261,15 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw,
|
|||
break;
|
||||
}
|
||||
|
||||
if (seg_fw->size != phdr->p_filesz) {
|
||||
dev_err(dev,
|
||||
"failed to load segment %d from truncated file %s\n",
|
||||
i, fw_name);
|
||||
release_firmware(seg_fw);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
release_firmware(seg_fw);
|
||||
}
|
||||
|
||||
|
|
|
@ -153,7 +153,7 @@ static int pdr_register_listener(struct pdr_handle *pdr,
|
|||
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
|
||||
pr_err("PDR: %s register listener failed: 0x%x\n",
|
||||
pds->service_path, resp.resp.error);
|
||||
return ret;
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
|
||||
pds->state = resp.curr_state;
|
||||
|
|
|
@ -199,6 +199,8 @@ static int wcnss_download_nv(struct wcnss_ctrl *wcnss, bool *expect_cbc)
|
|||
{
|
||||
struct wcnss_download_nv_req *req;
|
||||
const struct firmware *fw;
|
||||
struct device *dev = wcnss->dev;
|
||||
const char *nvbin = NVBIN_FILE;
|
||||
const void *data;
|
||||
ssize_t left;
|
||||
int ret;
|
||||
|
@ -207,10 +209,13 @@ static int wcnss_download_nv(struct wcnss_ctrl *wcnss, bool *expect_cbc)
|
|||
if (!req)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = request_firmware(&fw, NVBIN_FILE, wcnss->dev);
|
||||
ret = of_property_read_string(dev->of_node, "firmware-name", &nvbin);
|
||||
if (ret < 0 && ret != -EINVAL)
|
||||
goto free_req;
|
||||
|
||||
ret = request_firmware(&fw, nvbin, dev);
|
||||
if (ret < 0) {
|
||||
dev_err(wcnss->dev, "Failed to load nv file %s: %d\n",
|
||||
NVBIN_FILE, ret);
|
||||
dev_err(dev, "Failed to load nv file %s: %d\n", nvbin, ret);
|
||||
goto free_req;
|
||||
}
|
||||
|
||||
|
@ -235,7 +240,7 @@ static int wcnss_download_nv(struct wcnss_ctrl *wcnss, bool *expect_cbc)
|
|||
|
||||
ret = rpmsg_send(wcnss->channel, req, req->hdr.len);
|
||||
if (ret < 0) {
|
||||
dev_err(wcnss->dev, "failed to send smd packet\n");
|
||||
dev_err(dev, "failed to send smd packet\n");
|
||||
goto release_fw;
|
||||
}
|
||||
|
||||
|
@ -248,7 +253,7 @@ static int wcnss_download_nv(struct wcnss_ctrl *wcnss, bool *expect_cbc)
|
|||
|
||||
ret = wait_for_completion_timeout(&wcnss->ack, WCNSS_REQUEST_TIMEOUT);
|
||||
if (!ret) {
|
||||
dev_err(wcnss->dev, "timeout waiting for nv upload ack\n");
|
||||
dev_err(dev, "timeout waiting for nv upload ack\n");
|
||||
ret = -ETIMEDOUT;
|
||||
} else {
|
||||
*expect_cbc = wcnss->ack_status == WCNSS_ACK_COLD_BOOTING;
|
||||
|
|
|
@ -113,7 +113,7 @@ void apr_driver_unregister(struct apr_driver *drv);
|
|||
|
||||
/**
|
||||
* module_apr_driver() - Helper macro for registering a aprbus driver
|
||||
* @__aprbus_driver: aprbus_driver struct
|
||||
* @__apr_driver: apr_driver struct
|
||||
*
|
||||
* Helper macro for aprbus drivers which do not do anything special in
|
||||
* module init/exit. This eliminates a lot of boilerplate. Each module
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
#define GPIO_NO_WAKE_IRQ ~0U
|
||||
|
||||
/**
|
||||
/*
|
||||
* QCOM specific IRQ domain flags that distinguishes the handling of wakeup
|
||||
* capable interrupts by different interrupt controllers.
|
||||
*
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
#define LLCC_WRCACHE 31
|
||||
|
||||
/**
|
||||
* llcc_slice_desc - Cache slice descriptor
|
||||
* struct llcc_slice_desc - Cache slice descriptor
|
||||
* @slice_id: llcc slice id
|
||||
* @slice_size: Size allocated for the llcc slice
|
||||
*/
|
||||
|
@ -45,7 +45,7 @@ struct llcc_slice_desc {
|
|||
};
|
||||
|
||||
/**
|
||||
* llcc_edac_reg_data - llcc edac registers data for each error type
|
||||
* struct llcc_edac_reg_data - llcc edac registers data for each error type
|
||||
* @name: Name of the error
|
||||
* @synd_reg: Syndrome register address
|
||||
* @count_status_reg: Status register address to read the error count
|
||||
|
@ -69,7 +69,7 @@ struct llcc_edac_reg_data {
|
|||
};
|
||||
|
||||
/**
|
||||
* llcc_drv_data - Data associated with the llcc driver
|
||||
* struct llcc_drv_data - Data associated with the llcc driver
|
||||
* @regmap: regmap associated with the llcc device
|
||||
* @bcast_regmap: regmap associated with llcc broadcast offset
|
||||
* @cfg: pointer to the data structure for slice configuration
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
struct socket;
|
||||
|
||||
/**
|
||||
* qmi_header - wireformat header of QMI messages
|
||||
* struct qmi_header - wireformat header of QMI messages
|
||||
* @type: type of message
|
||||
* @txn_id: transaction id
|
||||
* @msg_id: message id
|
||||
|
@ -93,7 +93,7 @@ struct qmi_elem_info {
|
|||
#define QMI_ERR_NOT_SUPPORTED_V01 94
|
||||
|
||||
/**
|
||||
* qmi_response_type_v01 - common response header (decoded)
|
||||
* struct qmi_response_type_v01 - common response header (decoded)
|
||||
* @result: result of the transaction
|
||||
* @error: error value, when @result is QMI_RESULT_FAILURE_V01
|
||||
*/
|
||||
|
|
Загрузка…
Ссылка в новой задаче