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:
Arnd Bergmann 2021-04-09 21:46:14 +02:00
commit 30be8446db
13 changed files with 106 additions and 60 deletions

View File

@ -24,6 +24,13 @@ block and a BT, WiFi and FM radio block, all using SMD as command channels.
"qcom,riva", "qcom,riva",
"qcom,pronto" "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 = SUBNODES
The subnodes of the wcnss node are optional and describe the individual blocks in The subnodes of the wcnss node are optional and describe the individual blocks in
the WCNSS. the WCNSS.

View File

@ -2296,6 +2296,7 @@ F: drivers/tty/serial/msm_serial.c
F: drivers/usb/dwc3/dwc3-qcom.c F: drivers/usb/dwc3/dwc3-qcom.c
F: include/dt-bindings/*/qcom* F: include/dt-bindings/*/qcom*
F: include/linux/*/qcom* F: include/linux/*/qcom*
F: include/linux/soc/qcom/
ARM/RADISYS ENP2611 MACHINE SUPPORT ARM/RADISYS ENP2611 MACHINE SUPPORT
M: Lennert Buytenhek <kernel@wantstofly.org> M: Lennert Buytenhek <kernel@wantstofly.org>

View File

@ -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. * finish processing.
* *
* A note on cache maintenance: * A note on cache maintenance:
@ -209,7 +209,7 @@ out:
(n & 0xf)) (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 * and 3 return values
* @desc: SCM call descriptor containing arguments * @desc: SCM call descriptor containing arguments
* @res: SCM call return values * @res: SCM call return values

View File

@ -77,7 +77,9 @@ static void __scm_smc_do(const struct arm_smccc_args *smc,
} while (res->a0 == QCOM_SCM_V2_EBUSY); } while (res->a0 == QCOM_SCM_V2_EBUSY);
} }
int scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc,
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) struct qcom_scm_res *res, bool atomic)
{ {
int arglen = desc->arginfo & 0xf; int arglen = desc->arginfo & 0xf;
@ -87,8 +89,7 @@ int scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc,
size_t alloc_len; size_t alloc_len;
gfp_t flag = atomic ? GFP_ATOMIC : GFP_KERNEL; gfp_t flag = atomic ? GFP_ATOMIC : GFP_KERNEL;
u32 smccc_call_type = atomic ? ARM_SMCCC_FAST_CALL : ARM_SMCCC_STD_CALL; u32 smccc_call_type = atomic ? ARM_SMCCC_FAST_CALL : ARM_SMCCC_STD_CALL;
u32 qcom_smccc_convention = u32 qcom_smccc_convention = (qcom_convention == SMC_CONVENTION_ARM_32) ?
(qcom_scm_convention == SMC_CONVENTION_ARM_32) ?
ARM_SMCCC_SMC_32 : ARM_SMCCC_SMC_64; ARM_SMCCC_SMC_32 : ARM_SMCCC_SMC_64;
struct arm_smccc_res smc_res; struct arm_smccc_res smc_res;
struct arm_smccc_args smc = {0}; 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; return (long)smc_res.a0 ? qcom_scm_remap_error(smc_res.a0) : 0;
} }

View File

@ -113,14 +113,10 @@ static void qcom_scm_clk_disable(void)
clk_disable_unprepare(__scm->bus_clk); clk_disable_unprepare(__scm->bus_clk);
} }
static int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, enum qcom_scm_convention qcom_scm_convention = SMC_CONVENTION_UNKNOWN;
u32 cmd_id); static DEFINE_SPINLOCK(scm_query_lock);
enum qcom_scm_convention qcom_scm_convention; static enum qcom_scm_convention __get_convention(void)
static bool has_queried __read_mostly;
static DEFINE_SPINLOCK(query_lock);
static void __query_convention(void)
{ {
unsigned long flags; unsigned long flags;
struct qcom_scm_desc desc = { struct qcom_scm_desc desc = {
@ -133,36 +129,50 @@ static void __query_convention(void)
.owner = ARM_SMCCC_OWNER_SIP, .owner = ARM_SMCCC_OWNER_SIP,
}; };
struct qcom_scm_res res; struct qcom_scm_res res;
enum qcom_scm_convention probed_convention;
int ret; int ret;
bool forced = false;
spin_lock_irqsave(&query_lock, flags); if (likely(qcom_scm_convention != SMC_CONVENTION_UNKNOWN))
if (has_queried) return qcom_scm_convention;
goto out;
qcom_scm_convention = SMC_CONVENTION_ARM_64; /*
// Device isn't required as there is only one argument - no device * Device isn't required as there is only one argument - no device
// needed to dma_map_single to secure world * needed to dma_map_single to secure world
ret = scm_smc_call(NULL, &desc, &res, true); */
probed_convention = SMC_CONVENTION_ARM_64;
ret = __scm_smc_call(NULL, &desc, probed_convention, &res, true);
if (!ret && res.result[0] == 1) 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
if (!ret && res.result[0] == 1) * QCOM_SCM_INFO_IS_CALL_AVAIL call, so we fallback to forcing ARM_64
goto out; * calling conventions on these firmwares. Luckily we don't make any
* early calls into the firmware on these SoCs so the device pointer
qcom_scm_convention = SMC_CONVENTION_LEGACY; * will be valid here to check if the compatible matches.
out: */
has_queried = true; if (of_device_is_compatible(__scm ? __scm->dev->of_node : NULL, "qcom,scm-sc7180")) {
spin_unlock_irqrestore(&query_lock, flags); forced = true;
pr_info("qcom_scm: convention: %s\n", goto found;
qcom_scm_convention_names[qcom_scm_convention]);
} }
static inline enum qcom_scm_convention __get_convention(void) probed_convention = SMC_CONVENTION_ARM_32;
{ ret = __scm_smc_call(NULL, &desc, probed_convention, &res, true);
if (unlikely(!has_queried)) if (!ret && res.result[0] == 1)
__query_convention(); goto found;
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);
return qcom_scm_convention; return qcom_scm_convention;
} }
@ -219,7 +229,7 @@ static int qcom_scm_call_atomic(struct device *dev,
} }
} }
static int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, static bool __qcom_scm_is_call_available(struct device *dev, u32 svc_id,
u32 cmd_id) u32 cmd_id)
{ {
int ret; int ret;
@ -247,7 +257,7 @@ static int __qcom_scm_is_call_available(struct device *dev, u32 svc_id,
ret = qcom_scm_call(dev, &desc, &res); 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; struct qcom_scm_res res;
ret = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL, if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL,
QCOM_SCM_PIL_PAS_IS_SUPPORTED); QCOM_SCM_PIL_PAS_IS_SUPPORTED))
if (ret <= 0)
return false; return false;
ret = qcom_scm_call(__scm->dev, &desc, &res); 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 qcom_scm_hdcp_available(void)
{ {
bool avail;
int ret = qcom_scm_clk_enable(); int ret = qcom_scm_clk_enable();
if (ret) if (ret)
return 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_HDCP_INVOKE);
qcom_scm_clk_disable(); qcom_scm_clk_disable();
return ret > 0; return avail;
} }
EXPORT_SYMBOL(qcom_scm_hdcp_available); EXPORT_SYMBOL(qcom_scm_hdcp_available);
@ -1242,7 +1252,7 @@ static int qcom_scm_probe(struct platform_device *pdev)
__scm = scm; __scm = scm;
__scm->dev = &pdev->dev; __scm->dev = &pdev->dev;
__query_convention(); __get_convention();
/* /*
* If requested enable "download mode", from this point on warmboot * If requested enable "download mode", from this point on warmboot
@ -1291,6 +1301,7 @@ static struct platform_driver qcom_scm_driver = {
.driver = { .driver = {
.name = "qcom_scm", .name = "qcom_scm",
.of_match_table = qcom_scm_dt_match, .of_match_table = qcom_scm_dt_match,
.suppress_bind_attrs = true,
}, },
.probe = qcom_scm_probe, .probe = qcom_scm_probe,
.shutdown = qcom_scm_shutdown, .shutdown = qcom_scm_shutdown,

View File

@ -61,8 +61,11 @@ struct qcom_scm_res {
}; };
#define SCM_SMC_FNID(s, c) ((((s) & 0xFF) << 8) | ((c) & 0xFF)) #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, 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); 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)) #define SCM_LEGACY_FNID(s, c) (((s) << 10) | ((c) & 0x3ff))
extern int scm_legacy_call_atomic(struct device *dev, extern int scm_legacy_call_atomic(struct device *dev,

View File

@ -230,6 +230,14 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw,
break; 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; ptr = mem_region + offset;
if (phdr->p_filesz && phdr->p_offset < fw->size) { 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; 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); release_firmware(seg_fw);
} }

View File

@ -153,7 +153,7 @@ static int pdr_register_listener(struct pdr_handle *pdr,
if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
pr_err("PDR: %s register listener failed: 0x%x\n", pr_err("PDR: %s register listener failed: 0x%x\n",
pds->service_path, resp.resp.error); pds->service_path, resp.resp.error);
return ret; return -EREMOTEIO;
} }
pds->state = resp.curr_state; pds->state = resp.curr_state;

View File

@ -199,6 +199,8 @@ static int wcnss_download_nv(struct wcnss_ctrl *wcnss, bool *expect_cbc)
{ {
struct wcnss_download_nv_req *req; struct wcnss_download_nv_req *req;
const struct firmware *fw; const struct firmware *fw;
struct device *dev = wcnss->dev;
const char *nvbin = NVBIN_FILE;
const void *data; const void *data;
ssize_t left; ssize_t left;
int ret; int ret;
@ -207,10 +209,13 @@ static int wcnss_download_nv(struct wcnss_ctrl *wcnss, bool *expect_cbc)
if (!req) if (!req)
return -ENOMEM; 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) { if (ret < 0) {
dev_err(wcnss->dev, "Failed to load nv file %s: %d\n", dev_err(dev, "Failed to load nv file %s: %d\n", nvbin, ret);
NVBIN_FILE, ret);
goto free_req; 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); ret = rpmsg_send(wcnss->channel, req, req->hdr.len);
if (ret < 0) { 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; 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); ret = wait_for_completion_timeout(&wcnss->ack, WCNSS_REQUEST_TIMEOUT);
if (!ret) { 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; ret = -ETIMEDOUT;
} else { } else {
*expect_cbc = wcnss->ack_status == WCNSS_ACK_COLD_BOOTING; *expect_cbc = wcnss->ack_status == WCNSS_ACK_COLD_BOOTING;

View File

@ -113,7 +113,7 @@ void apr_driver_unregister(struct apr_driver *drv);
/** /**
* module_apr_driver() - Helper macro for registering a aprbus driver * 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 * Helper macro for aprbus drivers which do not do anything special in
* module init/exit. This eliminates a lot of boilerplate. Each module * module init/exit. This eliminates a lot of boilerplate. Each module

View File

@ -7,7 +7,7 @@
#define GPIO_NO_WAKE_IRQ ~0U #define GPIO_NO_WAKE_IRQ ~0U
/** /*
* QCOM specific IRQ domain flags that distinguishes the handling of wakeup * QCOM specific IRQ domain flags that distinguishes the handling of wakeup
* capable interrupts by different interrupt controllers. * capable interrupts by different interrupt controllers.
* *

View File

@ -35,7 +35,7 @@
#define LLCC_WRCACHE 31 #define LLCC_WRCACHE 31
/** /**
* llcc_slice_desc - Cache slice descriptor * struct llcc_slice_desc - Cache slice descriptor
* @slice_id: llcc slice id * @slice_id: llcc slice id
* @slice_size: Size allocated for the llcc slice * @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 * @name: Name of the error
* @synd_reg: Syndrome register address * @synd_reg: Syndrome register address
* @count_status_reg: Status register address to read the error count * @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 * @regmap: regmap associated with the llcc device
* @bcast_regmap: regmap associated with llcc broadcast offset * @bcast_regmap: regmap associated with llcc broadcast offset
* @cfg: pointer to the data structure for slice configuration * @cfg: pointer to the data structure for slice configuration

View File

@ -16,7 +16,7 @@
struct socket; struct socket;
/** /**
* qmi_header - wireformat header of QMI messages * struct qmi_header - wireformat header of QMI messages
* @type: type of message * @type: type of message
* @txn_id: transaction id * @txn_id: transaction id
* @msg_id: message id * @msg_id: message id
@ -93,7 +93,7 @@ struct qmi_elem_info {
#define QMI_ERR_NOT_SUPPORTED_V01 94 #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 * @result: result of the transaction
* @error: error value, when @result is QMI_RESULT_FAILURE_V01 * @error: error value, when @result is QMI_RESULT_FAILURE_V01
*/ */