mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 04:18:39 +08:00
SoC driver updates for 6.7
The highlights for the driver support this time are - Qualcomm platforms gain support for the Qualcomm Secure Execution Environment firmware interface to access EFI variables on certain devices, and new features for multiple platform and firmware drivers. - Arm FF-A firmware support gains support for v1.1 specification features, in particular notification and memory transaction descriptor changes. - SCMI firmware support now support v3.2 features for clock and DVFS configuration and a new transport for Qualcomm platforms. - Minor cleanups and bugfixes are added to pretty much all the active platforms: qualcomm, broadcom, dove, ti-k3, rockchip, sifive, amlogic, atmel, tegra, aspeed, vexpress, mediatek, samsung and more. In particular, this contains portions of the treewide conversion to use __counted_by annotations and the device_get_match_data helper. -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEiK/NIGsWEZVxh/FrYKtH/8kJUicFAmVC10IACgkQYKtH/8kJ UifFoQ//Tw7aux88EA2UkyL2Wulv80NwRQn3tQlxI/6ltjBX64yeQ6Y8OzmYdSYK 20NEpbU7VWOFftN+D6Jp1HLrvfi0OV9uJn3WiTX3ChgDXixpOXo4TYgNNTlb9uZ4 MrSTG3NkS27m/oTaCmYprOObgSNLq1FRCGIP7w4U9gyMk9N9FSKMpSJjlH06qPz6 WBLTaIwPgBsyrLfCdxfA1y7AFCAHVxQJO4bp0VWSIalTrneGTeQrd2FgYMUesQ2e fIUNCaU4mpmj8XnQ/W19Wsek8FRB+fOh0hn/Gl+iHYibpxusIsn7bkdZ5BOJn2J0 OY3C1biopaaxXcZ+wmnX9X0ieZ3TDsHzYOEf0zmNGzMZaZkV8kQt4/Ykv77xz6Gc 4Bl6JI5QZ4rTZvlHYGMYxhy3hKuB31mO2rHbei7eR7J7UmjzWcl5P6HYfCgj7wzH crIWj1IR1Nx6Dt/wXf3HlRcEiAEJ2D0M3KIFjAVT239TsxacBfDrRk+YedF2bKbn WMYfVM6jJnPOykGg/gMRlttS/o/7TqHBl3y/900Idiijcm3cRPbQ+uKfkpHXftN/ 2vOtsw7pzEg7QQI9GVrb4drTrLvYJ7GQOi4o0twXTCshlXUk2V684jvHt0emFkdX ew9Zft4YLAYSmuJ3XqGhhMP63FsHKMlB1aSTKKPeswdIJmrdO80= =QIut -----END PGP SIGNATURE----- Merge tag 'soc-drivers-6.7' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc Pull SoC driver updates from Arnd Bergmann: "The highlights for the driver support this time are - Qualcomm platforms gain support for the Qualcomm Secure Execution Environment firmware interface to access EFI variables on certain devices, and new features for multiple platform and firmware drivers. - Arm FF-A firmware support gains support for v1.1 specification features, in particular notification and memory transaction descriptor changes. - SCMI firmware support now support v3.2 features for clock and DVFS configuration and a new transport for Qualcomm platforms. - Minor cleanups and bugfixes are added to pretty much all the active platforms: qualcomm, broadcom, dove, ti-k3, rockchip, sifive, amlogic, atmel, tegra, aspeed, vexpress, mediatek, samsung and more. In particular, this contains portions of the treewide conversion to use __counted_by annotations and the device_get_match_data helper" * tag 'soc-drivers-6.7' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (156 commits) soc: qcom: pmic_glink_altmode: Print return value on error firmware: qcom: scm: remove unneeded 'extern' specifiers firmware: qcom: scm: add a missing forward declaration for struct device firmware: qcom: move Qualcomm code into its own directory soc: samsung: exynos-chipid: Convert to platform remove callback returning void soc: qcom: apr: Add __counted_by for struct apr_rx_buf and use struct_size() soc: qcom: pmic_glink: fix connector type to be DisplayPort soc: ti: k3-socinfo: Avoid overriding return value soc: ti: k3-socinfo: Fix typo in bitfield documentation soc: ti: knav_qmss_queue: Use device_get_match_data() firmware: ti_sci: Use device_get_match_data() firmware: qcom: qseecom: add missing include guards soc/pxa: ssp: Convert to platform remove callback returning void soc/mediatek: mtk-mmsys: Convert to platform remove callback returning void soc/mediatek: mtk-devapc: Convert to platform remove callback returning void soc/loongson: loongson2_guts: Convert to platform remove callback returning void soc/litex: litex_soc_ctrl: Convert to platform remove callback returning void soc/ixp4xx: ixp4xx-qmgr: Convert to platform remove callback returning void soc/ixp4xx: ixp4xx-npe: Convert to platform remove callback returning void soc/hisilicon: kunpeng_hccs: Convert to platform remove callback returning void ...
This commit is contained in:
commit
385903a7ec
@ -309,7 +309,9 @@ properties:
|
||||
power-domains property.
|
||||
|
||||
For PSCI based platforms, the name corresponding to the index of the PSCI
|
||||
PM domain provider, must be "psci".
|
||||
PM domain provider, must be "psci". For SCMI based platforms, the name
|
||||
corresponding to the index of an SCMI performance domain provider, must be
|
||||
"perf".
|
||||
|
||||
qcom,saw:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
|
@ -20,6 +20,7 @@ description: |
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,qdu1000-llcc
|
||||
- qcom,sc7180-llcc
|
||||
- qcom,sc7280-llcc
|
||||
- qcom,sc8180x-llcc
|
||||
@ -44,6 +45,14 @@ properties:
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
nvmem-cells:
|
||||
items:
|
||||
- description: Reference to an nvmem node for multi channel DDR
|
||||
|
||||
nvmem-cell-names:
|
||||
items:
|
||||
- const: multi-chan-ddr
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
@ -92,6 +101,7 @@ allOf:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,qdu1000-llcc
|
||||
- qcom,sc8180x-llcc
|
||||
- qcom,sc8280xp-llcc
|
||||
then:
|
||||
|
@ -38,6 +38,9 @@ properties:
|
||||
with shmem address(4KB-page, offset) as parameters
|
||||
items:
|
||||
- const: arm,scmi-smc-param
|
||||
- description: SCMI compliant firmware with Qualcomm SMC/HVC transport
|
||||
items:
|
||||
- const: qcom,scmi-smc
|
||||
- description: SCMI compliant firmware with SCMI Virtio transport.
|
||||
The virtio transport only supports a single device.
|
||||
items:
|
||||
@ -149,8 +152,15 @@ properties:
|
||||
'#clock-cells':
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- '#clock-cells'
|
||||
'#power-domain-cells':
|
||||
const: 1
|
||||
|
||||
oneOf:
|
||||
- required:
|
||||
- '#clock-cells'
|
||||
|
||||
- required:
|
||||
- '#power-domain-cells'
|
||||
|
||||
protocol@14:
|
||||
$ref: '#/$defs/protocol-node'
|
||||
@ -306,6 +316,7 @@ else:
|
||||
enum:
|
||||
- arm,scmi-smc
|
||||
- arm,scmi-smc-param
|
||||
- qcom,scmi-smc
|
||||
then:
|
||||
required:
|
||||
- arm,smc-id
|
||||
|
@ -24,6 +24,7 @@ properties:
|
||||
- qcom,scm-apq8064
|
||||
- qcom,scm-apq8084
|
||||
- qcom,scm-ipq4019
|
||||
- qcom,scm-ipq5018
|
||||
- qcom,scm-ipq5332
|
||||
- qcom,scm-ipq6018
|
||||
- qcom,scm-ipq806x
|
||||
@ -56,6 +57,7 @@ properties:
|
||||
- qcom,scm-sm6125
|
||||
- qcom,scm-sm6350
|
||||
- qcom,scm-sm6375
|
||||
- qcom,scm-sm7150
|
||||
- qcom,scm-sm8150
|
||||
- qcom,scm-sm8250
|
||||
- qcom,scm-sm8350
|
||||
@ -89,6 +91,14 @@ properties:
|
||||
protocol to handle sleeping SCM calls.
|
||||
maxItems: 1
|
||||
|
||||
qcom,sdi-enabled:
|
||||
description:
|
||||
Indicates that the SDI (Secure Debug Image) has been enabled by TZ
|
||||
by default and it needs to be disabled.
|
||||
If not disabled WDT assertion or reboot will cause the board to hang
|
||||
in the debug mode.
|
||||
type: boolean
|
||||
|
||||
qcom,dload-mode:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
items:
|
||||
|
@ -40,6 +40,7 @@ patternProperties:
|
||||
".*@[0-9]+$":
|
||||
type: object
|
||||
$ref: mc-peripheral-props.yaml#
|
||||
additionalProperties: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
@ -80,6 +80,8 @@ properties:
|
||||
patternProperties:
|
||||
"flash@[0-9a-f]+$":
|
||||
type: object
|
||||
additionalProperties: true
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
|
@ -130,7 +130,7 @@ patternProperties:
|
||||
bus. The device can be a NAND chip, SRAM device, NOR device
|
||||
or an ASIC.
|
||||
$ref: ti,gpmc-child.yaml
|
||||
|
||||
additionalProperties: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
@ -13,8 +13,9 @@ maintainers:
|
||||
|
||||
description: |+
|
||||
System on chip designs are often divided into multiple PM domains that can be
|
||||
used for power gating of selected IP blocks for power saving by reduced leakage
|
||||
current.
|
||||
used for power gating of selected IP blocks for power saving by reduced
|
||||
leakage current. Moreover, in some cases the similar PM domains may also be
|
||||
capable of scaling performance for a group of IP blocks.
|
||||
|
||||
This device tree binding can be used to bind PM domain consumer devices with
|
||||
their PM domains provided by PM domain providers. A PM domain provider can be
|
||||
@ -25,7 +26,7 @@ description: |+
|
||||
|
||||
properties:
|
||||
$nodename:
|
||||
pattern: "^(power-controller|power-domain)([@-].*)?$"
|
||||
pattern: "^(power-controller|power-domain|performance-domain)([@-].*)?$"
|
||||
|
||||
domain-idle-states:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
@ -44,11 +45,11 @@ properties:
|
||||
|
||||
operating-points-v2:
|
||||
description:
|
||||
Phandles to the OPP tables of power domains provided by a power domain
|
||||
provider. If the provider provides a single power domain only or all
|
||||
the power domains provided by the provider have identical OPP tables,
|
||||
then this shall contain a single phandle. Refer to ../opp/opp-v2-base.yaml
|
||||
for more information.
|
||||
Phandles to the OPP tables of power domains that are capable of scaling
|
||||
performance, provided by a power domain provider. If the provider provides
|
||||
a single power domain only or all the power domains provided by the
|
||||
provider have identical OPP tables, then this shall contain a single
|
||||
phandle. Refer to ../opp/opp-v2-base.yaml for more information.
|
||||
|
||||
"#power-domain-cells":
|
||||
description:
|
||||
|
@ -26,6 +26,17 @@ properties:
|
||||
description: >
|
||||
identifier of the client to use this region for buffers
|
||||
|
||||
qcom,use-guard-pages:
|
||||
type: boolean
|
||||
description: >
|
||||
Indicates that the firmware, or hardware, does not gracefully handle
|
||||
memory protection of this region when placed adjacent to other protected
|
||||
memory regions, and that padding around the used portion of the memory
|
||||
region is necessary.
|
||||
|
||||
When this is set, the first and last page should be left unused, and the
|
||||
effective size of the region will thereby shrink with two pages.
|
||||
|
||||
qcom,vmid:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
description: >
|
||||
|
@ -22,6 +22,7 @@ properties:
|
||||
compatible:
|
||||
enum:
|
||||
- mediatek,mt8183-svs
|
||||
- mediatek,mt8188-svs
|
||||
- mediatek,mt8192-svs
|
||||
|
||||
reg:
|
||||
|
@ -52,6 +52,8 @@ properties:
|
||||
iommus:
|
||||
maxItems: 1
|
||||
|
||||
dma-coherent: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
13
MAINTAINERS
13
MAINTAINERS
@ -17894,6 +17894,18 @@ S: Maintained
|
||||
F: Documentation/devicetree/bindings/mtd/qcom,nandc.yaml
|
||||
F: drivers/mtd/nand/raw/qcom_nandc.c
|
||||
|
||||
QUALCOMM QSEECOM DRIVER
|
||||
M: Maximilian Luz <luzmaximilian@gmail.com>
|
||||
L: linux-arm-msm@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/firmware/qcom/qcom_qseecom.c
|
||||
|
||||
QUALCOMM QSEECOM UEFISECAPP DRIVER
|
||||
M: Maximilian Luz <luzmaximilian@gmail.com>
|
||||
L: linux-arm-msm@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/firmware/qcom/qcom_qseecom_uefisecapp.c
|
||||
|
||||
QUALCOMM RMNET DRIVER
|
||||
M: Subash Abhinov Kasiviswanathan <quic_subashab@quicinc.com>
|
||||
M: Sean Tranchetti <quic_stranche@quicinc.com>
|
||||
@ -21003,6 +21015,7 @@ F: drivers/clk/clk-sc[mp]i.c
|
||||
F: drivers/cpufreq/sc[mp]i-cpufreq.c
|
||||
F: drivers/firmware/arm_scmi/
|
||||
F: drivers/firmware/arm_scpi.c
|
||||
F: drivers/pmdomain/arm/
|
||||
F: drivers/powercap/arm_scmi_powercap.c
|
||||
F: drivers/regulator/scmi-regulator.c
|
||||
F: drivers/reset/reset-scmi.c
|
||||
|
@ -423,6 +423,7 @@ static __always_inline void do_ffa_mem_xfer(const u64 func_id,
|
||||
DECLARE_REG(u32, fraglen, ctxt, 2);
|
||||
DECLARE_REG(u64, addr_mbz, ctxt, 3);
|
||||
DECLARE_REG(u32, npages_mbz, ctxt, 4);
|
||||
struct ffa_mem_region_attributes *ep_mem_access;
|
||||
struct ffa_composite_mem_region *reg;
|
||||
struct ffa_mem_region *buf;
|
||||
u32 offset, nr_ranges;
|
||||
@ -452,7 +453,9 @@ static __always_inline void do_ffa_mem_xfer(const u64 func_id,
|
||||
buf = hyp_buffers.tx;
|
||||
memcpy(buf, host_buffers.tx, fraglen);
|
||||
|
||||
offset = buf->ep_mem_access[0].composite_off;
|
||||
ep_mem_access = (void *)buf +
|
||||
ffa_mem_desc_offset(buf, 0, FFA_VERSION_1_0);
|
||||
offset = ep_mem_access->composite_off;
|
||||
if (!offset || buf->ep_count != 1 || buf->sender_id != HOST_FFA_ID) {
|
||||
ret = FFA_RET_INVALID_PARAMETERS;
|
||||
goto out_unlock;
|
||||
@ -504,6 +507,7 @@ static void do_ffa_mem_reclaim(struct arm_smccc_res *res,
|
||||
DECLARE_REG(u32, handle_lo, ctxt, 1);
|
||||
DECLARE_REG(u32, handle_hi, ctxt, 2);
|
||||
DECLARE_REG(u32, flags, ctxt, 3);
|
||||
struct ffa_mem_region_attributes *ep_mem_access;
|
||||
struct ffa_composite_mem_region *reg;
|
||||
u32 offset, len, fraglen, fragoff;
|
||||
struct ffa_mem_region *buf;
|
||||
@ -528,7 +532,9 @@ static void do_ffa_mem_reclaim(struct arm_smccc_res *res,
|
||||
len = res->a1;
|
||||
fraglen = res->a2;
|
||||
|
||||
offset = buf->ep_mem_access[0].composite_off;
|
||||
ep_mem_access = (void *)buf +
|
||||
ffa_mem_desc_offset(buf, 0, FFA_VERSION_1_0);
|
||||
offset = ep_mem_access->composite_off;
|
||||
/*
|
||||
* We can trust the SPMD to get this right, but let's at least
|
||||
* check that we end up with something that doesn't look _completely_
|
||||
|
@ -34,6 +34,7 @@ config SOC_STARFIVE
|
||||
bool "StarFive SoCs"
|
||||
select PINCTRL
|
||||
select RESET_CONTROLLER
|
||||
select ARM_AMBA
|
||||
help
|
||||
This enables support for StarFive SoC platform hardware.
|
||||
|
||||
|
@ -130,6 +130,7 @@ static const struct genpd_lock_ops genpd_spin_ops = {
|
||||
#define genpd_is_active_wakeup(genpd) (genpd->flags & GENPD_FLAG_ACTIVE_WAKEUP)
|
||||
#define genpd_is_cpu_domain(genpd) (genpd->flags & GENPD_FLAG_CPU_DOMAIN)
|
||||
#define genpd_is_rpm_always_on(genpd) (genpd->flags & GENPD_FLAG_RPM_ALWAYS_ON)
|
||||
#define genpd_is_opp_table_fw(genpd) (genpd->flags & GENPD_FLAG_OPP_TABLE_FW)
|
||||
|
||||
static inline bool irq_safe_dev_in_sleep_domain(struct device *dev,
|
||||
const struct generic_pm_domain *genpd)
|
||||
@ -2337,7 +2338,7 @@ int of_genpd_add_provider_simple(struct device_node *np,
|
||||
genpd->dev.of_node = np;
|
||||
|
||||
/* Parse genpd OPP table */
|
||||
if (genpd->set_performance_state) {
|
||||
if (!genpd_is_opp_table_fw(genpd) && genpd->set_performance_state) {
|
||||
ret = dev_pm_opp_of_add_table(&genpd->dev);
|
||||
if (ret)
|
||||
return dev_err_probe(&genpd->dev, ret, "Failed to add OPP table\n");
|
||||
@ -2352,7 +2353,7 @@ int of_genpd_add_provider_simple(struct device_node *np,
|
||||
|
||||
ret = genpd_add_provider(np, genpd_xlate_simple, genpd);
|
||||
if (ret) {
|
||||
if (genpd->set_performance_state) {
|
||||
if (!genpd_is_opp_table_fw(genpd) && genpd->set_performance_state) {
|
||||
dev_pm_opp_put_opp_table(genpd->opp_table);
|
||||
dev_pm_opp_of_remove_table(&genpd->dev);
|
||||
}
|
||||
@ -2396,7 +2397,7 @@ int of_genpd_add_provider_onecell(struct device_node *np,
|
||||
genpd->dev.of_node = np;
|
||||
|
||||
/* Parse genpd OPP table */
|
||||
if (genpd->set_performance_state) {
|
||||
if (!genpd_is_opp_table_fw(genpd) && genpd->set_performance_state) {
|
||||
ret = dev_pm_opp_of_add_table_indexed(&genpd->dev, i);
|
||||
if (ret) {
|
||||
dev_err_probe(&genpd->dev, ret,
|
||||
@ -2432,7 +2433,7 @@ error:
|
||||
genpd->provider = NULL;
|
||||
genpd->has_provider = false;
|
||||
|
||||
if (genpd->set_performance_state) {
|
||||
if (!genpd_is_opp_table_fw(genpd) && genpd->set_performance_state) {
|
||||
dev_pm_opp_put_opp_table(genpd->opp_table);
|
||||
dev_pm_opp_of_remove_table(&genpd->dev);
|
||||
}
|
||||
@ -2464,7 +2465,7 @@ void of_genpd_del_provider(struct device_node *np)
|
||||
if (gpd->provider == &np->fwnode) {
|
||||
gpd->has_provider = false;
|
||||
|
||||
if (!gpd->set_performance_state)
|
||||
if (genpd_is_opp_table_fw(gpd) || !gpd->set_performance_state)
|
||||
continue;
|
||||
|
||||
dev_pm_opp_put_opp_table(gpd->opp_table);
|
||||
|
@ -31,7 +31,7 @@ config ARM_INTEGRATOR_LM
|
||||
|
||||
config BRCMSTB_GISB_ARB
|
||||
tristate "Broadcom STB GISB bus arbiter"
|
||||
depends on ARM || ARM64 || MIPS
|
||||
depends on ARCH_BRCMSTB || BMIPS_GENERIC
|
||||
default ARCH_BRCMSTB || BMIPS_GENERIC
|
||||
help
|
||||
Driver for the Broadcom Set Top Box System-on-a-chip internal bus
|
||||
|
@ -54,7 +54,7 @@ struct vexpress_syscfg_func {
|
||||
struct vexpress_syscfg *syscfg;
|
||||
struct regmap *regmap;
|
||||
int num_templates;
|
||||
u32 template[]; /* Keep it last! */
|
||||
u32 template[] __counted_by(num_templates); /* Keep it last! */
|
||||
};
|
||||
|
||||
struct vexpress_config_bridge_ops {
|
||||
|
@ -13,13 +13,18 @@
|
||||
#include <linux/scmi_protocol.h>
|
||||
#include <asm/div64.h>
|
||||
|
||||
#define NOT_ATOMIC false
|
||||
#define ATOMIC true
|
||||
|
||||
static const struct scmi_clk_proto_ops *scmi_proto_clk_ops;
|
||||
|
||||
struct scmi_clk {
|
||||
u32 id;
|
||||
struct device *dev;
|
||||
struct clk_hw hw;
|
||||
const struct scmi_clock_info *info;
|
||||
const struct scmi_protocol_handle *ph;
|
||||
struct clk_parent_data *parent_data;
|
||||
};
|
||||
|
||||
#define to_scmi_clk(clk) container_of(clk, struct scmi_clk, hw)
|
||||
@ -74,38 +79,89 @@ static int scmi_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
return scmi_proto_clk_ops->rate_set(clk->ph, clk->id, rate);
|
||||
}
|
||||
|
||||
static int scmi_clk_set_parent(struct clk_hw *hw, u8 parent_index)
|
||||
{
|
||||
struct scmi_clk *clk = to_scmi_clk(hw);
|
||||
|
||||
return scmi_proto_clk_ops->parent_set(clk->ph, clk->id, parent_index);
|
||||
}
|
||||
|
||||
static u8 scmi_clk_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct scmi_clk *clk = to_scmi_clk(hw);
|
||||
u32 parent_id, p_idx;
|
||||
int ret;
|
||||
|
||||
ret = scmi_proto_clk_ops->parent_get(clk->ph, clk->id, &parent_id);
|
||||
if (ret)
|
||||
return 0;
|
||||
|
||||
for (p_idx = 0; p_idx < clk->info->num_parents; p_idx++) {
|
||||
if (clk->parent_data[p_idx].index == parent_id)
|
||||
break;
|
||||
}
|
||||
|
||||
if (p_idx == clk->info->num_parents)
|
||||
return 0;
|
||||
|
||||
return p_idx;
|
||||
}
|
||||
|
||||
static int scmi_clk_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
|
||||
{
|
||||
/*
|
||||
* Suppose all the requested rates are supported, and let firmware
|
||||
* to handle the left work.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scmi_clk_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct scmi_clk *clk = to_scmi_clk(hw);
|
||||
|
||||
return scmi_proto_clk_ops->enable(clk->ph, clk->id);
|
||||
return scmi_proto_clk_ops->enable(clk->ph, clk->id, NOT_ATOMIC);
|
||||
}
|
||||
|
||||
static void scmi_clk_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct scmi_clk *clk = to_scmi_clk(hw);
|
||||
|
||||
scmi_proto_clk_ops->disable(clk->ph, clk->id);
|
||||
scmi_proto_clk_ops->disable(clk->ph, clk->id, NOT_ATOMIC);
|
||||
}
|
||||
|
||||
static int scmi_clk_atomic_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct scmi_clk *clk = to_scmi_clk(hw);
|
||||
|
||||
return scmi_proto_clk_ops->enable_atomic(clk->ph, clk->id);
|
||||
return scmi_proto_clk_ops->enable(clk->ph, clk->id, ATOMIC);
|
||||
}
|
||||
|
||||
static void scmi_clk_atomic_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct scmi_clk *clk = to_scmi_clk(hw);
|
||||
|
||||
scmi_proto_clk_ops->disable_atomic(clk->ph, clk->id);
|
||||
scmi_proto_clk_ops->disable(clk->ph, clk->id, ATOMIC);
|
||||
}
|
||||
|
||||
static int scmi_clk_atomic_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
int ret;
|
||||
bool enabled = false;
|
||||
struct scmi_clk *clk = to_scmi_clk(hw);
|
||||
|
||||
ret = scmi_proto_clk_ops->state_get(clk->ph, clk->id, &enabled, ATOMIC);
|
||||
if (ret)
|
||||
dev_warn(clk->dev,
|
||||
"Failed to get state for clock ID %d\n", clk->id);
|
||||
|
||||
return !!enabled;
|
||||
}
|
||||
|
||||
/*
|
||||
* We can provide enable/disable atomic callbacks only if the underlying SCMI
|
||||
* transport for an SCMI instance is configured to handle SCMI commands in an
|
||||
* atomic manner.
|
||||
* We can provide enable/disable/is_enabled atomic callbacks only if the
|
||||
* underlying SCMI transport for an SCMI instance is configured to handle
|
||||
* SCMI commands in an atomic manner.
|
||||
*
|
||||
* When no SCMI atomic transport support is available we instead provide only
|
||||
* the prepare/unprepare API, as allowed by the clock framework when atomic
|
||||
@ -121,6 +177,9 @@ static const struct clk_ops scmi_clk_ops = {
|
||||
.set_rate = scmi_clk_set_rate,
|
||||
.prepare = scmi_clk_enable,
|
||||
.unprepare = scmi_clk_disable,
|
||||
.set_parent = scmi_clk_set_parent,
|
||||
.get_parent = scmi_clk_get_parent,
|
||||
.determine_rate = scmi_clk_determine_rate,
|
||||
};
|
||||
|
||||
static const struct clk_ops scmi_atomic_clk_ops = {
|
||||
@ -129,6 +188,10 @@ static const struct clk_ops scmi_atomic_clk_ops = {
|
||||
.set_rate = scmi_clk_set_rate,
|
||||
.enable = scmi_clk_atomic_enable,
|
||||
.disable = scmi_clk_atomic_disable,
|
||||
.is_enabled = scmi_clk_atomic_is_enabled,
|
||||
.set_parent = scmi_clk_set_parent,
|
||||
.get_parent = scmi_clk_get_parent,
|
||||
.determine_rate = scmi_clk_determine_rate,
|
||||
};
|
||||
|
||||
static int scmi_clk_ops_init(struct device *dev, struct scmi_clk *sclk,
|
||||
@ -139,9 +202,10 @@ static int scmi_clk_ops_init(struct device *dev, struct scmi_clk *sclk,
|
||||
|
||||
struct clk_init_data init = {
|
||||
.flags = CLK_GET_RATE_NOCACHE,
|
||||
.num_parents = 0,
|
||||
.num_parents = sclk->info->num_parents,
|
||||
.ops = scmi_ops,
|
||||
.name = sclk->info->name,
|
||||
.parent_data = sclk->parent_data,
|
||||
};
|
||||
|
||||
sclk->hw.init = &init;
|
||||
@ -213,11 +277,13 @@ static int scmi_clocks_probe(struct scmi_device *sdev)
|
||||
sclk->info = scmi_proto_clk_ops->info_get(ph, idx);
|
||||
if (!sclk->info) {
|
||||
dev_dbg(dev, "invalid clock info for idx %d\n", idx);
|
||||
devm_kfree(dev, sclk);
|
||||
continue;
|
||||
}
|
||||
|
||||
sclk->id = idx;
|
||||
sclk->ph = ph;
|
||||
sclk->dev = dev;
|
||||
|
||||
/*
|
||||
* Note that when transport is atomic but SCMI protocol did not
|
||||
@ -230,9 +296,23 @@ static int scmi_clocks_probe(struct scmi_device *sdev)
|
||||
else
|
||||
scmi_ops = &scmi_clk_ops;
|
||||
|
||||
/* Initialize clock parent data. */
|
||||
if (sclk->info->num_parents > 0) {
|
||||
sclk->parent_data = devm_kcalloc(dev, sclk->info->num_parents,
|
||||
sizeof(*sclk->parent_data), GFP_KERNEL);
|
||||
if (!sclk->parent_data)
|
||||
return -ENOMEM;
|
||||
|
||||
for (int i = 0; i < sclk->info->num_parents; i++) {
|
||||
sclk->parent_data[i].index = sclk->info->parents[i];
|
||||
sclk->parent_data[i].hw = hws[sclk->info->parents[i]];
|
||||
}
|
||||
}
|
||||
|
||||
err = scmi_clk_ops_init(dev, sclk, scmi_ops);
|
||||
if (err) {
|
||||
dev_err(dev, "failed to register clock %d\n", idx);
|
||||
devm_kfree(dev, sclk->parent_data);
|
||||
devm_kfree(dev, sclk);
|
||||
hws[idx] = NULL;
|
||||
} else {
|
||||
|
@ -70,15 +70,35 @@ static unsigned int scmi_cpufreq_fast_switch(struct cpufreq_policy *policy,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask)
|
||||
static int scmi_cpu_domain_id(struct device *cpu_dev)
|
||||
{
|
||||
int cpu, domain, tdomain;
|
||||
struct device *tcpu_dev;
|
||||
struct device_node *np = cpu_dev->of_node;
|
||||
struct of_phandle_args domain_id;
|
||||
int index;
|
||||
|
||||
domain = perf_ops->device_domain_id(cpu_dev);
|
||||
if (domain < 0)
|
||||
return domain;
|
||||
if (of_parse_phandle_with_args(np, "clocks", "#clock-cells", 0,
|
||||
&domain_id)) {
|
||||
/* Find the corresponding index for power-domain "perf". */
|
||||
index = of_property_match_string(np, "power-domain-names",
|
||||
"perf");
|
||||
if (index < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (of_parse_phandle_with_args(np, "power-domains",
|
||||
"#power-domain-cells", index,
|
||||
&domain_id))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return domain_id.args[0];
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_get_sharing_cpus(struct device *cpu_dev, int domain,
|
||||
struct cpumask *cpumask)
|
||||
{
|
||||
int cpu, tdomain;
|
||||
struct device *tcpu_dev;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
if (cpu == cpu_dev->id)
|
||||
@ -88,7 +108,7 @@ scmi_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask)
|
||||
if (!tcpu_dev)
|
||||
continue;
|
||||
|
||||
tdomain = perf_ops->device_domain_id(tcpu_dev);
|
||||
tdomain = scmi_cpu_domain_id(tcpu_dev);
|
||||
if (tdomain == domain)
|
||||
cpumask_set_cpu(cpu, cpumask);
|
||||
}
|
||||
@ -104,7 +124,7 @@ scmi_get_cpu_power(struct device *cpu_dev, unsigned long *power,
|
||||
unsigned long Hz;
|
||||
int ret, domain;
|
||||
|
||||
domain = perf_ops->device_domain_id(cpu_dev);
|
||||
domain = scmi_cpu_domain_id(cpu_dev);
|
||||
if (domain < 0)
|
||||
return domain;
|
||||
|
||||
@ -126,7 +146,7 @@ scmi_get_cpu_power(struct device *cpu_dev, unsigned long *power,
|
||||
|
||||
static int scmi_cpufreq_init(struct cpufreq_policy *policy)
|
||||
{
|
||||
int ret, nr_opp;
|
||||
int ret, nr_opp, domain;
|
||||
unsigned int latency;
|
||||
struct device *cpu_dev;
|
||||
struct scmi_data *priv;
|
||||
@ -138,6 +158,10 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
domain = scmi_cpu_domain_id(cpu_dev);
|
||||
if (domain < 0)
|
||||
return domain;
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
@ -148,7 +172,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
|
||||
}
|
||||
|
||||
/* Obtain CPUs that share SCMI performance controls */
|
||||
ret = scmi_get_sharing_cpus(cpu_dev, policy->cpus);
|
||||
ret = scmi_get_sharing_cpus(cpu_dev, domain, policy->cpus);
|
||||
if (ret) {
|
||||
dev_warn(cpu_dev, "failed to get sharing cpumask\n");
|
||||
goto out_free_cpumask;
|
||||
@ -176,7 +200,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
|
||||
*/
|
||||
nr_opp = dev_pm_opp_get_opp_count(cpu_dev);
|
||||
if (nr_opp <= 0) {
|
||||
ret = perf_ops->device_opps_add(ph, cpu_dev);
|
||||
ret = perf_ops->device_opps_add(ph, cpu_dev, domain);
|
||||
if (ret) {
|
||||
dev_warn(cpu_dev, "failed to add opps to the device\n");
|
||||
goto out_free_cpumask;
|
||||
@ -209,7 +233,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
|
||||
}
|
||||
|
||||
priv->cpu_dev = cpu_dev;
|
||||
priv->domain_id = perf_ops->device_domain_id(cpu_dev);
|
||||
priv->domain_id = domain;
|
||||
|
||||
policy->driver_data = priv;
|
||||
policy->freq_table = freq_table;
|
||||
@ -217,14 +241,14 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
|
||||
/* SCMI allows DVFS request for any domain from any CPU */
|
||||
policy->dvfs_possible_from_any_cpu = true;
|
||||
|
||||
latency = perf_ops->transition_latency_get(ph, cpu_dev);
|
||||
latency = perf_ops->transition_latency_get(ph, domain);
|
||||
if (!latency)
|
||||
latency = CPUFREQ_ETERNAL;
|
||||
|
||||
policy->cpuinfo.transition_latency = latency;
|
||||
|
||||
policy->fast_switch_possible =
|
||||
perf_ops->fast_switch_possible(ph, cpu_dev);
|
||||
perf_ops->fast_switch_possible(ph, domain);
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -212,20 +212,6 @@ config MTK_ADSP_IPC
|
||||
ADSP exists on some mtk processors.
|
||||
Client might use shared memory to exchange information with ADSP.
|
||||
|
||||
config QCOM_SCM
|
||||
tristate
|
||||
|
||||
config QCOM_SCM_DOWNLOAD_MODE_DEFAULT
|
||||
bool "Qualcomm download mode enabled by default"
|
||||
depends on QCOM_SCM
|
||||
help
|
||||
A device with "download mode" enabled will upon an unexpected
|
||||
warm-restart enter a special debug mode that allows the user to
|
||||
"download" memory content over USB for offline postmortem analysis.
|
||||
The feature can be enabled/disabled on the kernel command line.
|
||||
|
||||
Say Y here to enable "download mode" by default.
|
||||
|
||||
config SYSFB
|
||||
bool
|
||||
select BOOT_VESA_SUPPORT
|
||||
@ -311,6 +297,7 @@ source "drivers/firmware/efi/Kconfig"
|
||||
source "drivers/firmware/imx/Kconfig"
|
||||
source "drivers/firmware/meson/Kconfig"
|
||||
source "drivers/firmware/psci/Kconfig"
|
||||
source "drivers/firmware/qcom/Kconfig"
|
||||
source "drivers/firmware/smccc/Kconfig"
|
||||
source "drivers/firmware/tegra/Kconfig"
|
||||
source "drivers/firmware/xilinx/Kconfig"
|
||||
|
@ -18,8 +18,6 @@ obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o
|
||||
obj-$(CONFIG_MTK_ADSP_IPC) += mtk-adsp-ipc.o
|
||||
obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o
|
||||
obj-$(CONFIG_FW_CFG_SYSFS) += qemu_fw_cfg.o
|
||||
obj-$(CONFIG_QCOM_SCM) += qcom-scm.o
|
||||
qcom-scm-objs += qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o
|
||||
obj-$(CONFIG_SYSFB) += sysfb.o
|
||||
obj-$(CONFIG_SYSFB_SIMPLEFB) += sysfb_simplefb.o
|
||||
obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o
|
||||
@ -35,6 +33,7 @@ obj-$(CONFIG_GOOGLE_FIRMWARE) += google/
|
||||
obj-y += efi/
|
||||
obj-y += imx/
|
||||
obj-y += psci/
|
||||
obj-y += qcom/
|
||||
obj-y += smccc/
|
||||
obj-y += tegra/
|
||||
obj-y += xilinx/
|
||||
|
@ -15,6 +15,8 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define SCMI_UEVENT_MODALIAS_FMT "arm_ffa:%04x:%pUb"
|
||||
|
||||
static DEFINE_IDA(ffa_bus_id);
|
||||
|
||||
static int ffa_device_match(struct device *dev, struct device_driver *drv)
|
||||
@ -63,10 +65,20 @@ static int ffa_device_uevent(const struct device *dev, struct kobj_uevent_env *e
|
||||
{
|
||||
const struct ffa_device *ffa_dev = to_ffa_dev(dev);
|
||||
|
||||
return add_uevent_var(env, "MODALIAS=arm_ffa:%04x:%pUb",
|
||||
return add_uevent_var(env, "MODALIAS=" SCMI_UEVENT_MODALIAS_FMT,
|
||||
ffa_dev->vm_id, &ffa_dev->uuid);
|
||||
}
|
||||
|
||||
static ssize_t modalias_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ffa_device *ffa_dev = to_ffa_dev(dev);
|
||||
|
||||
return sysfs_emit(buf, SCMI_UEVENT_MODALIAS_FMT, ffa_dev->vm_id,
|
||||
&ffa_dev->uuid);
|
||||
}
|
||||
static DEVICE_ATTR_RO(modalias);
|
||||
|
||||
static ssize_t partition_id_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
@ -88,6 +100,7 @@ static DEVICE_ATTR_RO(uuid);
|
||||
static struct attribute *ffa_device_attributes_attrs[] = {
|
||||
&dev_attr_partition_id.attr,
|
||||
&dev_attr_uuid.attr,
|
||||
&dev_attr_modalias.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(ffa_device_attributes);
|
||||
@ -193,6 +206,7 @@ struct ffa_device *ffa_device_register(const uuid_t *uuid, int vm_id,
|
||||
dev->release = ffa_release_device;
|
||||
dev_set_name(&ffa_dev->dev, "arm-ffa-%d", id);
|
||||
|
||||
ffa_dev->id = id;
|
||||
ffa_dev->vm_id = vm_id;
|
||||
ffa_dev->ops = ops;
|
||||
uuid_copy(&ffa_dev->uuid, uuid);
|
||||
|
@ -22,20 +22,28 @@
|
||||
#define DRIVER_NAME "ARM FF-A"
|
||||
#define pr_fmt(fmt) DRIVER_NAME ": " fmt
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/arm_ffa.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/cpuhotplug.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/uuid.h>
|
||||
#include <linux/xarray.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define FFA_DRIVER_VERSION FFA_VERSION_1_0
|
||||
#define FFA_DRIVER_VERSION FFA_VERSION_1_1
|
||||
#define FFA_MIN_VERSION FFA_VERSION_1_0
|
||||
|
||||
#define SENDER_ID_MASK GENMASK(31, 16)
|
||||
@ -51,6 +59,8 @@
|
||||
*/
|
||||
#define RXTX_BUFFER_SIZE SZ_4K
|
||||
|
||||
#define FFA_MAX_NOTIFICATIONS 64
|
||||
|
||||
static ffa_fn *invoke_ffa_fn;
|
||||
|
||||
static const int ffa_linux_errmap[] = {
|
||||
@ -64,6 +74,7 @@ static const int ffa_linux_errmap[] = {
|
||||
-EACCES, /* FFA_RET_DENIED */
|
||||
-EAGAIN, /* FFA_RET_RETRY */
|
||||
-ECANCELED, /* FFA_RET_ABORTED */
|
||||
-ENODATA, /* FFA_RET_NO_DATA */
|
||||
};
|
||||
|
||||
static inline int ffa_to_linux_errno(int errno)
|
||||
@ -75,6 +86,10 @@ static inline int ffa_to_linux_errno(int errno)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct ffa_pcpu_irq {
|
||||
struct ffa_drv_info *info;
|
||||
};
|
||||
|
||||
struct ffa_drv_info {
|
||||
u32 version;
|
||||
u16 vm_id;
|
||||
@ -83,6 +98,17 @@ struct ffa_drv_info {
|
||||
void *rx_buffer;
|
||||
void *tx_buffer;
|
||||
bool mem_ops_native;
|
||||
bool bitmap_created;
|
||||
unsigned int sched_recv_irq;
|
||||
unsigned int cpuhp_state;
|
||||
struct ffa_pcpu_irq __percpu *irq_pcpu;
|
||||
struct workqueue_struct *notif_pcpu_wq;
|
||||
struct work_struct notif_pcpu_work;
|
||||
struct work_struct irq_work;
|
||||
struct xarray partition_info;
|
||||
unsigned int partition_count;
|
||||
DECLARE_HASHTABLE(notifier_hash, ilog2(FFA_MAX_NOTIFICATIONS));
|
||||
struct mutex notify_lock; /* lock to protect notifier hashtable */
|
||||
};
|
||||
|
||||
static struct ffa_drv_info *drv_info;
|
||||
@ -397,7 +423,7 @@ static u32 ffa_get_num_pages_sg(struct scatterlist *sg)
|
||||
return num_pages;
|
||||
}
|
||||
|
||||
static u8 ffa_memory_attributes_get(u32 func_id)
|
||||
static u16 ffa_memory_attributes_get(u32 func_id)
|
||||
{
|
||||
/*
|
||||
* For the memory lend or donate operation, if the receiver is a PE or
|
||||
@ -416,38 +442,47 @@ ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize,
|
||||
{
|
||||
int rc = 0;
|
||||
bool first = true;
|
||||
u32 composite_offset;
|
||||
phys_addr_t addr = 0;
|
||||
struct ffa_mem_region *mem_region = buffer;
|
||||
struct ffa_composite_mem_region *composite;
|
||||
struct ffa_mem_region_addr_range *constituents;
|
||||
struct ffa_mem_region_attributes *ep_mem_access;
|
||||
struct ffa_mem_region *mem_region = buffer;
|
||||
u32 idx, frag_len, length, buf_sz = 0, num_entries = sg_nents(args->sg);
|
||||
|
||||
mem_region->tag = args->tag;
|
||||
mem_region->flags = args->flags;
|
||||
mem_region->sender_id = drv_info->vm_id;
|
||||
mem_region->attributes = ffa_memory_attributes_get(func_id);
|
||||
ep_mem_access = &mem_region->ep_mem_access[0];
|
||||
ep_mem_access = buffer +
|
||||
ffa_mem_desc_offset(buffer, 0, drv_info->version);
|
||||
composite_offset = ffa_mem_desc_offset(buffer, args->nattrs,
|
||||
drv_info->version);
|
||||
|
||||
for (idx = 0; idx < args->nattrs; idx++, ep_mem_access++) {
|
||||
ep_mem_access->receiver = args->attrs[idx].receiver;
|
||||
ep_mem_access->attrs = args->attrs[idx].attrs;
|
||||
ep_mem_access->composite_off = COMPOSITE_OFFSET(args->nattrs);
|
||||
ep_mem_access->composite_off = composite_offset;
|
||||
ep_mem_access->flag = 0;
|
||||
ep_mem_access->reserved = 0;
|
||||
}
|
||||
mem_region->handle = 0;
|
||||
mem_region->reserved_0 = 0;
|
||||
mem_region->reserved_1 = 0;
|
||||
mem_region->ep_count = args->nattrs;
|
||||
if (drv_info->version <= FFA_VERSION_1_0) {
|
||||
mem_region->ep_mem_size = 0;
|
||||
} else {
|
||||
mem_region->ep_mem_size = sizeof(*ep_mem_access);
|
||||
mem_region->ep_mem_offset = sizeof(*mem_region);
|
||||
memset(mem_region->reserved, 0, 12);
|
||||
}
|
||||
|
||||
composite = buffer + COMPOSITE_OFFSET(args->nattrs);
|
||||
composite = buffer + composite_offset;
|
||||
composite->total_pg_cnt = ffa_get_num_pages_sg(args->sg);
|
||||
composite->addr_range_cnt = num_entries;
|
||||
composite->reserved = 0;
|
||||
|
||||
length = COMPOSITE_CONSTITUENTS_OFFSET(args->nattrs, num_entries);
|
||||
frag_len = COMPOSITE_CONSTITUENTS_OFFSET(args->nattrs, 0);
|
||||
length = composite_offset + CONSTITUENTS_OFFSET(num_entries);
|
||||
frag_len = composite_offset + CONSTITUENTS_OFFSET(0);
|
||||
if (frag_len > max_fragsize)
|
||||
return -ENXIO;
|
||||
|
||||
@ -554,6 +589,236 @@ static int ffa_features(u32 func_feat_id, u32 input_props,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ffa_notification_bitmap_create(void)
|
||||
{
|
||||
ffa_value_t ret;
|
||||
u16 vcpu_count = nr_cpu_ids;
|
||||
|
||||
invoke_ffa_fn((ffa_value_t){
|
||||
.a0 = FFA_NOTIFICATION_BITMAP_CREATE,
|
||||
.a1 = drv_info->vm_id, .a2 = vcpu_count,
|
||||
}, &ret);
|
||||
|
||||
if (ret.a0 == FFA_ERROR)
|
||||
return ffa_to_linux_errno((int)ret.a2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ffa_notification_bitmap_destroy(void)
|
||||
{
|
||||
ffa_value_t ret;
|
||||
|
||||
invoke_ffa_fn((ffa_value_t){
|
||||
.a0 = FFA_NOTIFICATION_BITMAP_DESTROY,
|
||||
.a1 = drv_info->vm_id,
|
||||
}, &ret);
|
||||
|
||||
if (ret.a0 == FFA_ERROR)
|
||||
return ffa_to_linux_errno((int)ret.a2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define NOTIFICATION_LOW_MASK GENMASK(31, 0)
|
||||
#define NOTIFICATION_HIGH_MASK GENMASK(63, 32)
|
||||
#define NOTIFICATION_BITMAP_HIGH(x) \
|
||||
((u32)(FIELD_GET(NOTIFICATION_HIGH_MASK, (x))))
|
||||
#define NOTIFICATION_BITMAP_LOW(x) \
|
||||
((u32)(FIELD_GET(NOTIFICATION_LOW_MASK, (x))))
|
||||
#define PACK_NOTIFICATION_BITMAP(low, high) \
|
||||
(FIELD_PREP(NOTIFICATION_LOW_MASK, (low)) | \
|
||||
FIELD_PREP(NOTIFICATION_HIGH_MASK, (high)))
|
||||
|
||||
#define RECEIVER_VCPU_MASK GENMASK(31, 16)
|
||||
#define PACK_NOTIFICATION_GET_RECEIVER_INFO(vcpu_r, r) \
|
||||
(FIELD_PREP(RECEIVER_VCPU_MASK, (vcpu_r)) | \
|
||||
FIELD_PREP(RECEIVER_ID_MASK, (r)))
|
||||
|
||||
#define NOTIFICATION_INFO_GET_MORE_PEND_MASK BIT(0)
|
||||
#define NOTIFICATION_INFO_GET_ID_COUNT GENMASK(11, 7)
|
||||
#define ID_LIST_MASK_64 GENMASK(51, 12)
|
||||
#define ID_LIST_MASK_32 GENMASK(31, 12)
|
||||
#define MAX_IDS_64 20
|
||||
#define MAX_IDS_32 10
|
||||
|
||||
#define PER_VCPU_NOTIFICATION_FLAG BIT(0)
|
||||
#define SECURE_PARTITION_BITMAP BIT(0)
|
||||
#define NON_SECURE_VM_BITMAP BIT(1)
|
||||
#define SPM_FRAMEWORK_BITMAP BIT(2)
|
||||
#define NS_HYP_FRAMEWORK_BITMAP BIT(3)
|
||||
|
||||
static int ffa_notification_bind_common(u16 dst_id, u64 bitmap,
|
||||
u32 flags, bool is_bind)
|
||||
{
|
||||
ffa_value_t ret;
|
||||
u32 func, src_dst_ids = PACK_TARGET_INFO(dst_id, drv_info->vm_id);
|
||||
|
||||
func = is_bind ? FFA_NOTIFICATION_BIND : FFA_NOTIFICATION_UNBIND;
|
||||
|
||||
invoke_ffa_fn((ffa_value_t){
|
||||
.a0 = func, .a1 = src_dst_ids, .a2 = flags,
|
||||
.a3 = NOTIFICATION_BITMAP_LOW(bitmap),
|
||||
.a4 = NOTIFICATION_BITMAP_HIGH(bitmap),
|
||||
}, &ret);
|
||||
|
||||
if (ret.a0 == FFA_ERROR)
|
||||
return ffa_to_linux_errno((int)ret.a2);
|
||||
else if (ret.a0 != FFA_SUCCESS)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int ffa_notification_set(u16 src_id, u16 dst_id, u32 flags, u64 bitmap)
|
||||
{
|
||||
ffa_value_t ret;
|
||||
u32 src_dst_ids = PACK_TARGET_INFO(dst_id, src_id);
|
||||
|
||||
invoke_ffa_fn((ffa_value_t) {
|
||||
.a0 = FFA_NOTIFICATION_SET, .a1 = src_dst_ids, .a2 = flags,
|
||||
.a3 = NOTIFICATION_BITMAP_LOW(bitmap),
|
||||
.a4 = NOTIFICATION_BITMAP_HIGH(bitmap),
|
||||
}, &ret);
|
||||
|
||||
if (ret.a0 == FFA_ERROR)
|
||||
return ffa_to_linux_errno((int)ret.a2);
|
||||
else if (ret.a0 != FFA_SUCCESS)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ffa_notify_bitmaps {
|
||||
u64 sp_map;
|
||||
u64 vm_map;
|
||||
u64 arch_map;
|
||||
};
|
||||
|
||||
static int ffa_notification_get(u32 flags, struct ffa_notify_bitmaps *notify)
|
||||
{
|
||||
ffa_value_t ret;
|
||||
u16 src_id = drv_info->vm_id;
|
||||
u16 cpu_id = smp_processor_id();
|
||||
u32 rec_vcpu_ids = PACK_NOTIFICATION_GET_RECEIVER_INFO(cpu_id, src_id);
|
||||
|
||||
invoke_ffa_fn((ffa_value_t){
|
||||
.a0 = FFA_NOTIFICATION_GET, .a1 = rec_vcpu_ids, .a2 = flags,
|
||||
}, &ret);
|
||||
|
||||
if (ret.a0 == FFA_ERROR)
|
||||
return ffa_to_linux_errno((int)ret.a2);
|
||||
else if (ret.a0 != FFA_SUCCESS)
|
||||
return -EINVAL; /* Something else went wrong. */
|
||||
|
||||
notify->sp_map = PACK_NOTIFICATION_BITMAP(ret.a2, ret.a3);
|
||||
notify->vm_map = PACK_NOTIFICATION_BITMAP(ret.a4, ret.a5);
|
||||
notify->arch_map = PACK_NOTIFICATION_BITMAP(ret.a6, ret.a7);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ffa_dev_part_info {
|
||||
ffa_sched_recv_cb callback;
|
||||
void *cb_data;
|
||||
rwlock_t rw_lock;
|
||||
};
|
||||
|
||||
static void __do_sched_recv_cb(u16 part_id, u16 vcpu, bool is_per_vcpu)
|
||||
{
|
||||
struct ffa_dev_part_info *partition;
|
||||
ffa_sched_recv_cb callback;
|
||||
void *cb_data;
|
||||
|
||||
partition = xa_load(&drv_info->partition_info, part_id);
|
||||
read_lock(&partition->rw_lock);
|
||||
callback = partition->callback;
|
||||
cb_data = partition->cb_data;
|
||||
read_unlock(&partition->rw_lock);
|
||||
|
||||
if (callback)
|
||||
callback(vcpu, is_per_vcpu, cb_data);
|
||||
}
|
||||
|
||||
static void ffa_notification_info_get(void)
|
||||
{
|
||||
int idx, list, max_ids, lists_cnt, ids_processed, ids_count[MAX_IDS_64];
|
||||
bool is_64b_resp;
|
||||
ffa_value_t ret;
|
||||
u64 id_list;
|
||||
|
||||
do {
|
||||
invoke_ffa_fn((ffa_value_t){
|
||||
.a0 = FFA_FN_NATIVE(NOTIFICATION_INFO_GET),
|
||||
}, &ret);
|
||||
|
||||
if (ret.a0 != FFA_FN_NATIVE(SUCCESS) && ret.a0 != FFA_SUCCESS) {
|
||||
if (ret.a2 != FFA_RET_NO_DATA)
|
||||
pr_err("Notification Info fetch failed: 0x%lx (0x%lx)",
|
||||
ret.a0, ret.a2);
|
||||
return;
|
||||
}
|
||||
|
||||
is_64b_resp = (ret.a0 == FFA_FN64_SUCCESS);
|
||||
|
||||
ids_processed = 0;
|
||||
lists_cnt = FIELD_GET(NOTIFICATION_INFO_GET_ID_COUNT, ret.a2);
|
||||
if (is_64b_resp) {
|
||||
max_ids = MAX_IDS_64;
|
||||
id_list = FIELD_GET(ID_LIST_MASK_64, ret.a2);
|
||||
} else {
|
||||
max_ids = MAX_IDS_32;
|
||||
id_list = FIELD_GET(ID_LIST_MASK_32, ret.a2);
|
||||
}
|
||||
|
||||
for (idx = 0; idx < lists_cnt; idx++, id_list >>= 2)
|
||||
ids_count[idx] = (id_list & 0x3) + 1;
|
||||
|
||||
/* Process IDs */
|
||||
for (list = 0; list < lists_cnt; list++) {
|
||||
u16 vcpu_id, part_id, *packed_id_list = (u16 *)&ret.a3;
|
||||
|
||||
if (ids_processed >= max_ids - 1)
|
||||
break;
|
||||
|
||||
part_id = packed_id_list[++ids_processed];
|
||||
|
||||
if (!ids_count[list]) { /* Global Notification */
|
||||
__do_sched_recv_cb(part_id, 0, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Per vCPU Notification */
|
||||
for (idx = 0; idx < ids_count[list]; idx++) {
|
||||
if (ids_processed >= max_ids - 1)
|
||||
break;
|
||||
|
||||
vcpu_id = packed_id_list[++ids_processed];
|
||||
|
||||
__do_sched_recv_cb(part_id, vcpu_id, true);
|
||||
}
|
||||
}
|
||||
} while (ret.a2 & NOTIFICATION_INFO_GET_MORE_PEND_MASK);
|
||||
}
|
||||
|
||||
static int ffa_run(struct ffa_device *dev, u16 vcpu)
|
||||
{
|
||||
ffa_value_t ret;
|
||||
u32 target = dev->vm_id << 16 | vcpu;
|
||||
|
||||
invoke_ffa_fn((ffa_value_t){ .a0 = FFA_RUN, .a1 = target, }, &ret);
|
||||
|
||||
while (ret.a0 == FFA_INTERRUPT)
|
||||
invoke_ffa_fn((ffa_value_t){ .a0 = FFA_RUN, .a1 = ret.a1, },
|
||||
&ret);
|
||||
|
||||
if (ret.a0 == FFA_ERROR)
|
||||
return ffa_to_linux_errno((int)ret.a2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ffa_set_up_mem_ops_native_flag(void)
|
||||
{
|
||||
if (!ffa_features(FFA_FN_NATIVE(MEM_LEND), 0, NULL, NULL) ||
|
||||
@ -587,17 +852,9 @@ static int ffa_partition_info_get(const char *uuid_str,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _ffa_mode_32bit_set(struct ffa_device *dev)
|
||||
{
|
||||
dev->mode_32bit = true;
|
||||
}
|
||||
|
||||
static void ffa_mode_32bit_set(struct ffa_device *dev)
|
||||
{
|
||||
if (drv_info->version > FFA_VERSION_1_0)
|
||||
return;
|
||||
|
||||
_ffa_mode_32bit_set(dev);
|
||||
dev->mode_32bit = true;
|
||||
}
|
||||
|
||||
static int ffa_sync_send_receive(struct ffa_device *dev,
|
||||
@ -630,6 +887,231 @@ static int ffa_memory_lend(struct ffa_mem_ops_args *args)
|
||||
return ffa_memory_ops(FFA_MEM_LEND, args);
|
||||
}
|
||||
|
||||
#define FFA_SECURE_PARTITION_ID_FLAG BIT(15)
|
||||
|
||||
enum notify_type {
|
||||
NON_SECURE_VM,
|
||||
SECURE_PARTITION,
|
||||
FRAMEWORK,
|
||||
};
|
||||
|
||||
struct notifier_cb_info {
|
||||
struct hlist_node hnode;
|
||||
ffa_notifier_cb cb;
|
||||
void *cb_data;
|
||||
enum notify_type type;
|
||||
};
|
||||
|
||||
static int ffa_sched_recv_cb_update(u16 part_id, ffa_sched_recv_cb callback,
|
||||
void *cb_data, bool is_registration)
|
||||
{
|
||||
struct ffa_dev_part_info *partition;
|
||||
bool cb_valid;
|
||||
|
||||
partition = xa_load(&drv_info->partition_info, part_id);
|
||||
write_lock(&partition->rw_lock);
|
||||
|
||||
cb_valid = !!partition->callback;
|
||||
if (!(is_registration ^ cb_valid)) {
|
||||
write_unlock(&partition->rw_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
partition->callback = callback;
|
||||
partition->cb_data = cb_data;
|
||||
|
||||
write_unlock(&partition->rw_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ffa_sched_recv_cb_register(struct ffa_device *dev,
|
||||
ffa_sched_recv_cb cb, void *cb_data)
|
||||
{
|
||||
return ffa_sched_recv_cb_update(dev->vm_id, cb, cb_data, true);
|
||||
}
|
||||
|
||||
static int ffa_sched_recv_cb_unregister(struct ffa_device *dev)
|
||||
{
|
||||
return ffa_sched_recv_cb_update(dev->vm_id, NULL, NULL, false);
|
||||
}
|
||||
|
||||
static int ffa_notification_bind(u16 dst_id, u64 bitmap, u32 flags)
|
||||
{
|
||||
return ffa_notification_bind_common(dst_id, bitmap, flags, true);
|
||||
}
|
||||
|
||||
static int ffa_notification_unbind(u16 dst_id, u64 bitmap)
|
||||
{
|
||||
return ffa_notification_bind_common(dst_id, bitmap, 0, false);
|
||||
}
|
||||
|
||||
/* Should be called while the notify_lock is taken */
|
||||
static struct notifier_cb_info *
|
||||
notifier_hash_node_get(u16 notify_id, enum notify_type type)
|
||||
{
|
||||
struct notifier_cb_info *node;
|
||||
|
||||
hash_for_each_possible(drv_info->notifier_hash, node, hnode, notify_id)
|
||||
if (type == node->type)
|
||||
return node;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
update_notifier_cb(int notify_id, enum notify_type type, ffa_notifier_cb cb,
|
||||
void *cb_data, bool is_registration)
|
||||
{
|
||||
struct notifier_cb_info *cb_info = NULL;
|
||||
bool cb_found;
|
||||
|
||||
cb_info = notifier_hash_node_get(notify_id, type);
|
||||
cb_found = !!cb_info;
|
||||
|
||||
if (!(is_registration ^ cb_found))
|
||||
return -EINVAL;
|
||||
|
||||
if (is_registration) {
|
||||
cb_info = kzalloc(sizeof(*cb_info), GFP_KERNEL);
|
||||
if (!cb_info)
|
||||
return -ENOMEM;
|
||||
|
||||
cb_info->type = type;
|
||||
cb_info->cb = cb;
|
||||
cb_info->cb_data = cb_data;
|
||||
|
||||
hash_add(drv_info->notifier_hash, &cb_info->hnode, notify_id);
|
||||
} else {
|
||||
hash_del(&cb_info->hnode);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum notify_type ffa_notify_type_get(u16 vm_id)
|
||||
{
|
||||
if (vm_id & FFA_SECURE_PARTITION_ID_FLAG)
|
||||
return SECURE_PARTITION;
|
||||
else
|
||||
return NON_SECURE_VM;
|
||||
}
|
||||
|
||||
static int ffa_notify_relinquish(struct ffa_device *dev, int notify_id)
|
||||
{
|
||||
int rc;
|
||||
enum notify_type type = ffa_notify_type_get(dev->vm_id);
|
||||
|
||||
if (notify_id >= FFA_MAX_NOTIFICATIONS)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&drv_info->notify_lock);
|
||||
|
||||
rc = update_notifier_cb(notify_id, type, NULL, NULL, false);
|
||||
if (rc) {
|
||||
pr_err("Could not unregister notification callback\n");
|
||||
mutex_unlock(&drv_info->notify_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = ffa_notification_unbind(dev->vm_id, BIT(notify_id));
|
||||
|
||||
mutex_unlock(&drv_info->notify_lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ffa_notify_request(struct ffa_device *dev, bool is_per_vcpu,
|
||||
ffa_notifier_cb cb, void *cb_data, int notify_id)
|
||||
{
|
||||
int rc;
|
||||
u32 flags = 0;
|
||||
enum notify_type type = ffa_notify_type_get(dev->vm_id);
|
||||
|
||||
if (notify_id >= FFA_MAX_NOTIFICATIONS)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&drv_info->notify_lock);
|
||||
|
||||
if (is_per_vcpu)
|
||||
flags = PER_VCPU_NOTIFICATION_FLAG;
|
||||
|
||||
rc = ffa_notification_bind(dev->vm_id, BIT(notify_id), flags);
|
||||
if (rc) {
|
||||
mutex_unlock(&drv_info->notify_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = update_notifier_cb(notify_id, type, cb, cb_data, true);
|
||||
if (rc) {
|
||||
pr_err("Failed to register callback for %d - %d\n",
|
||||
notify_id, rc);
|
||||
ffa_notification_unbind(dev->vm_id, BIT(notify_id));
|
||||
}
|
||||
mutex_unlock(&drv_info->notify_lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ffa_notify_send(struct ffa_device *dev, int notify_id,
|
||||
bool is_per_vcpu, u16 vcpu)
|
||||
{
|
||||
u32 flags = 0;
|
||||
|
||||
if (is_per_vcpu)
|
||||
flags |= (PER_VCPU_NOTIFICATION_FLAG | vcpu << 16);
|
||||
|
||||
return ffa_notification_set(dev->vm_id, drv_info->vm_id, flags,
|
||||
BIT(notify_id));
|
||||
}
|
||||
|
||||
static void handle_notif_callbacks(u64 bitmap, enum notify_type type)
|
||||
{
|
||||
int notify_id;
|
||||
struct notifier_cb_info *cb_info = NULL;
|
||||
|
||||
for (notify_id = 0; notify_id <= FFA_MAX_NOTIFICATIONS && bitmap;
|
||||
notify_id++, bitmap >>= 1) {
|
||||
if (!(bitmap & 1))
|
||||
continue;
|
||||
|
||||
mutex_lock(&drv_info->notify_lock);
|
||||
cb_info = notifier_hash_node_get(notify_id, type);
|
||||
mutex_unlock(&drv_info->notify_lock);
|
||||
|
||||
if (cb_info && cb_info->cb)
|
||||
cb_info->cb(notify_id, cb_info->cb_data);
|
||||
}
|
||||
}
|
||||
|
||||
static void notif_pcpu_irq_work_fn(struct work_struct *work)
|
||||
{
|
||||
int rc;
|
||||
struct ffa_notify_bitmaps bitmaps;
|
||||
|
||||
rc = ffa_notification_get(SECURE_PARTITION_BITMAP |
|
||||
SPM_FRAMEWORK_BITMAP, &bitmaps);
|
||||
if (rc) {
|
||||
pr_err("Failed to retrieve notifications with %d!\n", rc);
|
||||
return;
|
||||
}
|
||||
|
||||
handle_notif_callbacks(bitmaps.vm_map, NON_SECURE_VM);
|
||||
handle_notif_callbacks(bitmaps.sp_map, SECURE_PARTITION);
|
||||
handle_notif_callbacks(bitmaps.arch_map, FRAMEWORK);
|
||||
}
|
||||
|
||||
static void
|
||||
ffa_self_notif_handle(u16 vcpu, bool is_per_vcpu, void *cb_data)
|
||||
{
|
||||
struct ffa_drv_info *info = cb_data;
|
||||
|
||||
if (!is_per_vcpu)
|
||||
notif_pcpu_irq_work_fn(&info->notif_pcpu_work);
|
||||
else
|
||||
queue_work_on(vcpu, info->notif_pcpu_wq,
|
||||
&info->notif_pcpu_work);
|
||||
}
|
||||
|
||||
static const struct ffa_info_ops ffa_drv_info_ops = {
|
||||
.api_version_get = ffa_api_version_get,
|
||||
.partition_info_get = ffa_partition_info_get,
|
||||
@ -646,10 +1128,24 @@ static const struct ffa_mem_ops ffa_drv_mem_ops = {
|
||||
.memory_lend = ffa_memory_lend,
|
||||
};
|
||||
|
||||
static const struct ffa_cpu_ops ffa_drv_cpu_ops = {
|
||||
.run = ffa_run,
|
||||
};
|
||||
|
||||
static const struct ffa_notifier_ops ffa_drv_notifier_ops = {
|
||||
.sched_recv_cb_register = ffa_sched_recv_cb_register,
|
||||
.sched_recv_cb_unregister = ffa_sched_recv_cb_unregister,
|
||||
.notify_request = ffa_notify_request,
|
||||
.notify_relinquish = ffa_notify_relinquish,
|
||||
.notify_send = ffa_notify_send,
|
||||
};
|
||||
|
||||
static const struct ffa_ops ffa_drv_ops = {
|
||||
.info_ops = &ffa_drv_info_ops,
|
||||
.msg_ops = &ffa_drv_msg_ops,
|
||||
.mem_ops = &ffa_drv_mem_ops,
|
||||
.cpu_ops = &ffa_drv_cpu_ops,
|
||||
.notifier_ops = &ffa_drv_notifier_ops,
|
||||
};
|
||||
|
||||
void ffa_device_match_uuid(struct ffa_device *ffa_dev, const uuid_t *uuid)
|
||||
@ -680,6 +1176,7 @@ static void ffa_setup_partitions(void)
|
||||
int count, idx;
|
||||
uuid_t uuid;
|
||||
struct ffa_device *ffa_dev;
|
||||
struct ffa_dev_part_info *info;
|
||||
struct ffa_partition_info *pbuf, *tpbuf;
|
||||
|
||||
count = ffa_partition_probe(&uuid_null, &pbuf);
|
||||
@ -688,6 +1185,7 @@ static void ffa_setup_partitions(void)
|
||||
return;
|
||||
}
|
||||
|
||||
xa_init(&drv_info->partition_info);
|
||||
for (idx = 0, tpbuf = pbuf; idx < count; idx++, tpbuf++) {
|
||||
import_uuid(&uuid, (u8 *)tpbuf->uuid);
|
||||
|
||||
@ -706,9 +1204,232 @@ static void ffa_setup_partitions(void)
|
||||
|
||||
if (drv_info->version > FFA_VERSION_1_0 &&
|
||||
!(tpbuf->properties & FFA_PARTITION_AARCH64_EXEC))
|
||||
_ffa_mode_32bit_set(ffa_dev);
|
||||
ffa_mode_32bit_set(ffa_dev);
|
||||
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info) {
|
||||
ffa_device_unregister(ffa_dev);
|
||||
continue;
|
||||
}
|
||||
xa_store(&drv_info->partition_info, tpbuf->id, info, GFP_KERNEL);
|
||||
}
|
||||
drv_info->partition_count = count;
|
||||
|
||||
kfree(pbuf);
|
||||
|
||||
/* Allocate for the host */
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return;
|
||||
xa_store(&drv_info->partition_info, drv_info->vm_id, info, GFP_KERNEL);
|
||||
drv_info->partition_count++;
|
||||
}
|
||||
|
||||
static void ffa_partitions_cleanup(void)
|
||||
{
|
||||
struct ffa_dev_part_info **info;
|
||||
int idx, count = drv_info->partition_count;
|
||||
|
||||
if (!count)
|
||||
return;
|
||||
|
||||
info = kcalloc(count, sizeof(**info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return;
|
||||
|
||||
xa_extract(&drv_info->partition_info, (void **)info, 0, VM_ID_MASK,
|
||||
count, XA_PRESENT);
|
||||
|
||||
for (idx = 0; idx < count; idx++)
|
||||
kfree(info[idx]);
|
||||
kfree(info);
|
||||
|
||||
drv_info->partition_count = 0;
|
||||
xa_destroy(&drv_info->partition_info);
|
||||
}
|
||||
|
||||
/* FFA FEATURE IDs */
|
||||
#define FFA_FEAT_NOTIFICATION_PENDING_INT (1)
|
||||
#define FFA_FEAT_SCHEDULE_RECEIVER_INT (2)
|
||||
#define FFA_FEAT_MANAGED_EXIT_INT (3)
|
||||
|
||||
static irqreturn_t irq_handler(int irq, void *irq_data)
|
||||
{
|
||||
struct ffa_pcpu_irq *pcpu = irq_data;
|
||||
struct ffa_drv_info *info = pcpu->info;
|
||||
|
||||
queue_work(info->notif_pcpu_wq, &info->irq_work);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void ffa_sched_recv_irq_work_fn(struct work_struct *work)
|
||||
{
|
||||
ffa_notification_info_get();
|
||||
}
|
||||
|
||||
static int ffa_sched_recv_irq_map(void)
|
||||
{
|
||||
int ret, irq, sr_intid;
|
||||
|
||||
/* The returned sr_intid is assumed to be SGI donated to NS world */
|
||||
ret = ffa_features(FFA_FEAT_SCHEDULE_RECEIVER_INT, 0, &sr_intid, NULL);
|
||||
if (ret < 0) {
|
||||
if (ret != -EOPNOTSUPP)
|
||||
pr_err("Failed to retrieve scheduler Rx interrupt\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (acpi_disabled) {
|
||||
struct of_phandle_args oirq = {};
|
||||
struct device_node *gic;
|
||||
|
||||
/* Only GICv3 supported currently with the device tree */
|
||||
gic = of_find_compatible_node(NULL, NULL, "arm,gic-v3");
|
||||
if (!gic)
|
||||
return -ENXIO;
|
||||
|
||||
oirq.np = gic;
|
||||
oirq.args_count = 1;
|
||||
oirq.args[0] = sr_intid;
|
||||
irq = irq_create_of_mapping(&oirq);
|
||||
of_node_put(gic);
|
||||
#ifdef CONFIG_ACPI
|
||||
} else {
|
||||
irq = acpi_register_gsi(NULL, sr_intid, ACPI_EDGE_SENSITIVE,
|
||||
ACPI_ACTIVE_HIGH);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (irq <= 0) {
|
||||
pr_err("Failed to create IRQ mapping!\n");
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
return irq;
|
||||
}
|
||||
|
||||
static void ffa_sched_recv_irq_unmap(void)
|
||||
{
|
||||
if (drv_info->sched_recv_irq)
|
||||
irq_dispose_mapping(drv_info->sched_recv_irq);
|
||||
}
|
||||
|
||||
static int ffa_cpuhp_pcpu_irq_enable(unsigned int cpu)
|
||||
{
|
||||
enable_percpu_irq(drv_info->sched_recv_irq, IRQ_TYPE_NONE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ffa_cpuhp_pcpu_irq_disable(unsigned int cpu)
|
||||
{
|
||||
disable_percpu_irq(drv_info->sched_recv_irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ffa_uninit_pcpu_irq(void)
|
||||
{
|
||||
if (drv_info->cpuhp_state)
|
||||
cpuhp_remove_state(drv_info->cpuhp_state);
|
||||
|
||||
if (drv_info->notif_pcpu_wq)
|
||||
destroy_workqueue(drv_info->notif_pcpu_wq);
|
||||
|
||||
if (drv_info->sched_recv_irq)
|
||||
free_percpu_irq(drv_info->sched_recv_irq, drv_info->irq_pcpu);
|
||||
|
||||
if (drv_info->irq_pcpu)
|
||||
free_percpu(drv_info->irq_pcpu);
|
||||
}
|
||||
|
||||
static int ffa_init_pcpu_irq(unsigned int irq)
|
||||
{
|
||||
struct ffa_pcpu_irq __percpu *irq_pcpu;
|
||||
int ret, cpu;
|
||||
|
||||
irq_pcpu = alloc_percpu(struct ffa_pcpu_irq);
|
||||
if (!irq_pcpu)
|
||||
return -ENOMEM;
|
||||
|
||||
for_each_present_cpu(cpu)
|
||||
per_cpu_ptr(irq_pcpu, cpu)->info = drv_info;
|
||||
|
||||
drv_info->irq_pcpu = irq_pcpu;
|
||||
|
||||
ret = request_percpu_irq(irq, irq_handler, "ARM-FFA", irq_pcpu);
|
||||
if (ret) {
|
||||
pr_err("Error registering notification IRQ %d: %d\n", irq, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
INIT_WORK(&drv_info->irq_work, ffa_sched_recv_irq_work_fn);
|
||||
INIT_WORK(&drv_info->notif_pcpu_work, notif_pcpu_irq_work_fn);
|
||||
drv_info->notif_pcpu_wq = create_workqueue("ffa_pcpu_irq_notification");
|
||||
if (!drv_info->notif_pcpu_wq)
|
||||
return -EINVAL;
|
||||
|
||||
ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "ffa/pcpu-irq:starting",
|
||||
ffa_cpuhp_pcpu_irq_enable,
|
||||
ffa_cpuhp_pcpu_irq_disable);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
drv_info->cpuhp_state = ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ffa_notifications_cleanup(void)
|
||||
{
|
||||
ffa_uninit_pcpu_irq();
|
||||
ffa_sched_recv_irq_unmap();
|
||||
|
||||
if (drv_info->bitmap_created) {
|
||||
ffa_notification_bitmap_destroy();
|
||||
drv_info->bitmap_created = false;
|
||||
}
|
||||
}
|
||||
|
||||
static int ffa_notifications_setup(void)
|
||||
{
|
||||
int ret, irq;
|
||||
|
||||
ret = ffa_features(FFA_NOTIFICATION_BITMAP_CREATE, 0, NULL, NULL);
|
||||
if (ret) {
|
||||
pr_err("Notifications not supported, continuing with it ..\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = ffa_notification_bitmap_create();
|
||||
if (ret) {
|
||||
pr_err("notification_bitmap_create error %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
drv_info->bitmap_created = true;
|
||||
|
||||
irq = ffa_sched_recv_irq_map();
|
||||
if (irq <= 0) {
|
||||
ret = irq;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
drv_info->sched_recv_irq = irq;
|
||||
|
||||
ret = ffa_init_pcpu_irq(irq);
|
||||
if (ret)
|
||||
goto cleanup;
|
||||
|
||||
hash_init(drv_info->notifier_hash);
|
||||
mutex_init(&drv_info->notify_lock);
|
||||
|
||||
/* Register internal scheduling callback */
|
||||
ret = ffa_sched_recv_cb_update(drv_info->vm_id, ffa_self_notif_handle,
|
||||
drv_info, true);
|
||||
if (!ret)
|
||||
return ret;
|
||||
cleanup:
|
||||
ffa_notifications_cleanup();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __init ffa_init(void)
|
||||
@ -766,7 +1487,13 @@ static int __init ffa_init(void)
|
||||
|
||||
ffa_set_up_mem_ops_native_flag();
|
||||
|
||||
ret = ffa_notifications_setup();
|
||||
if (ret)
|
||||
goto partitions_cleanup;
|
||||
|
||||
return 0;
|
||||
partitions_cleanup:
|
||||
ffa_partitions_cleanup();
|
||||
free_pages:
|
||||
if (drv_info->tx_buffer)
|
||||
free_pages_exact(drv_info->tx_buffer, RXTX_BUFFER_SIZE);
|
||||
@ -781,9 +1508,12 @@ subsys_initcall(ffa_init);
|
||||
|
||||
static void __exit ffa_exit(void)
|
||||
{
|
||||
ffa_notifications_cleanup();
|
||||
ffa_partitions_cleanup();
|
||||
ffa_rxtx_unmap(drv_info->vm_id);
|
||||
free_pages_exact(drv_info->tx_buffer, RXTX_BUFFER_SIZE);
|
||||
free_pages_exact(drv_info->rx_buffer, RXTX_BUFFER_SIZE);
|
||||
xa_destroy(&drv_info->partition_info);
|
||||
kfree(drv_info);
|
||||
arm_ffa_bus_exit();
|
||||
}
|
||||
|
@ -181,6 +181,18 @@ config ARM_SCMI_POWER_DOMAIN
|
||||
will be called scmi_pm_domain. Note this may needed early in boot
|
||||
before rootfs may be available.
|
||||
|
||||
config ARM_SCMI_PERF_DOMAIN
|
||||
tristate "SCMI performance domain driver"
|
||||
depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
|
||||
default y
|
||||
select PM_GENERIC_DOMAINS if PM
|
||||
help
|
||||
This enables support for the SCMI performance domains which can be
|
||||
enabled or disabled via the SCP firmware.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called scmi_perf_domain.
|
||||
|
||||
config ARM_SCMI_POWER_CONTROL
|
||||
tristate "SCMI system power control driver"
|
||||
depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
|
||||
|
@ -16,7 +16,6 @@ scmi-module-objs := $(scmi-driver-y) $(scmi-protocols-y) $(scmi-transport-y)
|
||||
obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-core.o
|
||||
obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o
|
||||
|
||||
obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o
|
||||
obj-$(CONFIG_ARM_SCMI_POWER_CONTROL) += scmi_power_control.o
|
||||
|
||||
ifeq ($(CONFIG_THUMB2_KERNEL)$(CONFIG_CC_IS_CLANG),yy)
|
||||
|
@ -21,6 +21,17 @@ enum scmi_clock_protocol_cmd {
|
||||
CLOCK_NAME_GET = 0x8,
|
||||
CLOCK_RATE_NOTIFY = 0x9,
|
||||
CLOCK_RATE_CHANGE_REQUESTED_NOTIFY = 0xA,
|
||||
CLOCK_CONFIG_GET = 0xB,
|
||||
CLOCK_POSSIBLE_PARENTS_GET = 0xC,
|
||||
CLOCK_PARENT_SET = 0xD,
|
||||
CLOCK_PARENT_GET = 0xE,
|
||||
};
|
||||
|
||||
enum clk_state {
|
||||
CLK_STATE_DISABLE,
|
||||
CLK_STATE_ENABLE,
|
||||
CLK_STATE_RESERVED,
|
||||
CLK_STATE_UNCHANGED,
|
||||
};
|
||||
|
||||
struct scmi_msg_resp_clock_protocol_attributes {
|
||||
@ -31,19 +42,59 @@ struct scmi_msg_resp_clock_protocol_attributes {
|
||||
|
||||
struct scmi_msg_resp_clock_attributes {
|
||||
__le32 attributes;
|
||||
#define CLOCK_ENABLE BIT(0)
|
||||
#define SUPPORTS_RATE_CHANGED_NOTIF(x) ((x) & BIT(31))
|
||||
#define SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(x) ((x) & BIT(30))
|
||||
#define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(29))
|
||||
#define SUPPORTS_PARENT_CLOCK(x) ((x) & BIT(28))
|
||||
u8 name[SCMI_SHORT_NAME_MAX_SIZE];
|
||||
__le32 clock_enable_latency;
|
||||
};
|
||||
|
||||
struct scmi_clock_set_config {
|
||||
struct scmi_msg_clock_possible_parents {
|
||||
__le32 id;
|
||||
__le32 skip_parents;
|
||||
};
|
||||
|
||||
struct scmi_msg_resp_clock_possible_parents {
|
||||
__le32 num_parent_flags;
|
||||
#define NUM_PARENTS_RETURNED(x) ((x) & 0xff)
|
||||
#define NUM_PARENTS_REMAINING(x) ((x) >> 24)
|
||||
__le32 possible_parents[];
|
||||
};
|
||||
|
||||
struct scmi_msg_clock_set_parent {
|
||||
__le32 id;
|
||||
__le32 parent_id;
|
||||
};
|
||||
|
||||
struct scmi_msg_clock_config_set {
|
||||
__le32 id;
|
||||
__le32 attributes;
|
||||
};
|
||||
|
||||
/* Valid only from SCMI clock v2.1 */
|
||||
struct scmi_msg_clock_config_set_v2 {
|
||||
__le32 id;
|
||||
__le32 attributes;
|
||||
#define NULL_OEM_TYPE 0
|
||||
#define REGMASK_OEM_TYPE_SET GENMASK(23, 16)
|
||||
#define REGMASK_CLK_STATE GENMASK(1, 0)
|
||||
__le32 oem_config_val;
|
||||
};
|
||||
|
||||
struct scmi_msg_clock_config_get {
|
||||
__le32 id;
|
||||
__le32 flags;
|
||||
#define REGMASK_OEM_TYPE_GET GENMASK(7, 0)
|
||||
};
|
||||
|
||||
struct scmi_msg_resp_clock_config_get {
|
||||
__le32 attributes;
|
||||
__le32 config;
|
||||
#define IS_CLK_ENABLED(x) le32_get_bits((x), BIT(0))
|
||||
__le32 oem_config_val;
|
||||
};
|
||||
|
||||
struct scmi_msg_clock_describe_rates {
|
||||
__le32 id;
|
||||
__le32 rate_index;
|
||||
@ -100,6 +151,12 @@ struct clock_info {
|
||||
int max_async_req;
|
||||
atomic_t cur_async_req;
|
||||
struct scmi_clock_info *clk;
|
||||
int (*clock_config_set)(const struct scmi_protocol_handle *ph,
|
||||
u32 clk_id, enum clk_state state,
|
||||
u8 oem_type, u32 oem_val, bool atomic);
|
||||
int (*clock_config_get)(const struct scmi_protocol_handle *ph,
|
||||
u32 clk_id, u8 oem_type, u32 *attributes,
|
||||
bool *enabled, u32 *oem_val, bool atomic);
|
||||
};
|
||||
|
||||
static enum scmi_clock_protocol_cmd evt_2_cmd[] = {
|
||||
@ -132,6 +189,98 @@ scmi_clock_protocol_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct scmi_clk_ipriv {
|
||||
struct device *dev;
|
||||
u32 clk_id;
|
||||
struct scmi_clock_info *clk;
|
||||
};
|
||||
|
||||
static void iter_clk_possible_parents_prepare_message(void *message, unsigned int desc_index,
|
||||
const void *priv)
|
||||
{
|
||||
struct scmi_msg_clock_possible_parents *msg = message;
|
||||
const struct scmi_clk_ipriv *p = priv;
|
||||
|
||||
msg->id = cpu_to_le32(p->clk_id);
|
||||
/* Set the number of OPPs to be skipped/already read */
|
||||
msg->skip_parents = cpu_to_le32(desc_index);
|
||||
}
|
||||
|
||||
static int iter_clk_possible_parents_update_state(struct scmi_iterator_state *st,
|
||||
const void *response, void *priv)
|
||||
{
|
||||
const struct scmi_msg_resp_clock_possible_parents *r = response;
|
||||
struct scmi_clk_ipriv *p = priv;
|
||||
struct device *dev = ((struct scmi_clk_ipriv *)p)->dev;
|
||||
u32 flags;
|
||||
|
||||
flags = le32_to_cpu(r->num_parent_flags);
|
||||
st->num_returned = NUM_PARENTS_RETURNED(flags);
|
||||
st->num_remaining = NUM_PARENTS_REMAINING(flags);
|
||||
|
||||
/*
|
||||
* num parents is not declared previously anywhere so we
|
||||
* assume it's returned+remaining on first call.
|
||||
*/
|
||||
if (!st->max_resources) {
|
||||
p->clk->num_parents = st->num_returned + st->num_remaining;
|
||||
p->clk->parents = devm_kcalloc(dev, p->clk->num_parents,
|
||||
sizeof(*p->clk->parents),
|
||||
GFP_KERNEL);
|
||||
if (!p->clk->parents) {
|
||||
p->clk->num_parents = 0;
|
||||
return -ENOMEM;
|
||||
}
|
||||
st->max_resources = st->num_returned + st->num_remaining;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iter_clk_possible_parents_process_response(const struct scmi_protocol_handle *ph,
|
||||
const void *response,
|
||||
struct scmi_iterator_state *st,
|
||||
void *priv)
|
||||
{
|
||||
const struct scmi_msg_resp_clock_possible_parents *r = response;
|
||||
struct scmi_clk_ipriv *p = priv;
|
||||
|
||||
u32 *parent = &p->clk->parents[st->desc_index + st->loop_idx];
|
||||
|
||||
*parent = le32_to_cpu(r->possible_parents[st->loop_idx]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scmi_clock_possible_parents(const struct scmi_protocol_handle *ph, u32 clk_id,
|
||||
struct scmi_clock_info *clk)
|
||||
{
|
||||
struct scmi_iterator_ops ops = {
|
||||
.prepare_message = iter_clk_possible_parents_prepare_message,
|
||||
.update_state = iter_clk_possible_parents_update_state,
|
||||
.process_response = iter_clk_possible_parents_process_response,
|
||||
};
|
||||
|
||||
struct scmi_clk_ipriv ppriv = {
|
||||
.clk_id = clk_id,
|
||||
.clk = clk,
|
||||
.dev = ph->dev,
|
||||
};
|
||||
void *iter;
|
||||
int ret;
|
||||
|
||||
iter = ph->hops->iter_response_init(ph, &ops, 0,
|
||||
CLOCK_POSSIBLE_PARENTS_GET,
|
||||
sizeof(struct scmi_msg_clock_possible_parents),
|
||||
&ppriv);
|
||||
if (IS_ERR(iter))
|
||||
return PTR_ERR(iter);
|
||||
|
||||
ret = ph->hops->iter_response_run(iter);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
u32 clk_id, struct scmi_clock_info *clk,
|
||||
u32 version)
|
||||
@ -176,6 +325,8 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
clk->rate_changed_notifications = true;
|
||||
if (SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(attributes))
|
||||
clk->rate_change_requested_notifications = true;
|
||||
if (SUPPORTS_PARENT_CLOCK(attributes))
|
||||
scmi_clock_possible_parents(ph, clk_id, clk);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -193,12 +344,6 @@ static int rate_cmp_func(const void *_r1, const void *_r2)
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct scmi_clk_ipriv {
|
||||
struct device *dev;
|
||||
u32 clk_id;
|
||||
struct scmi_clock_info *clk;
|
||||
};
|
||||
|
||||
static void iter_clk_describe_prepare_message(void *message,
|
||||
const unsigned int desc_index,
|
||||
const void *priv)
|
||||
@ -395,11 +540,15 @@ static int scmi_clock_rate_set(const struct scmi_protocol_handle *ph,
|
||||
|
||||
static int
|
||||
scmi_clock_config_set(const struct scmi_protocol_handle *ph, u32 clk_id,
|
||||
u32 config, bool atomic)
|
||||
enum clk_state state, u8 __unused0, u32 __unused1,
|
||||
bool atomic)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_clock_set_config *cfg;
|
||||
struct scmi_msg_clock_config_set *cfg;
|
||||
|
||||
if (state >= CLK_STATE_RESERVED)
|
||||
return -EINVAL;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, CLOCK_CONFIG_SET,
|
||||
sizeof(*cfg), 0, &t);
|
||||
@ -410,7 +559,7 @@ scmi_clock_config_set(const struct scmi_protocol_handle *ph, u32 clk_id,
|
||||
|
||||
cfg = t->tx.buf;
|
||||
cfg->id = cpu_to_le32(clk_id);
|
||||
cfg->attributes = cpu_to_le32(config);
|
||||
cfg->attributes = cpu_to_le32(state);
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
|
||||
@ -418,26 +567,221 @@ scmi_clock_config_set(const struct scmi_protocol_handle *ph, u32 clk_id,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_clock_enable(const struct scmi_protocol_handle *ph, u32 clk_id)
|
||||
static int
|
||||
scmi_clock_set_parent(const struct scmi_protocol_handle *ph, u32 clk_id,
|
||||
u32 parent_id)
|
||||
{
|
||||
return scmi_clock_config_set(ph, clk_id, CLOCK_ENABLE, false);
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_msg_clock_set_parent *cfg;
|
||||
struct clock_info *ci = ph->get_priv(ph);
|
||||
struct scmi_clock_info *clk;
|
||||
|
||||
if (clk_id >= ci->num_clocks)
|
||||
return -EINVAL;
|
||||
|
||||
clk = ci->clk + clk_id;
|
||||
|
||||
if (parent_id >= clk->num_parents)
|
||||
return -EINVAL;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, CLOCK_PARENT_SET,
|
||||
sizeof(*cfg), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
t->hdr.poll_completion = false;
|
||||
|
||||
cfg = t->tx.buf;
|
||||
cfg->id = cpu_to_le32(clk_id);
|
||||
cfg->parent_id = cpu_to_le32(clk->parents[parent_id]);
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_clock_disable(const struct scmi_protocol_handle *ph, u32 clk_id)
|
||||
static int
|
||||
scmi_clock_get_parent(const struct scmi_protocol_handle *ph, u32 clk_id,
|
||||
u32 *parent_id)
|
||||
{
|
||||
return scmi_clock_config_set(ph, clk_id, 0, false);
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, CLOCK_PARENT_GET,
|
||||
sizeof(__le32), sizeof(u32), &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
put_unaligned_le32(clk_id, t->tx.buf);
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (!ret)
|
||||
*parent_id = get_unaligned_le32(t->rx.buf);
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_clock_enable_atomic(const struct scmi_protocol_handle *ph,
|
||||
u32 clk_id)
|
||||
/* For SCMI clock v2.1 and onwards */
|
||||
static int
|
||||
scmi_clock_config_set_v2(const struct scmi_protocol_handle *ph, u32 clk_id,
|
||||
enum clk_state state, u8 oem_type, u32 oem_val,
|
||||
bool atomic)
|
||||
{
|
||||
return scmi_clock_config_set(ph, clk_id, CLOCK_ENABLE, true);
|
||||
int ret;
|
||||
u32 attrs;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_msg_clock_config_set_v2 *cfg;
|
||||
|
||||
if (state == CLK_STATE_RESERVED ||
|
||||
(!oem_type && state == CLK_STATE_UNCHANGED))
|
||||
return -EINVAL;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, CLOCK_CONFIG_SET,
|
||||
sizeof(*cfg), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
t->hdr.poll_completion = atomic;
|
||||
|
||||
attrs = FIELD_PREP(REGMASK_OEM_TYPE_SET, oem_type) |
|
||||
FIELD_PREP(REGMASK_CLK_STATE, state);
|
||||
|
||||
cfg = t->tx.buf;
|
||||
cfg->id = cpu_to_le32(clk_id);
|
||||
cfg->attributes = cpu_to_le32(attrs);
|
||||
/* Clear in any case */
|
||||
cfg->oem_config_val = cpu_to_le32(0);
|
||||
if (oem_type)
|
||||
cfg->oem_config_val = cpu_to_le32(oem_val);
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_clock_disable_atomic(const struct scmi_protocol_handle *ph,
|
||||
u32 clk_id)
|
||||
static int scmi_clock_enable(const struct scmi_protocol_handle *ph, u32 clk_id,
|
||||
bool atomic)
|
||||
{
|
||||
return scmi_clock_config_set(ph, clk_id, 0, true);
|
||||
struct clock_info *ci = ph->get_priv(ph);
|
||||
|
||||
return ci->clock_config_set(ph, clk_id, CLK_STATE_ENABLE,
|
||||
NULL_OEM_TYPE, 0, atomic);
|
||||
}
|
||||
|
||||
static int scmi_clock_disable(const struct scmi_protocol_handle *ph, u32 clk_id,
|
||||
bool atomic)
|
||||
{
|
||||
struct clock_info *ci = ph->get_priv(ph);
|
||||
|
||||
return ci->clock_config_set(ph, clk_id, CLK_STATE_DISABLE,
|
||||
NULL_OEM_TYPE, 0, atomic);
|
||||
}
|
||||
|
||||
/* For SCMI clock v2.1 and onwards */
|
||||
static int
|
||||
scmi_clock_config_get_v2(const struct scmi_protocol_handle *ph, u32 clk_id,
|
||||
u8 oem_type, u32 *attributes, bool *enabled,
|
||||
u32 *oem_val, bool atomic)
|
||||
{
|
||||
int ret;
|
||||
u32 flags;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_msg_clock_config_get *cfg;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, CLOCK_CONFIG_GET,
|
||||
sizeof(*cfg), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
t->hdr.poll_completion = atomic;
|
||||
|
||||
flags = FIELD_PREP(REGMASK_OEM_TYPE_GET, oem_type);
|
||||
|
||||
cfg = t->tx.buf;
|
||||
cfg->id = cpu_to_le32(clk_id);
|
||||
cfg->flags = cpu_to_le32(flags);
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (!ret) {
|
||||
struct scmi_msg_resp_clock_config_get *resp = t->rx.buf;
|
||||
|
||||
if (attributes)
|
||||
*attributes = le32_to_cpu(resp->attributes);
|
||||
|
||||
if (enabled)
|
||||
*enabled = IS_CLK_ENABLED(resp->config);
|
||||
|
||||
if (oem_val && oem_type)
|
||||
*oem_val = le32_to_cpu(resp->oem_config_val);
|
||||
}
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_clock_config_get(const struct scmi_protocol_handle *ph, u32 clk_id,
|
||||
u8 oem_type, u32 *attributes, bool *enabled,
|
||||
u32 *oem_val, bool atomic)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_msg_resp_clock_attributes *resp;
|
||||
|
||||
if (!enabled)
|
||||
return -EINVAL;
|
||||
|
||||
ret = ph->xops->xfer_get_init(ph, CLOCK_ATTRIBUTES,
|
||||
sizeof(clk_id), sizeof(*resp), &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
t->hdr.poll_completion = atomic;
|
||||
put_unaligned_le32(clk_id, t->tx.buf);
|
||||
resp = t->rx.buf;
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (!ret)
|
||||
*enabled = IS_CLK_ENABLED(resp->attributes);
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_clock_state_get(const struct scmi_protocol_handle *ph,
|
||||
u32 clk_id, bool *enabled, bool atomic)
|
||||
{
|
||||
struct clock_info *ci = ph->get_priv(ph);
|
||||
|
||||
return ci->clock_config_get(ph, clk_id, NULL_OEM_TYPE, NULL,
|
||||
enabled, NULL, atomic);
|
||||
}
|
||||
|
||||
static int scmi_clock_config_oem_set(const struct scmi_protocol_handle *ph,
|
||||
u32 clk_id, u8 oem_type, u32 oem_val,
|
||||
bool atomic)
|
||||
{
|
||||
struct clock_info *ci = ph->get_priv(ph);
|
||||
|
||||
return ci->clock_config_set(ph, clk_id, CLK_STATE_UNCHANGED,
|
||||
oem_type, oem_val, atomic);
|
||||
}
|
||||
|
||||
static int scmi_clock_config_oem_get(const struct scmi_protocol_handle *ph,
|
||||
u32 clk_id, u8 oem_type, u32 *oem_val,
|
||||
u32 *attributes, bool atomic)
|
||||
{
|
||||
struct clock_info *ci = ph->get_priv(ph);
|
||||
|
||||
return ci->clock_config_get(ph, clk_id, oem_type, attributes,
|
||||
NULL, oem_val, atomic);
|
||||
}
|
||||
|
||||
static int scmi_clock_count_get(const struct scmi_protocol_handle *ph)
|
||||
@ -470,8 +814,11 @@ static const struct scmi_clk_proto_ops clk_proto_ops = {
|
||||
.rate_set = scmi_clock_rate_set,
|
||||
.enable = scmi_clock_enable,
|
||||
.disable = scmi_clock_disable,
|
||||
.enable_atomic = scmi_clock_enable_atomic,
|
||||
.disable_atomic = scmi_clock_disable_atomic,
|
||||
.state_get = scmi_clock_state_get,
|
||||
.config_oem_get = scmi_clock_config_oem_get,
|
||||
.config_oem_set = scmi_clock_config_oem_set,
|
||||
.parent_set = scmi_clock_set_parent,
|
||||
.parent_get = scmi_clock_get_parent,
|
||||
};
|
||||
|
||||
static int scmi_clk_rate_notify(const struct scmi_protocol_handle *ph,
|
||||
@ -604,6 +951,15 @@ static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph)
|
||||
scmi_clock_describe_rates_get(ph, clkid, clk);
|
||||
}
|
||||
|
||||
if (PROTOCOL_REV_MAJOR(version) >= 0x2 &&
|
||||
PROTOCOL_REV_MINOR(version) >= 0x1) {
|
||||
cinfo->clock_config_set = scmi_clock_config_set_v2;
|
||||
cinfo->clock_config_get = scmi_clock_config_get_v2;
|
||||
} else {
|
||||
cinfo->clock_config_set = scmi_clock_config_set;
|
||||
cinfo->clock_config_get = scmi_clock_config_get;
|
||||
}
|
||||
|
||||
cinfo->version = version;
|
||||
return ph->set_priv(ph, cinfo);
|
||||
}
|
||||
|
@ -2915,6 +2915,7 @@ static const struct of_device_id scmi_of_match[] = {
|
||||
#ifdef CONFIG_ARM_SCMI_TRANSPORT_SMC
|
||||
{ .compatible = "arm,scmi-smc", .data = &scmi_smc_desc},
|
||||
{ .compatible = "arm,scmi-smc-param", .data = &scmi_smc_desc},
|
||||
{ .compatible = "qcom,scmi-smc", .data = &scmi_smc_desc},
|
||||
#endif
|
||||
#ifdef CONFIG_ARM_SCMI_TRANSPORT_VIRTIO
|
||||
{ .compatible = "arm,scmi-virtio", .data = &scmi_virtio_desc},
|
||||
|
@ -145,7 +145,6 @@ struct scmi_msg_resp_perf_describe_levels_v4 {
|
||||
struct perf_dom_info {
|
||||
u32 id;
|
||||
bool set_limits;
|
||||
bool set_perf;
|
||||
bool perf_limit_notify;
|
||||
bool perf_level_notify;
|
||||
bool perf_fastchannels;
|
||||
@ -154,7 +153,7 @@ struct perf_dom_info {
|
||||
u32 sustained_freq_khz;
|
||||
u32 sustained_perf_level;
|
||||
u32 mult_factor;
|
||||
char name[SCMI_MAX_STR_SIZE];
|
||||
struct scmi_perf_domain_info info;
|
||||
struct scmi_opp opp[MAX_OPPS];
|
||||
struct scmi_fc_info *fc_info;
|
||||
struct xarray opps_by_idx;
|
||||
@ -257,7 +256,7 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
flags = le32_to_cpu(attr->flags);
|
||||
|
||||
dom_info->set_limits = SUPPORTS_SET_LIMITS(flags);
|
||||
dom_info->set_perf = SUPPORTS_SET_PERF_LVL(flags);
|
||||
dom_info->info.set_perf = SUPPORTS_SET_PERF_LVL(flags);
|
||||
dom_info->perf_limit_notify = SUPPORTS_PERF_LIMIT_NOTIFY(flags);
|
||||
dom_info->perf_level_notify = SUPPORTS_PERF_LEVEL_NOTIFY(flags);
|
||||
dom_info->perf_fastchannels = SUPPORTS_PERF_FASTCHANNELS(flags);
|
||||
@ -276,7 +275,8 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
dom_info->mult_factor =
|
||||
(dom_info->sustained_freq_khz * 1000) /
|
||||
dom_info->sustained_perf_level;
|
||||
strscpy(dom_info->name, attr->name, SCMI_SHORT_NAME_MAX_SIZE);
|
||||
strscpy(dom_info->info.name, attr->name,
|
||||
SCMI_SHORT_NAME_MAX_SIZE);
|
||||
}
|
||||
|
||||
ph->xops->xfer_put(ph, t);
|
||||
@ -288,7 +288,7 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
|
||||
if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x3 &&
|
||||
SUPPORTS_EXTENDED_NAMES(flags))
|
||||
ph->hops->extended_name_get(ph, PERF_DOMAIN_NAME_GET,
|
||||
dom_info->id, dom_info->name,
|
||||
dom_info->id, dom_info->info.name,
|
||||
SCMI_MAX_STR_SIZE);
|
||||
|
||||
if (dom_info->level_indexing_mode) {
|
||||
@ -423,6 +423,36 @@ scmi_perf_describe_levels_get(const struct scmi_protocol_handle *ph,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_perf_num_domains_get(const struct scmi_protocol_handle *ph)
|
||||
{
|
||||
struct scmi_perf_info *pi = ph->get_priv(ph);
|
||||
|
||||
return pi->num_domains;
|
||||
}
|
||||
|
||||
static inline struct perf_dom_info *
|
||||
scmi_perf_domain_lookup(const struct scmi_protocol_handle *ph, u32 domain)
|
||||
{
|
||||
struct scmi_perf_info *pi = ph->get_priv(ph);
|
||||
|
||||
if (domain >= pi->num_domains)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
return pi->dom_info + domain;
|
||||
}
|
||||
|
||||
static const struct scmi_perf_domain_info *
|
||||
scmi_perf_info_get(const struct scmi_protocol_handle *ph, u32 domain)
|
||||
{
|
||||
struct perf_dom_info *dom;
|
||||
|
||||
dom = scmi_perf_domain_lookup(ph, domain);
|
||||
if (IS_ERR(dom))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
return &dom->info;
|
||||
}
|
||||
|
||||
static int scmi_perf_msg_limits_set(const struct scmi_protocol_handle *ph,
|
||||
u32 domain, u32 max_perf, u32 min_perf)
|
||||
{
|
||||
@ -446,17 +476,6 @@ static int scmi_perf_msg_limits_set(const struct scmi_protocol_handle *ph,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline struct perf_dom_info *
|
||||
scmi_perf_domain_lookup(const struct scmi_protocol_handle *ph, u32 domain)
|
||||
{
|
||||
struct scmi_perf_info *pi = ph->get_priv(ph);
|
||||
|
||||
if (domain >= pi->num_domains)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
return pi->dom_info + domain;
|
||||
}
|
||||
|
||||
static int __scmi_perf_limits_set(const struct scmi_protocol_handle *ph,
|
||||
struct perf_dom_info *dom, u32 max_perf,
|
||||
u32 min_perf)
|
||||
@ -763,71 +782,46 @@ static void scmi_perf_domain_init_fc(const struct scmi_protocol_handle *ph,
|
||||
*p_fc = fc;
|
||||
}
|
||||
|
||||
/* Device specific ops */
|
||||
static int scmi_dev_domain_id(struct device *dev)
|
||||
{
|
||||
struct of_phandle_args clkspec;
|
||||
|
||||
if (of_parse_phandle_with_args(dev->of_node, "clocks", "#clock-cells",
|
||||
0, &clkspec))
|
||||
return -EINVAL;
|
||||
|
||||
return clkspec.args[0];
|
||||
}
|
||||
|
||||
static int scmi_dvfs_device_opps_add(const struct scmi_protocol_handle *ph,
|
||||
struct device *dev)
|
||||
struct device *dev, u32 domain)
|
||||
{
|
||||
int idx, ret, domain;
|
||||
int idx, ret;
|
||||
unsigned long freq;
|
||||
struct scmi_opp *opp;
|
||||
struct dev_pm_opp_data data = {};
|
||||
struct perf_dom_info *dom;
|
||||
|
||||
domain = scmi_dev_domain_id(dev);
|
||||
if (domain < 0)
|
||||
return -EINVAL;
|
||||
|
||||
dom = scmi_perf_domain_lookup(ph, domain);
|
||||
if (IS_ERR(dom))
|
||||
return PTR_ERR(dom);
|
||||
|
||||
for (opp = dom->opp, idx = 0; idx < dom->opp_count; idx++, opp++) {
|
||||
for (idx = 0; idx < dom->opp_count; idx++) {
|
||||
if (!dom->level_indexing_mode)
|
||||
freq = opp->perf * dom->mult_factor;
|
||||
freq = dom->opp[idx].perf * dom->mult_factor;
|
||||
else
|
||||
freq = opp->indicative_freq * 1000;
|
||||
freq = dom->opp[idx].indicative_freq * 1000;
|
||||
|
||||
ret = dev_pm_opp_add(dev, freq, 0);
|
||||
data.level = dom->opp[idx].perf;
|
||||
data.freq = freq;
|
||||
|
||||
ret = dev_pm_opp_add_dynamic(dev, &data);
|
||||
if (ret) {
|
||||
dev_warn(dev, "failed to add opp %luHz\n", freq);
|
||||
|
||||
while (idx-- > 0) {
|
||||
if (!dom->level_indexing_mode)
|
||||
freq = (--opp)->perf * dom->mult_factor;
|
||||
else
|
||||
freq = (--opp)->indicative_freq * 1000;
|
||||
dev_pm_opp_remove(dev, freq);
|
||||
}
|
||||
dev_pm_opp_remove_all_dynamic(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "[%d][%s]:: Registered OPP[%d] %lu\n",
|
||||
domain, dom->name, idx, freq);
|
||||
domain, dom->info.name, idx, freq);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_dvfs_transition_latency_get(const struct scmi_protocol_handle *ph,
|
||||
struct device *dev)
|
||||
u32 domain)
|
||||
{
|
||||
int domain;
|
||||
struct perf_dom_info *dom;
|
||||
|
||||
domain = scmi_dev_domain_id(dev);
|
||||
if (domain < 0)
|
||||
return -EINVAL;
|
||||
|
||||
dom = scmi_perf_domain_lookup(ph, domain);
|
||||
if (IS_ERR(dom))
|
||||
return PTR_ERR(dom);
|
||||
@ -923,15 +917,10 @@ static int scmi_dvfs_est_power_get(const struct scmi_protocol_handle *ph,
|
||||
}
|
||||
|
||||
static bool scmi_fast_switch_possible(const struct scmi_protocol_handle *ph,
|
||||
struct device *dev)
|
||||
u32 domain)
|
||||
{
|
||||
int domain;
|
||||
struct perf_dom_info *dom;
|
||||
|
||||
domain = scmi_dev_domain_id(dev);
|
||||
if (domain < 0)
|
||||
return false;
|
||||
|
||||
dom = scmi_perf_domain_lookup(ph, domain);
|
||||
if (IS_ERR(dom))
|
||||
return false;
|
||||
@ -948,11 +937,12 @@ scmi_power_scale_get(const struct scmi_protocol_handle *ph)
|
||||
}
|
||||
|
||||
static const struct scmi_perf_proto_ops perf_proto_ops = {
|
||||
.num_domains_get = scmi_perf_num_domains_get,
|
||||
.info_get = scmi_perf_info_get,
|
||||
.limits_set = scmi_perf_limits_set,
|
||||
.limits_get = scmi_perf_limits_get,
|
||||
.level_set = scmi_perf_level_set,
|
||||
.level_get = scmi_perf_level_get,
|
||||
.device_domain_id = scmi_dev_domain_id,
|
||||
.transition_latency_get = scmi_dvfs_transition_latency_get,
|
||||
.device_opps_add = scmi_dvfs_device_opps_add,
|
||||
.freq_set = scmi_dvfs_freq_set,
|
||||
|
@ -360,8 +360,8 @@ static int scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle *ph,
|
||||
msg = t->tx.buf;
|
||||
msg->domain = cpu_to_le32(pc->id);
|
||||
msg->flags =
|
||||
cpu_to_le32(FIELD_PREP(CAP_SET_ASYNC, !!pc->async_powercap_cap_set) |
|
||||
FIELD_PREP(CAP_SET_IGNORE_DRESP, !!ignore_dresp));
|
||||
cpu_to_le32(FIELD_PREP(CAP_SET_ASYNC, pc->async_powercap_cap_set) |
|
||||
FIELD_PREP(CAP_SET_IGNORE_DRESP, ignore_dresp));
|
||||
msg->value = cpu_to_le32(power_cap);
|
||||
|
||||
if (!pc->async_powercap_cap_set || ignore_dresp) {
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/processor.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
@ -50,6 +51,8 @@
|
||||
* @func_id: smc/hvc call function id
|
||||
* @param_page: 4K page number of the shmem channel
|
||||
* @param_offset: Offset within the 4K page of the shmem channel
|
||||
* @cap_id: smc/hvc doorbell's capability id to be used on Qualcomm virtual
|
||||
* platforms
|
||||
*/
|
||||
|
||||
struct scmi_smc {
|
||||
@ -60,9 +63,10 @@ struct scmi_smc {
|
||||
struct mutex shmem_lock;
|
||||
#define INFLIGHT_NONE MSG_TOKEN_MAX
|
||||
atomic_t inflight;
|
||||
u32 func_id;
|
||||
u32 param_page;
|
||||
u32 param_offset;
|
||||
unsigned long func_id;
|
||||
unsigned long param_page;
|
||||
unsigned long param_offset;
|
||||
unsigned long cap_id;
|
||||
};
|
||||
|
||||
static irqreturn_t smc_msg_done_isr(int irq, void *data)
|
||||
@ -124,6 +128,7 @@ static int smc_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,
|
||||
bool tx)
|
||||
{
|
||||
struct device *cdev = cinfo->dev;
|
||||
unsigned long cap_id = ULONG_MAX;
|
||||
struct scmi_smc *scmi_info;
|
||||
resource_size_t size;
|
||||
struct resource res;
|
||||
@ -162,6 +167,18 @@ static int smc_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (of_device_is_compatible(dev->of_node, "qcom,scmi-smc")) {
|
||||
void __iomem *ptr = (void __iomem *)scmi_info->shmem + size - 8;
|
||||
/* The capability-id is kept in last 8 bytes of shmem.
|
||||
* +-------+ <-- 0
|
||||
* | shmem |
|
||||
* +-------+ <-- size - 8
|
||||
* | capId |
|
||||
* +-------+ <-- size
|
||||
*/
|
||||
memcpy_fromio(&cap_id, ptr, sizeof(cap_id));
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(dev->of_node, "arm,scmi-smc-param")) {
|
||||
scmi_info->param_page = SHMEM_PAGE(res.start);
|
||||
scmi_info->param_offset = SHMEM_OFFSET(res.start);
|
||||
@ -184,6 +201,7 @@ static int smc_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,
|
||||
}
|
||||
|
||||
scmi_info->func_id = func_id;
|
||||
scmi_info->cap_id = cap_id;
|
||||
scmi_info->cinfo = cinfo;
|
||||
smc_channel_lock_init(scmi_info);
|
||||
cinfo->transport_info = scmi_info;
|
||||
@ -211,8 +229,6 @@ static int smc_send_message(struct scmi_chan_info *cinfo,
|
||||
{
|
||||
struct scmi_smc *scmi_info = cinfo->transport_info;
|
||||
struct arm_smccc_res res;
|
||||
unsigned long page = scmi_info->param_page;
|
||||
unsigned long offset = scmi_info->param_offset;
|
||||
|
||||
/*
|
||||
* Channel will be released only once response has been
|
||||
@ -222,8 +238,13 @@ static int smc_send_message(struct scmi_chan_info *cinfo,
|
||||
|
||||
shmem_tx_prepare(scmi_info->shmem, xfer, cinfo);
|
||||
|
||||
arm_smccc_1_1_invoke(scmi_info->func_id, page, offset, 0, 0, 0, 0, 0,
|
||||
&res);
|
||||
if (scmi_info->cap_id != ULONG_MAX)
|
||||
arm_smccc_1_1_invoke(scmi_info->func_id, scmi_info->cap_id, 0,
|
||||
0, 0, 0, 0, 0, &res);
|
||||
else
|
||||
arm_smccc_1_1_invoke(scmi_info->func_id, scmi_info->param_page,
|
||||
scmi_info->param_offset, 0, 0, 0, 0, 0,
|
||||
&res);
|
||||
|
||||
/* Only SMCCC_RET_NOT_SUPPORTED is valid error code */
|
||||
if (res.a0) {
|
||||
|
@ -26,9 +26,12 @@
|
||||
#include <linux/list.h>
|
||||
#include <linux/mailbox_client.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/scpi_protocol.h>
|
||||
#include <linux/slab.h>
|
||||
@ -894,11 +897,6 @@ static int scpi_alloc_xfer_list(struct device *dev, struct scpi_chan *ch)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id legacy_scpi_of_match[] = {
|
||||
{.compatible = "arm,scpi-pre-1.0"},
|
||||
{},
|
||||
};
|
||||
|
||||
static const struct of_device_id shmem_of_match[] __maybe_unused = {
|
||||
{ .compatible = "amlogic,meson-gxbb-scp-shmem", },
|
||||
{ .compatible = "amlogic,meson-axg-scp-shmem", },
|
||||
@ -919,8 +917,7 @@ static int scpi_probe(struct platform_device *pdev)
|
||||
if (!scpi_drvinfo)
|
||||
return -ENOMEM;
|
||||
|
||||
if (of_match_device(legacy_scpi_of_match, &pdev->dev))
|
||||
scpi_drvinfo->is_legacy = true;
|
||||
scpi_drvinfo->is_legacy = !!device_get_match_data(dev);
|
||||
|
||||
count = of_count_phandle_with_args(np, "mboxes", "#mbox-cells");
|
||||
if (count < 0) {
|
||||
@ -1038,7 +1035,7 @@ static int scpi_probe(struct platform_device *pdev)
|
||||
|
||||
static const struct of_device_id scpi_of_match[] = {
|
||||
{.compatible = "arm,scpi"},
|
||||
{.compatible = "arm,scpi-pre-1.0"},
|
||||
{.compatible = "arm,scpi-pre-1.0", .data = (void *)1UL },
|
||||
{},
|
||||
};
|
||||
|
||||
|
@ -13,9 +13,10 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/slab.h>
|
||||
@ -67,7 +68,7 @@ static u32 meson_sm_get_cmd(const struct meson_sm_chip *chip,
|
||||
return cmd->smc_id;
|
||||
}
|
||||
|
||||
static u32 __meson_sm_call(u32 cmd, u32 arg0, u32 arg1, u32 arg2,
|
||||
static s32 __meson_sm_call(u32 cmd, u32 arg0, u32 arg1, u32 arg2,
|
||||
u32 arg3, u32 arg4)
|
||||
{
|
||||
struct arm_smccc_res res;
|
||||
@ -102,9 +103,10 @@ static void __iomem *meson_sm_map_shmem(u32 cmd_shmem, unsigned int size)
|
||||
* Return: 0 on success, a negative value on error
|
||||
*/
|
||||
int meson_sm_call(struct meson_sm_firmware *fw, unsigned int cmd_index,
|
||||
u32 *ret, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4)
|
||||
s32 *ret, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4)
|
||||
{
|
||||
u32 cmd, lret;
|
||||
u32 cmd;
|
||||
s32 lret;
|
||||
|
||||
if (!fw->chip)
|
||||
return -ENOENT;
|
||||
@ -143,7 +145,7 @@ int meson_sm_call_read(struct meson_sm_firmware *fw, void *buffer,
|
||||
unsigned int bsize, unsigned int cmd_index, u32 arg0,
|
||||
u32 arg1, u32 arg2, u32 arg3, u32 arg4)
|
||||
{
|
||||
u32 size;
|
||||
s32 size;
|
||||
int ret;
|
||||
|
||||
if (!fw->chip)
|
||||
@ -158,11 +160,16 @@ int meson_sm_call_read(struct meson_sm_firmware *fw, void *buffer,
|
||||
if (meson_sm_call(fw, cmd_index, &size, arg0, arg1, arg2, arg3, arg4) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (size > bsize)
|
||||
if (size < 0 || size > bsize)
|
||||
return -EINVAL;
|
||||
|
||||
ret = size;
|
||||
|
||||
/* In some cases (for example GET_CHIP_ID command),
|
||||
* SMC doesn't return the number of bytes read, even
|
||||
* though the bytes were actually read into sm_shmem_out.
|
||||
* So this check is needed.
|
||||
*/
|
||||
if (!size)
|
||||
size = bsize;
|
||||
|
||||
@ -192,7 +199,7 @@ int meson_sm_call_write(struct meson_sm_firmware *fw, void *buffer,
|
||||
unsigned int size, unsigned int cmd_index, u32 arg0,
|
||||
u32 arg1, u32 arg2, u32 arg3, u32 arg4)
|
||||
{
|
||||
u32 written;
|
||||
s32 written;
|
||||
|
||||
if (!fw->chip)
|
||||
return -ENOENT;
|
||||
@ -208,7 +215,7 @@ int meson_sm_call_write(struct meson_sm_firmware *fw, void *buffer,
|
||||
if (meson_sm_call(fw, cmd_index, &written, arg0, arg1, arg2, arg3, arg4) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!written)
|
||||
if (written <= 0 || written > size)
|
||||
return -EINVAL;
|
||||
|
||||
return written;
|
||||
@ -291,7 +298,7 @@ static int __init meson_sm_probe(struct platform_device *pdev)
|
||||
if (!fw)
|
||||
return -ENOMEM;
|
||||
|
||||
chip = of_match_device(meson_sm_ids, dev)->data;
|
||||
chip = device_get_match_data(dev);
|
||||
if (!chip)
|
||||
return -EINVAL;
|
||||
|
||||
|
56
drivers/firmware/qcom/Kconfig
Normal file
56
drivers/firmware/qcom/Kconfig
Normal file
@ -0,0 +1,56 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# For a description of the syntax of this configuration file,
|
||||
# see Documentation/kbuild/kconfig-language.rst.
|
||||
#
|
||||
|
||||
menu "Qualcomm firmware drivers"
|
||||
|
||||
config QCOM_SCM
|
||||
tristate
|
||||
|
||||
config QCOM_SCM_DOWNLOAD_MODE_DEFAULT
|
||||
bool "Qualcomm download mode enabled by default"
|
||||
depends on QCOM_SCM
|
||||
help
|
||||
A device with "download mode" enabled will upon an unexpected
|
||||
warm-restart enter a special debug mode that allows the user to
|
||||
"download" memory content over USB for offline postmortem analysis.
|
||||
The feature can be enabled/disabled on the kernel command line.
|
||||
|
||||
Say Y here to enable "download mode" by default.
|
||||
|
||||
config QCOM_QSEECOM
|
||||
bool "Qualcomm QSEECOM interface driver"
|
||||
depends on QCOM_SCM=y
|
||||
select AUXILIARY_BUS
|
||||
help
|
||||
Various Qualcomm SoCs have a Secure Execution Environment (SEE) running
|
||||
in the Trust Zone. This module provides an interface to that via the
|
||||
QSEECOM mechanism, using SCM calls.
|
||||
|
||||
The QSEECOM interface allows, among other things, access to applications
|
||||
running in the SEE. An example of such an application is 'uefisecapp',
|
||||
which is required to access UEFI variables on certain systems. If
|
||||
selected, the interface will also attempt to detect and register client
|
||||
devices for supported applications.
|
||||
|
||||
Select Y here to enable the QSEECOM interface driver.
|
||||
|
||||
config QCOM_QSEECOM_UEFISECAPP
|
||||
bool "Qualcomm SEE UEFI Secure App client driver"
|
||||
depends on QCOM_QSEECOM
|
||||
depends on EFI
|
||||
help
|
||||
Various Qualcomm SoCs do not allow direct access to EFI variables.
|
||||
Instead, these need to be accessed via the UEFI Secure Application
|
||||
(uefisecapp), residing in the Secure Execution Environment (SEE).
|
||||
|
||||
This module provides a client driver for uefisecapp, installing efivar
|
||||
operations to allow the kernel accessing EFI variables, and via that also
|
||||
provide user-space with access to EFI variables via efivarfs.
|
||||
|
||||
Select Y here to provide access to EFI variables on the aforementioned
|
||||
platforms.
|
||||
|
||||
endmenu
|
9
drivers/firmware/qcom/Makefile
Normal file
9
drivers/firmware/qcom/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Makefile for the linux kernel.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_QCOM_SCM) += qcom-scm.o
|
||||
qcom-scm-objs += qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o
|
||||
obj-$(CONFIG_QCOM_QSEECOM) += qcom_qseecom.o
|
||||
obj-$(CONFIG_QCOM_QSEECOM_UEFISECAPP) += qcom_qseecom_uefisecapp.o
|
120
drivers/firmware/qcom/qcom_qseecom.c
Normal file
120
drivers/firmware/qcom/qcom_qseecom.c
Normal file
@ -0,0 +1,120 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Driver for Qualcomm Secure Execution Environment (SEE) interface (QSEECOM).
|
||||
* Responsible for setting up and managing QSEECOM client devices.
|
||||
*
|
||||
* Copyright (C) 2023 Maximilian Luz <luzmaximilian@gmail.com>
|
||||
*/
|
||||
#include <linux/auxiliary_bus.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <linux/firmware/qcom/qcom_qseecom.h>
|
||||
#include <linux/firmware/qcom/qcom_scm.h>
|
||||
|
||||
struct qseecom_app_desc {
|
||||
const char *app_name;
|
||||
const char *dev_name;
|
||||
};
|
||||
|
||||
static void qseecom_client_release(struct device *dev)
|
||||
{
|
||||
struct qseecom_client *client;
|
||||
|
||||
client = container_of(dev, struct qseecom_client, aux_dev.dev);
|
||||
kfree(client);
|
||||
}
|
||||
|
||||
static void qseecom_client_remove(void *data)
|
||||
{
|
||||
struct qseecom_client *client = data;
|
||||
|
||||
auxiliary_device_delete(&client->aux_dev);
|
||||
auxiliary_device_uninit(&client->aux_dev);
|
||||
}
|
||||
|
||||
static int qseecom_client_register(struct platform_device *qseecom_dev,
|
||||
const struct qseecom_app_desc *desc)
|
||||
{
|
||||
struct qseecom_client *client;
|
||||
u32 app_id;
|
||||
int ret;
|
||||
|
||||
/* Try to find the app ID, skip device if not found */
|
||||
ret = qcom_scm_qseecom_app_get_id(desc->app_name, &app_id);
|
||||
if (ret)
|
||||
return ret == -ENOENT ? 0 : ret;
|
||||
|
||||
dev_info(&qseecom_dev->dev, "setting up client for %s\n", desc->app_name);
|
||||
|
||||
/* Allocate and set-up the client device */
|
||||
client = kzalloc(sizeof(*client), GFP_KERNEL);
|
||||
if (!client)
|
||||
return -ENOMEM;
|
||||
|
||||
client->aux_dev.name = desc->dev_name;
|
||||
client->aux_dev.dev.parent = &qseecom_dev->dev;
|
||||
client->aux_dev.dev.release = qseecom_client_release;
|
||||
client->app_id = app_id;
|
||||
|
||||
ret = auxiliary_device_init(&client->aux_dev);
|
||||
if (ret) {
|
||||
kfree(client);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = auxiliary_device_add(&client->aux_dev);
|
||||
if (ret) {
|
||||
auxiliary_device_uninit(&client->aux_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_add_action_or_reset(&qseecom_dev->dev, qseecom_client_remove, client);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* List of supported applications. One client device will be created per entry,
|
||||
* assuming the app has already been loaded (usually by firmware bootloaders)
|
||||
* and its ID can be queried successfully.
|
||||
*/
|
||||
static const struct qseecom_app_desc qcom_qseecom_apps[] = {
|
||||
{ "qcom.tz.uefisecapp", "uefisecapp" },
|
||||
};
|
||||
|
||||
static int qcom_qseecom_probe(struct platform_device *qseecom_dev)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
/* Set up client devices for each base application */
|
||||
for (i = 0; i < ARRAY_SIZE(qcom_qseecom_apps); i++) {
|
||||
ret = qseecom_client_register(qseecom_dev, &qcom_qseecom_apps[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver qcom_qseecom_driver = {
|
||||
.driver = {
|
||||
.name = "qcom_qseecom",
|
||||
},
|
||||
.probe = qcom_qseecom_probe,
|
||||
};
|
||||
|
||||
static int __init qcom_qseecom_init(void)
|
||||
{
|
||||
return platform_driver_register(&qcom_qseecom_driver);
|
||||
}
|
||||
subsys_initcall(qcom_qseecom_init);
|
||||
|
||||
MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
|
||||
MODULE_DESCRIPTION("Driver for the Qualcomm SEE (QSEECOM) interface");
|
||||
MODULE_LICENSE("GPL");
|
871
drivers/firmware/qcom/qcom_qseecom_uefisecapp.c
Normal file
871
drivers/firmware/qcom/qcom_qseecom_uefisecapp.c
Normal file
@ -0,0 +1,871 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Client driver for Qualcomm UEFI Secure Application (qcom.tz.uefisecapp).
|
||||
* Provides access to UEFI variables on platforms where they are secured by the
|
||||
* aforementioned Secure Execution Environment (SEE) application.
|
||||
*
|
||||
* Copyright (C) 2023 Maximilian Luz <luzmaximilian@gmail.com>
|
||||
*/
|
||||
|
||||
#include <linux/efi.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/ucs2_string.h>
|
||||
|
||||
#include <linux/firmware/qcom/qcom_qseecom.h>
|
||||
|
||||
/* -- Qualcomm "uefisecapp" interface definitions. -------------------------- */
|
||||
|
||||
/* Maximum length of name string with null-terminator */
|
||||
#define QSEE_MAX_NAME_LEN 1024
|
||||
|
||||
#define QSEE_CMD_UEFI(x) (0x8000 | (x))
|
||||
#define QSEE_CMD_UEFI_GET_VARIABLE QSEE_CMD_UEFI(0)
|
||||
#define QSEE_CMD_UEFI_SET_VARIABLE QSEE_CMD_UEFI(1)
|
||||
#define QSEE_CMD_UEFI_GET_NEXT_VARIABLE QSEE_CMD_UEFI(2)
|
||||
#define QSEE_CMD_UEFI_QUERY_VARIABLE_INFO QSEE_CMD_UEFI(3)
|
||||
|
||||
/**
|
||||
* struct qsee_req_uefi_get_variable - Request for GetVariable command.
|
||||
* @command_id: The ID of the command. Must be %QSEE_CMD_UEFI_GET_VARIABLE.
|
||||
* @length: Length of the request in bytes, including this struct and any
|
||||
* parameters (name, GUID) stored after it as well as any padding
|
||||
* thereof for alignment.
|
||||
* @name_offset: Offset from the start of this struct to where the variable
|
||||
* name is stored (as utf-16 string), in bytes.
|
||||
* @name_size: Size of the name parameter in bytes, including null-terminator.
|
||||
* @guid_offset: Offset from the start of this struct to where the GUID
|
||||
* parameter is stored, in bytes.
|
||||
* @guid_size: Size of the GUID parameter in bytes, i.e. sizeof(efi_guid_t).
|
||||
* @data_size: Size of the output buffer, in bytes.
|
||||
*/
|
||||
struct qsee_req_uefi_get_variable {
|
||||
u32 command_id;
|
||||
u32 length;
|
||||
u32 name_offset;
|
||||
u32 name_size;
|
||||
u32 guid_offset;
|
||||
u32 guid_size;
|
||||
u32 data_size;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct qsee_rsp_uefi_get_variable - Response for GetVariable command.
|
||||
* @command_id: The ID of the command. Should be %QSEE_CMD_UEFI_GET_VARIABLE.
|
||||
* @length: Length of the response in bytes, including this struct and the
|
||||
* returned data.
|
||||
* @status: Status of this command.
|
||||
* @attributes: EFI variable attributes.
|
||||
* @data_offset: Offset from the start of this struct to where the data is
|
||||
* stored, in bytes.
|
||||
* @data_size: Size of the returned data, in bytes. In case status indicates
|
||||
* that the buffer is too small, this will be the size required
|
||||
* to store the EFI variable data.
|
||||
*/
|
||||
struct qsee_rsp_uefi_get_variable {
|
||||
u32 command_id;
|
||||
u32 length;
|
||||
u32 status;
|
||||
u32 attributes;
|
||||
u32 data_offset;
|
||||
u32 data_size;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct qsee_req_uefi_set_variable - Request for the SetVariable command.
|
||||
* @command_id: The ID of the command. Must be %QSEE_CMD_UEFI_SET_VARIABLE.
|
||||
* @length: Length of the request in bytes, including this struct and any
|
||||
* parameters (name, GUID, data) stored after it as well as any
|
||||
* padding thereof required for alignment.
|
||||
* @name_offset: Offset from the start of this struct to where the variable
|
||||
* name is stored (as utf-16 string), in bytes.
|
||||
* @name_size: Size of the name parameter in bytes, including null-terminator.
|
||||
* @guid_offset: Offset from the start of this struct to where the GUID
|
||||
* parameter is stored, in bytes.
|
||||
* @guid_size: Size of the GUID parameter in bytes, i.e. sizeof(efi_guid_t).
|
||||
* @attributes: The EFI variable attributes to set for this variable.
|
||||
* @data_offset: Offset from the start of this struct to where the EFI variable
|
||||
* data is stored, in bytes.
|
||||
* @data_size: Size of EFI variable data, in bytes.
|
||||
*
|
||||
*/
|
||||
struct qsee_req_uefi_set_variable {
|
||||
u32 command_id;
|
||||
u32 length;
|
||||
u32 name_offset;
|
||||
u32 name_size;
|
||||
u32 guid_offset;
|
||||
u32 guid_size;
|
||||
u32 attributes;
|
||||
u32 data_offset;
|
||||
u32 data_size;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct qsee_rsp_uefi_set_variable - Response for the SetVariable command.
|
||||
* @command_id: The ID of the command. Should be %QSEE_CMD_UEFI_SET_VARIABLE.
|
||||
* @length: The length of this response, i.e. the size of this struct in
|
||||
* bytes.
|
||||
* @status: Status of this command.
|
||||
* @_unknown1: Unknown response field.
|
||||
* @_unknown2: Unknown response field.
|
||||
*/
|
||||
struct qsee_rsp_uefi_set_variable {
|
||||
u32 command_id;
|
||||
u32 length;
|
||||
u32 status;
|
||||
u32 _unknown1;
|
||||
u32 _unknown2;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct qsee_req_uefi_get_next_variable - Request for the
|
||||
* GetNextVariableName command.
|
||||
* @command_id: The ID of the command. Must be
|
||||
* %QSEE_CMD_UEFI_GET_NEXT_VARIABLE.
|
||||
* @length: Length of the request in bytes, including this struct and any
|
||||
* parameters (name, GUID) stored after it as well as any padding
|
||||
* thereof for alignment.
|
||||
* @guid_offset: Offset from the start of this struct to where the GUID
|
||||
* parameter is stored, in bytes.
|
||||
* @guid_size: Size of the GUID parameter in bytes, i.e. sizeof(efi_guid_t).
|
||||
* @name_offset: Offset from the start of this struct to where the variable
|
||||
* name is stored (as utf-16 string), in bytes.
|
||||
* @name_size: Size of the name parameter in bytes, including null-terminator.
|
||||
*/
|
||||
struct qsee_req_uefi_get_next_variable {
|
||||
u32 command_id;
|
||||
u32 length;
|
||||
u32 guid_offset;
|
||||
u32 guid_size;
|
||||
u32 name_offset;
|
||||
u32 name_size;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct qsee_rsp_uefi_get_next_variable - Response for the
|
||||
* GetNextVariableName command.
|
||||
* @command_id: The ID of the command. Should be
|
||||
* %QSEE_CMD_UEFI_GET_NEXT_VARIABLE.
|
||||
* @length: Length of the response in bytes, including this struct and any
|
||||
* parameters (name, GUID) stored after it as well as any padding
|
||||
* thereof for alignment.
|
||||
* @status: Status of this command.
|
||||
* @guid_offset: Offset from the start of this struct to where the GUID
|
||||
* parameter is stored, in bytes.
|
||||
* @guid_size: Size of the GUID parameter in bytes, i.e. sizeof(efi_guid_t).
|
||||
* @name_offset: Offset from the start of this struct to where the variable
|
||||
* name is stored (as utf-16 string), in bytes.
|
||||
* @name_size: Size of the name parameter in bytes, including null-terminator.
|
||||
*/
|
||||
struct qsee_rsp_uefi_get_next_variable {
|
||||
u32 command_id;
|
||||
u32 length;
|
||||
u32 status;
|
||||
u32 guid_offset;
|
||||
u32 guid_size;
|
||||
u32 name_offset;
|
||||
u32 name_size;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct qsee_req_uefi_query_variable_info - Response for the
|
||||
* GetNextVariableName command.
|
||||
* @command_id: The ID of the command. Must be
|
||||
* %QSEE_CMD_UEFI_QUERY_VARIABLE_INFO.
|
||||
* @length: The length of this request, i.e. the size of this struct in
|
||||
* bytes.
|
||||
* @attributes: The storage attributes to query the info for.
|
||||
*/
|
||||
struct qsee_req_uefi_query_variable_info {
|
||||
u32 command_id;
|
||||
u32 length;
|
||||
u32 attributes;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct qsee_rsp_uefi_query_variable_info - Response for the
|
||||
* GetNextVariableName command.
|
||||
* @command_id: The ID of the command. Must be
|
||||
* %QSEE_CMD_UEFI_QUERY_VARIABLE_INFO.
|
||||
* @length: The length of this response, i.e. the size of this
|
||||
* struct in bytes.
|
||||
* @status: Status of this command.
|
||||
* @_pad: Padding.
|
||||
* @storage_space: Full storage space size, in bytes.
|
||||
* @remaining_space: Free storage space available, in bytes.
|
||||
* @max_variable_size: Maximum variable data size, in bytes.
|
||||
*/
|
||||
struct qsee_rsp_uefi_query_variable_info {
|
||||
u32 command_id;
|
||||
u32 length;
|
||||
u32 status;
|
||||
u32 _pad;
|
||||
u64 storage_space;
|
||||
u64 remaining_space;
|
||||
u64 max_variable_size;
|
||||
} __packed;
|
||||
|
||||
/* -- Alignment helpers ----------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Helper macro to ensure proper alignment of types (fields and arrays) when
|
||||
* stored in some (contiguous) buffer.
|
||||
*
|
||||
* Note: The driver from which this one has been reverse-engineered expects an
|
||||
* alignment of 8 bytes (64 bits) for GUIDs. Our definition of efi_guid_t,
|
||||
* however, has an alignment of 4 byte (32 bits). So far, this seems to work
|
||||
* fine here. See also the comment on the typedef of efi_guid_t.
|
||||
*/
|
||||
#define qcuefi_buf_align_fields(fields...) \
|
||||
({ \
|
||||
size_t __len = 0; \
|
||||
fields \
|
||||
__len; \
|
||||
})
|
||||
|
||||
#define __field_impl(size, align, offset) \
|
||||
({ \
|
||||
size_t *__offset = (offset); \
|
||||
size_t __aligned; \
|
||||
\
|
||||
__aligned = ALIGN(__len, align); \
|
||||
__len = __aligned + (size); \
|
||||
\
|
||||
if (__offset) \
|
||||
*__offset = __aligned; \
|
||||
});
|
||||
|
||||
#define __array_offs(type, count, offset) \
|
||||
__field_impl(sizeof(type) * (count), __alignof__(type), offset)
|
||||
|
||||
#define __array(type, count) __array_offs(type, count, NULL)
|
||||
#define __field_offs(type, offset) __array_offs(type, 1, offset)
|
||||
#define __field(type) __array_offs(type, 1, NULL)
|
||||
|
||||
/* -- UEFI app interface. --------------------------------------------------- */
|
||||
|
||||
struct qcuefi_client {
|
||||
struct qseecom_client *client;
|
||||
struct efivars efivars;
|
||||
};
|
||||
|
||||
static struct device *qcuefi_dev(struct qcuefi_client *qcuefi)
|
||||
{
|
||||
return &qcuefi->client->aux_dev.dev;
|
||||
}
|
||||
|
||||
static efi_status_t qsee_uefi_status_to_efi(u32 status)
|
||||
{
|
||||
u64 category = status & 0xf0000000;
|
||||
u64 code = status & 0x0fffffff;
|
||||
|
||||
return category << (BITS_PER_LONG - 32) | code;
|
||||
}
|
||||
|
||||
static efi_status_t qsee_uefi_get_variable(struct qcuefi_client *qcuefi, const efi_char16_t *name,
|
||||
const efi_guid_t *guid, u32 *attributes,
|
||||
unsigned long *data_size, void *data)
|
||||
{
|
||||
struct qsee_req_uefi_get_variable *req_data;
|
||||
struct qsee_rsp_uefi_get_variable *rsp_data;
|
||||
unsigned long buffer_size = *data_size;
|
||||
efi_status_t efi_status = EFI_SUCCESS;
|
||||
unsigned long name_length;
|
||||
size_t guid_offs;
|
||||
size_t name_offs;
|
||||
size_t req_size;
|
||||
size_t rsp_size;
|
||||
ssize_t status;
|
||||
|
||||
if (!name || !guid)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
name_length = ucs2_strnlen(name, QSEE_MAX_NAME_LEN) + 1;
|
||||
if (name_length > QSEE_MAX_NAME_LEN)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
if (buffer_size && !data)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
req_size = qcuefi_buf_align_fields(
|
||||
__field(*req_data)
|
||||
__array_offs(*name, name_length, &name_offs)
|
||||
__field_offs(*guid, &guid_offs)
|
||||
);
|
||||
|
||||
rsp_size = qcuefi_buf_align_fields(
|
||||
__field(*rsp_data)
|
||||
__array(u8, buffer_size)
|
||||
);
|
||||
|
||||
req_data = kzalloc(req_size, GFP_KERNEL);
|
||||
if (!req_data) {
|
||||
efi_status = EFI_OUT_OF_RESOURCES;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rsp_data = kzalloc(rsp_size, GFP_KERNEL);
|
||||
if (!rsp_data) {
|
||||
efi_status = EFI_OUT_OF_RESOURCES;
|
||||
goto out_free_req;
|
||||
}
|
||||
|
||||
req_data->command_id = QSEE_CMD_UEFI_GET_VARIABLE;
|
||||
req_data->data_size = buffer_size;
|
||||
req_data->name_offset = name_offs;
|
||||
req_data->name_size = name_length * sizeof(*name);
|
||||
req_data->guid_offset = guid_offs;
|
||||
req_data->guid_size = sizeof(*guid);
|
||||
req_data->length = req_size;
|
||||
|
||||
status = ucs2_strscpy(((void *)req_data) + req_data->name_offset, name, name_length);
|
||||
if (status < 0)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size);
|
||||
|
||||
status = qcom_qseecom_app_send(qcuefi->client, req_data, req_size, rsp_data, rsp_size);
|
||||
if (status) {
|
||||
efi_status = EFI_DEVICE_ERROR;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (rsp_data->command_id != QSEE_CMD_UEFI_GET_VARIABLE) {
|
||||
efi_status = EFI_DEVICE_ERROR;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (rsp_data->length < sizeof(*rsp_data)) {
|
||||
efi_status = EFI_DEVICE_ERROR;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (rsp_data->status) {
|
||||
dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n",
|
||||
__func__, rsp_data->status);
|
||||
efi_status = qsee_uefi_status_to_efi(rsp_data->status);
|
||||
|
||||
/* Update size and attributes in case buffer is too small. */
|
||||
if (efi_status == EFI_BUFFER_TOO_SMALL) {
|
||||
*data_size = rsp_data->data_size;
|
||||
if (attributes)
|
||||
*attributes = rsp_data->attributes;
|
||||
}
|
||||
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (rsp_data->length > rsp_size) {
|
||||
efi_status = EFI_DEVICE_ERROR;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (rsp_data->data_offset + rsp_data->data_size > rsp_data->length) {
|
||||
efi_status = EFI_DEVICE_ERROR;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: We need to set attributes and data size even if the buffer is
|
||||
* too small and we won't copy any data. This is described in spec, so
|
||||
* that callers can either allocate a buffer properly (with two calls
|
||||
* to this function) or just read back attributes withouth having to
|
||||
* deal with that.
|
||||
*
|
||||
* Specifically:
|
||||
* - If we have a buffer size of zero and no buffer, just return the
|
||||
* attributes, required size, and indicate success.
|
||||
* - If the buffer size is nonzero but too small, indicate that as an
|
||||
* error.
|
||||
* - Otherwise, we are good to copy the data.
|
||||
*
|
||||
* Note that we have already ensured above that the buffer pointer is
|
||||
* non-NULL if its size is nonzero.
|
||||
*/
|
||||
*data_size = rsp_data->data_size;
|
||||
if (attributes)
|
||||
*attributes = rsp_data->attributes;
|
||||
|
||||
if (buffer_size == 0 && !data) {
|
||||
efi_status = EFI_SUCCESS;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (buffer_size < rsp_data->data_size) {
|
||||
efi_status = EFI_BUFFER_TOO_SMALL;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
memcpy(data, ((void *)rsp_data) + rsp_data->data_offset, rsp_data->data_size);
|
||||
|
||||
out_free:
|
||||
kfree(rsp_data);
|
||||
out_free_req:
|
||||
kfree(req_data);
|
||||
out:
|
||||
return efi_status;
|
||||
}
|
||||
|
||||
static efi_status_t qsee_uefi_set_variable(struct qcuefi_client *qcuefi, const efi_char16_t *name,
|
||||
const efi_guid_t *guid, u32 attributes,
|
||||
unsigned long data_size, const void *data)
|
||||
{
|
||||
struct qsee_req_uefi_set_variable *req_data;
|
||||
struct qsee_rsp_uefi_set_variable *rsp_data;
|
||||
efi_status_t efi_status = EFI_SUCCESS;
|
||||
unsigned long name_length;
|
||||
size_t name_offs;
|
||||
size_t guid_offs;
|
||||
size_t data_offs;
|
||||
size_t req_size;
|
||||
ssize_t status;
|
||||
|
||||
if (!name || !guid)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
name_length = ucs2_strnlen(name, QSEE_MAX_NAME_LEN) + 1;
|
||||
if (name_length > QSEE_MAX_NAME_LEN)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
/*
|
||||
* Make sure we have some data if data_size is nonzero. Note that using
|
||||
* a size of zero is a valid use-case described in spec and deletes the
|
||||
* variable.
|
||||
*/
|
||||
if (data_size && !data)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
req_size = qcuefi_buf_align_fields(
|
||||
__field(*req_data)
|
||||
__array_offs(*name, name_length, &name_offs)
|
||||
__field_offs(*guid, &guid_offs)
|
||||
__array_offs(u8, data_size, &data_offs)
|
||||
);
|
||||
|
||||
req_data = kzalloc(req_size, GFP_KERNEL);
|
||||
if (!req_data) {
|
||||
efi_status = EFI_OUT_OF_RESOURCES;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rsp_data = kzalloc(sizeof(*rsp_data), GFP_KERNEL);
|
||||
if (!rsp_data) {
|
||||
efi_status = EFI_OUT_OF_RESOURCES;
|
||||
goto out_free_req;
|
||||
}
|
||||
|
||||
req_data->command_id = QSEE_CMD_UEFI_SET_VARIABLE;
|
||||
req_data->attributes = attributes;
|
||||
req_data->name_offset = name_offs;
|
||||
req_data->name_size = name_length * sizeof(*name);
|
||||
req_data->guid_offset = guid_offs;
|
||||
req_data->guid_size = sizeof(*guid);
|
||||
req_data->data_offset = data_offs;
|
||||
req_data->data_size = data_size;
|
||||
req_data->length = req_size;
|
||||
|
||||
status = ucs2_strscpy(((void *)req_data) + req_data->name_offset, name, name_length);
|
||||
if (status < 0)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size);
|
||||
|
||||
if (data_size)
|
||||
memcpy(((void *)req_data) + req_data->data_offset, data, req_data->data_size);
|
||||
|
||||
status = qcom_qseecom_app_send(qcuefi->client, req_data, req_size, rsp_data,
|
||||
sizeof(*rsp_data));
|
||||
if (status) {
|
||||
efi_status = EFI_DEVICE_ERROR;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (rsp_data->command_id != QSEE_CMD_UEFI_SET_VARIABLE) {
|
||||
efi_status = EFI_DEVICE_ERROR;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (rsp_data->length != sizeof(*rsp_data)) {
|
||||
efi_status = EFI_DEVICE_ERROR;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (rsp_data->status) {
|
||||
dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n",
|
||||
__func__, rsp_data->status);
|
||||
efi_status = qsee_uefi_status_to_efi(rsp_data->status);
|
||||
}
|
||||
|
||||
out_free:
|
||||
kfree(rsp_data);
|
||||
out_free_req:
|
||||
kfree(req_data);
|
||||
out:
|
||||
return efi_status;
|
||||
}
|
||||
|
||||
static efi_status_t qsee_uefi_get_next_variable(struct qcuefi_client *qcuefi,
|
||||
unsigned long *name_size, efi_char16_t *name,
|
||||
efi_guid_t *guid)
|
||||
{
|
||||
struct qsee_req_uefi_get_next_variable *req_data;
|
||||
struct qsee_rsp_uefi_get_next_variable *rsp_data;
|
||||
efi_status_t efi_status = EFI_SUCCESS;
|
||||
size_t guid_offs;
|
||||
size_t name_offs;
|
||||
size_t req_size;
|
||||
size_t rsp_size;
|
||||
ssize_t status;
|
||||
|
||||
if (!name_size || !name || !guid)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
if (*name_size == 0)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
req_size = qcuefi_buf_align_fields(
|
||||
__field(*req_data)
|
||||
__field_offs(*guid, &guid_offs)
|
||||
__array_offs(*name, *name_size / sizeof(*name), &name_offs)
|
||||
);
|
||||
|
||||
rsp_size = qcuefi_buf_align_fields(
|
||||
__field(*rsp_data)
|
||||
__field(*guid)
|
||||
__array(*name, *name_size / sizeof(*name))
|
||||
);
|
||||
|
||||
req_data = kzalloc(req_size, GFP_KERNEL);
|
||||
if (!req_data) {
|
||||
efi_status = EFI_OUT_OF_RESOURCES;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rsp_data = kzalloc(rsp_size, GFP_KERNEL);
|
||||
if (!rsp_data) {
|
||||
efi_status = EFI_OUT_OF_RESOURCES;
|
||||
goto out_free_req;
|
||||
}
|
||||
|
||||
req_data->command_id = QSEE_CMD_UEFI_GET_NEXT_VARIABLE;
|
||||
req_data->guid_offset = guid_offs;
|
||||
req_data->guid_size = sizeof(*guid);
|
||||
req_data->name_offset = name_offs;
|
||||
req_data->name_size = *name_size;
|
||||
req_data->length = req_size;
|
||||
|
||||
memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size);
|
||||
status = ucs2_strscpy(((void *)req_data) + req_data->name_offset, name,
|
||||
*name_size / sizeof(*name));
|
||||
if (status < 0)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
status = qcom_qseecom_app_send(qcuefi->client, req_data, req_size, rsp_data, rsp_size);
|
||||
if (status) {
|
||||
efi_status = EFI_DEVICE_ERROR;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (rsp_data->command_id != QSEE_CMD_UEFI_GET_NEXT_VARIABLE) {
|
||||
efi_status = EFI_DEVICE_ERROR;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (rsp_data->length < sizeof(*rsp_data)) {
|
||||
efi_status = EFI_DEVICE_ERROR;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (rsp_data->status) {
|
||||
dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n",
|
||||
__func__, rsp_data->status);
|
||||
efi_status = qsee_uefi_status_to_efi(rsp_data->status);
|
||||
|
||||
/*
|
||||
* If the buffer to hold the name is too small, update the
|
||||
* name_size with the required size, so that callers can
|
||||
* reallocate it accordingly.
|
||||
*/
|
||||
if (efi_status == EFI_BUFFER_TOO_SMALL)
|
||||
*name_size = rsp_data->name_size;
|
||||
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (rsp_data->length > rsp_size) {
|
||||
efi_status = EFI_DEVICE_ERROR;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (rsp_data->name_offset + rsp_data->name_size > rsp_data->length) {
|
||||
efi_status = EFI_DEVICE_ERROR;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (rsp_data->guid_offset + rsp_data->guid_size > rsp_data->length) {
|
||||
efi_status = EFI_DEVICE_ERROR;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (rsp_data->name_size > *name_size) {
|
||||
*name_size = rsp_data->name_size;
|
||||
efi_status = EFI_BUFFER_TOO_SMALL;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (rsp_data->guid_size != sizeof(*guid)) {
|
||||
efi_status = EFI_DEVICE_ERROR;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
memcpy(guid, ((void *)rsp_data) + rsp_data->guid_offset, rsp_data->guid_size);
|
||||
status = ucs2_strscpy(name, ((void *)rsp_data) + rsp_data->name_offset,
|
||||
rsp_data->name_size / sizeof(*name));
|
||||
*name_size = rsp_data->name_size;
|
||||
|
||||
if (status < 0) {
|
||||
/*
|
||||
* Return EFI_DEVICE_ERROR here because the buffer size should
|
||||
* have already been validated above, causing this function to
|
||||
* bail with EFI_BUFFER_TOO_SMALL.
|
||||
*/
|
||||
return EFI_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
out_free:
|
||||
kfree(rsp_data);
|
||||
out_free_req:
|
||||
kfree(req_data);
|
||||
out:
|
||||
return efi_status;
|
||||
}
|
||||
|
||||
static efi_status_t qsee_uefi_query_variable_info(struct qcuefi_client *qcuefi, u32 attr,
|
||||
u64 *storage_space, u64 *remaining_space,
|
||||
u64 *max_variable_size)
|
||||
{
|
||||
struct qsee_req_uefi_query_variable_info *req_data;
|
||||
struct qsee_rsp_uefi_query_variable_info *rsp_data;
|
||||
efi_status_t efi_status = EFI_SUCCESS;
|
||||
int status;
|
||||
|
||||
req_data = kzalloc(sizeof(*req_data), GFP_KERNEL);
|
||||
if (!req_data) {
|
||||
efi_status = EFI_OUT_OF_RESOURCES;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rsp_data = kzalloc(sizeof(*rsp_data), GFP_KERNEL);
|
||||
if (!rsp_data) {
|
||||
efi_status = EFI_OUT_OF_RESOURCES;
|
||||
goto out_free_req;
|
||||
}
|
||||
|
||||
req_data->command_id = QSEE_CMD_UEFI_QUERY_VARIABLE_INFO;
|
||||
req_data->attributes = attr;
|
||||
req_data->length = sizeof(*req_data);
|
||||
|
||||
status = qcom_qseecom_app_send(qcuefi->client, req_data, sizeof(*req_data), rsp_data,
|
||||
sizeof(*rsp_data));
|
||||
if (status) {
|
||||
efi_status = EFI_DEVICE_ERROR;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (rsp_data->command_id != QSEE_CMD_UEFI_QUERY_VARIABLE_INFO) {
|
||||
efi_status = EFI_DEVICE_ERROR;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (rsp_data->length != sizeof(*rsp_data)) {
|
||||
efi_status = EFI_DEVICE_ERROR;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (rsp_data->status) {
|
||||
dev_dbg(qcuefi_dev(qcuefi), "%s: uefisecapp error: 0x%x\n",
|
||||
__func__, rsp_data->status);
|
||||
efi_status = qsee_uefi_status_to_efi(rsp_data->status);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (storage_space)
|
||||
*storage_space = rsp_data->storage_space;
|
||||
|
||||
if (remaining_space)
|
||||
*remaining_space = rsp_data->remaining_space;
|
||||
|
||||
if (max_variable_size)
|
||||
*max_variable_size = rsp_data->max_variable_size;
|
||||
|
||||
out_free:
|
||||
kfree(rsp_data);
|
||||
out_free_req:
|
||||
kfree(req_data);
|
||||
out:
|
||||
return efi_status;
|
||||
}
|
||||
|
||||
/* -- Global efivar interface. ---------------------------------------------- */
|
||||
|
||||
static struct qcuefi_client *__qcuefi;
|
||||
static DEFINE_MUTEX(__qcuefi_lock);
|
||||
|
||||
static int qcuefi_set_reference(struct qcuefi_client *qcuefi)
|
||||
{
|
||||
mutex_lock(&__qcuefi_lock);
|
||||
|
||||
if (qcuefi && __qcuefi) {
|
||||
mutex_unlock(&__qcuefi_lock);
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
__qcuefi = qcuefi;
|
||||
|
||||
mutex_unlock(&__qcuefi_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct qcuefi_client *qcuefi_acquire(void)
|
||||
{
|
||||
mutex_lock(&__qcuefi_lock);
|
||||
return __qcuefi;
|
||||
}
|
||||
|
||||
static void qcuefi_release(void)
|
||||
{
|
||||
mutex_unlock(&__qcuefi_lock);
|
||||
}
|
||||
|
||||
static efi_status_t qcuefi_get_variable(efi_char16_t *name, efi_guid_t *vendor, u32 *attr,
|
||||
unsigned long *data_size, void *data)
|
||||
{
|
||||
struct qcuefi_client *qcuefi;
|
||||
efi_status_t status;
|
||||
|
||||
qcuefi = qcuefi_acquire();
|
||||
if (!qcuefi)
|
||||
return EFI_NOT_READY;
|
||||
|
||||
status = qsee_uefi_get_variable(qcuefi, name, vendor, attr, data_size, data);
|
||||
|
||||
qcuefi_release();
|
||||
return status;
|
||||
}
|
||||
|
||||
static efi_status_t qcuefi_set_variable(efi_char16_t *name, efi_guid_t *vendor,
|
||||
u32 attr, unsigned long data_size, void *data)
|
||||
{
|
||||
struct qcuefi_client *qcuefi;
|
||||
efi_status_t status;
|
||||
|
||||
qcuefi = qcuefi_acquire();
|
||||
if (!qcuefi)
|
||||
return EFI_NOT_READY;
|
||||
|
||||
status = qsee_uefi_set_variable(qcuefi, name, vendor, attr, data_size, data);
|
||||
|
||||
qcuefi_release();
|
||||
return status;
|
||||
}
|
||||
|
||||
static efi_status_t qcuefi_get_next_variable(unsigned long *name_size, efi_char16_t *name,
|
||||
efi_guid_t *vendor)
|
||||
{
|
||||
struct qcuefi_client *qcuefi;
|
||||
efi_status_t status;
|
||||
|
||||
qcuefi = qcuefi_acquire();
|
||||
if (!qcuefi)
|
||||
return EFI_NOT_READY;
|
||||
|
||||
status = qsee_uefi_get_next_variable(qcuefi, name_size, name, vendor);
|
||||
|
||||
qcuefi_release();
|
||||
return status;
|
||||
}
|
||||
|
||||
static efi_status_t qcuefi_query_variable_info(u32 attr, u64 *storage_space, u64 *remaining_space,
|
||||
u64 *max_variable_size)
|
||||
{
|
||||
struct qcuefi_client *qcuefi;
|
||||
efi_status_t status;
|
||||
|
||||
qcuefi = qcuefi_acquire();
|
||||
if (!qcuefi)
|
||||
return EFI_NOT_READY;
|
||||
|
||||
status = qsee_uefi_query_variable_info(qcuefi, attr, storage_space, remaining_space,
|
||||
max_variable_size);
|
||||
|
||||
qcuefi_release();
|
||||
return status;
|
||||
}
|
||||
|
||||
static const struct efivar_operations qcom_efivar_ops = {
|
||||
.get_variable = qcuefi_get_variable,
|
||||
.set_variable = qcuefi_set_variable,
|
||||
.get_next_variable = qcuefi_get_next_variable,
|
||||
.query_variable_info = qcuefi_query_variable_info,
|
||||
};
|
||||
|
||||
/* -- Driver setup. --------------------------------------------------------- */
|
||||
|
||||
static int qcom_uefisecapp_probe(struct auxiliary_device *aux_dev,
|
||||
const struct auxiliary_device_id *aux_dev_id)
|
||||
{
|
||||
struct qcuefi_client *qcuefi;
|
||||
int status;
|
||||
|
||||
qcuefi = devm_kzalloc(&aux_dev->dev, sizeof(*qcuefi), GFP_KERNEL);
|
||||
if (!qcuefi)
|
||||
return -ENOMEM;
|
||||
|
||||
qcuefi->client = container_of(aux_dev, struct qseecom_client, aux_dev);
|
||||
|
||||
auxiliary_set_drvdata(aux_dev, qcuefi);
|
||||
status = qcuefi_set_reference(qcuefi);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
status = efivars_register(&qcuefi->efivars, &qcom_efivar_ops);
|
||||
if (status)
|
||||
qcuefi_set_reference(NULL);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void qcom_uefisecapp_remove(struct auxiliary_device *aux_dev)
|
||||
{
|
||||
struct qcuefi_client *qcuefi = auxiliary_get_drvdata(aux_dev);
|
||||
|
||||
efivars_unregister(&qcuefi->efivars);
|
||||
qcuefi_set_reference(NULL);
|
||||
}
|
||||
|
||||
static const struct auxiliary_device_id qcom_uefisecapp_id_table[] = {
|
||||
{ .name = "qcom_qseecom.uefisecapp" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(auxiliary, qcom_uefisecapp_id_table);
|
||||
|
||||
static struct auxiliary_driver qcom_uefisecapp_driver = {
|
||||
.probe = qcom_uefisecapp_probe,
|
||||
.remove = qcom_uefisecapp_remove,
|
||||
.id_table = qcom_uefisecapp_id_table,
|
||||
.driver = {
|
||||
.name = "qcom_qseecom_uefisecapp",
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
},
|
||||
};
|
||||
module_auxiliary_driver(qcom_uefisecapp_driver);
|
||||
|
||||
MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
|
||||
MODULE_DESCRIPTION("Client driver for Qualcomm SEE UEFI Secure App");
|
||||
MODULE_LICENSE("GPL");
|
@ -2,24 +2,25 @@
|
||||
/* Copyright (c) 2010,2015,2019 The Linux Foundation. All rights reserved.
|
||||
* Copyright (C) 2015 Linaro Ltd.
|
||||
*/
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/interconnect.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/firmware/qcom/qcom_scm.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interconnect.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset-controller.h>
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "qcom_scm.h"
|
||||
|
||||
@ -55,6 +56,53 @@ struct qcom_scm_mem_map_info {
|
||||
__le64 mem_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct qcom_scm_qseecom_resp - QSEECOM SCM call response.
|
||||
* @result: Result or status of the SCM call. See &enum qcom_scm_qseecom_result.
|
||||
* @resp_type: Type of the response. See &enum qcom_scm_qseecom_resp_type.
|
||||
* @data: Response data. The type of this data is given in @resp_type.
|
||||
*/
|
||||
struct qcom_scm_qseecom_resp {
|
||||
u64 result;
|
||||
u64 resp_type;
|
||||
u64 data;
|
||||
};
|
||||
|
||||
enum qcom_scm_qseecom_result {
|
||||
QSEECOM_RESULT_SUCCESS = 0,
|
||||
QSEECOM_RESULT_INCOMPLETE = 1,
|
||||
QSEECOM_RESULT_BLOCKED_ON_LISTENER = 2,
|
||||
QSEECOM_RESULT_FAILURE = 0xFFFFFFFF,
|
||||
};
|
||||
|
||||
enum qcom_scm_qseecom_resp_type {
|
||||
QSEECOM_SCM_RES_APP_ID = 0xEE01,
|
||||
QSEECOM_SCM_RES_QSEOS_LISTENER_ID = 0xEE02,
|
||||
};
|
||||
|
||||
enum qcom_scm_qseecom_tz_owner {
|
||||
QSEECOM_TZ_OWNER_SIP = 2,
|
||||
QSEECOM_TZ_OWNER_TZ_APPS = 48,
|
||||
QSEECOM_TZ_OWNER_QSEE_OS = 50
|
||||
};
|
||||
|
||||
enum qcom_scm_qseecom_tz_svc {
|
||||
QSEECOM_TZ_SVC_APP_ID_PLACEHOLDER = 0,
|
||||
QSEECOM_TZ_SVC_APP_MGR = 1,
|
||||
QSEECOM_TZ_SVC_INFO = 6,
|
||||
};
|
||||
|
||||
enum qcom_scm_qseecom_tz_cmd_app {
|
||||
QSEECOM_TZ_CMD_APP_SEND = 1,
|
||||
QSEECOM_TZ_CMD_APP_LOOKUP = 3,
|
||||
};
|
||||
|
||||
enum qcom_scm_qseecom_tz_cmd_info {
|
||||
QSEECOM_TZ_CMD_INFO_VERSION = 3,
|
||||
};
|
||||
|
||||
#define QSEECOM_MAX_APP_NAME_SIZE 64
|
||||
|
||||
/* Each bit configures cold/warm boot address for one of the 4 CPUs */
|
||||
static const u8 qcom_scm_cpu_cold_bits[QCOM_SCM_BOOT_MAX_CPUS] = {
|
||||
0, BIT(0), BIT(3), BIT(5)
|
||||
@ -167,6 +215,12 @@ static enum qcom_scm_convention __get_convention(void)
|
||||
if (likely(qcom_scm_convention != SMC_CONVENTION_UNKNOWN))
|
||||
return qcom_scm_convention;
|
||||
|
||||
/*
|
||||
* Per the "SMC calling convention specification", the 64-bit calling
|
||||
* convention can only be used when the client is 64-bit, otherwise
|
||||
* system will encounter the undefined behaviour.
|
||||
*/
|
||||
#if IS_ENABLED(CONFIG_ARM64)
|
||||
/*
|
||||
* Device isn't required as there is only one argument - no device
|
||||
* needed to dma_map_single to secure world
|
||||
@ -187,6 +241,7 @@ static enum qcom_scm_convention __get_convention(void)
|
||||
forced = true;
|
||||
goto found;
|
||||
}
|
||||
#endif
|
||||
|
||||
probed_convention = SMC_CONVENTION_ARM_32;
|
||||
ret = __scm_smc_call(NULL, &desc, probed_convention, &res, true);
|
||||
@ -403,6 +458,29 @@ int qcom_scm_set_remote_state(u32 state, u32 id)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_scm_set_remote_state);
|
||||
|
||||
static int qcom_scm_disable_sdi(void)
|
||||
{
|
||||
int ret;
|
||||
struct qcom_scm_desc desc = {
|
||||
.svc = QCOM_SCM_SVC_BOOT,
|
||||
.cmd = QCOM_SCM_BOOT_SDI_CONFIG,
|
||||
.args[0] = 1, /* Disable watchdog debug */
|
||||
.args[1] = 0, /* Disable SDI */
|
||||
.arginfo = QCOM_SCM_ARGS(2),
|
||||
.owner = ARM_SMCCC_OWNER_SIP,
|
||||
};
|
||||
struct qcom_scm_res res;
|
||||
|
||||
ret = qcom_scm_clk_enable();
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = qcom_scm_call(__scm->dev, &desc, &res);
|
||||
|
||||
qcom_scm_clk_disable();
|
||||
|
||||
return ret ? : res.result[0];
|
||||
}
|
||||
|
||||
static int __qcom_scm_set_dload_mode(struct device *dev, bool enable)
|
||||
{
|
||||
struct qcom_scm_desc desc = {
|
||||
@ -1321,6 +1399,340 @@ static int qcom_scm_find_dload_address(struct device *dev, u64 *addr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_QCOM_QSEECOM
|
||||
|
||||
/* Lock for QSEECOM SCM call executions */
|
||||
static DEFINE_MUTEX(qcom_scm_qseecom_call_lock);
|
||||
|
||||
static int __qcom_scm_qseecom_call(const struct qcom_scm_desc *desc,
|
||||
struct qcom_scm_qseecom_resp *res)
|
||||
{
|
||||
struct qcom_scm_res scm_res = {};
|
||||
int status;
|
||||
|
||||
/*
|
||||
* QSEECOM SCM calls should not be executed concurrently. Therefore, we
|
||||
* require the respective call lock to be held.
|
||||
*/
|
||||
lockdep_assert_held(&qcom_scm_qseecom_call_lock);
|
||||
|
||||
status = qcom_scm_call(__scm->dev, desc, &scm_res);
|
||||
|
||||
res->result = scm_res.result[0];
|
||||
res->resp_type = scm_res.result[1];
|
||||
res->data = scm_res.result[2];
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* qcom_scm_qseecom_call() - Perform a QSEECOM SCM call.
|
||||
* @desc: SCM call descriptor.
|
||||
* @res: SCM call response (output).
|
||||
*
|
||||
* Performs the QSEECOM SCM call described by @desc, returning the response in
|
||||
* @rsp.
|
||||
*
|
||||
* Return: Zero on success, nonzero on failure.
|
||||
*/
|
||||
static int qcom_scm_qseecom_call(const struct qcom_scm_desc *desc,
|
||||
struct qcom_scm_qseecom_resp *res)
|
||||
{
|
||||
int status;
|
||||
|
||||
/*
|
||||
* Note: Multiple QSEECOM SCM calls should not be executed same time,
|
||||
* so lock things here. This needs to be extended to callback/listener
|
||||
* handling when support for that is implemented.
|
||||
*/
|
||||
|
||||
mutex_lock(&qcom_scm_qseecom_call_lock);
|
||||
status = __qcom_scm_qseecom_call(desc, res);
|
||||
mutex_unlock(&qcom_scm_qseecom_call_lock);
|
||||
|
||||
dev_dbg(__scm->dev, "%s: owner=%x, svc=%x, cmd=%x, result=%lld, type=%llx, data=%llx\n",
|
||||
__func__, desc->owner, desc->svc, desc->cmd, res->result,
|
||||
res->resp_type, res->data);
|
||||
|
||||
if (status) {
|
||||
dev_err(__scm->dev, "qseecom: scm call failed with error %d\n", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: Handle incomplete and blocked calls:
|
||||
*
|
||||
* Incomplete and blocked calls are not supported yet. Some devices
|
||||
* and/or commands require those, some don't. Let's warn about them
|
||||
* prominently in case someone attempts to try these commands with a
|
||||
* device/command combination that isn't supported yet.
|
||||
*/
|
||||
WARN_ON(res->result == QSEECOM_RESULT_INCOMPLETE);
|
||||
WARN_ON(res->result == QSEECOM_RESULT_BLOCKED_ON_LISTENER);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* qcom_scm_qseecom_get_version() - Query the QSEECOM version.
|
||||
* @version: Pointer where the QSEECOM version will be stored.
|
||||
*
|
||||
* Performs the QSEECOM SCM querying the QSEECOM version currently running in
|
||||
* the TrustZone.
|
||||
*
|
||||
* Return: Zero on success, nonzero on failure.
|
||||
*/
|
||||
static int qcom_scm_qseecom_get_version(u32 *version)
|
||||
{
|
||||
struct qcom_scm_desc desc = {};
|
||||
struct qcom_scm_qseecom_resp res = {};
|
||||
u32 feature = 10;
|
||||
int ret;
|
||||
|
||||
desc.owner = QSEECOM_TZ_OWNER_SIP;
|
||||
desc.svc = QSEECOM_TZ_SVC_INFO;
|
||||
desc.cmd = QSEECOM_TZ_CMD_INFO_VERSION;
|
||||
desc.arginfo = QCOM_SCM_ARGS(1, QCOM_SCM_VAL);
|
||||
desc.args[0] = feature;
|
||||
|
||||
ret = qcom_scm_qseecom_call(&desc, &res);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*version = res.result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* qcom_scm_qseecom_app_get_id() - Query the app ID for a given QSEE app name.
|
||||
* @app_name: The name of the app.
|
||||
* @app_id: The returned app ID.
|
||||
*
|
||||
* Query and return the application ID of the SEE app identified by the given
|
||||
* name. This returned ID is the unique identifier of the app required for
|
||||
* subsequent communication.
|
||||
*
|
||||
* Return: Zero on success, nonzero on failure, -ENOENT if the app has not been
|
||||
* loaded or could not be found.
|
||||
*/
|
||||
int qcom_scm_qseecom_app_get_id(const char *app_name, u32 *app_id)
|
||||
{
|
||||
unsigned long name_buf_size = QSEECOM_MAX_APP_NAME_SIZE;
|
||||
unsigned long app_name_len = strlen(app_name);
|
||||
struct qcom_scm_desc desc = {};
|
||||
struct qcom_scm_qseecom_resp res = {};
|
||||
dma_addr_t name_buf_phys;
|
||||
char *name_buf;
|
||||
int status;
|
||||
|
||||
if (app_name_len >= name_buf_size)
|
||||
return -EINVAL;
|
||||
|
||||
name_buf = kzalloc(name_buf_size, GFP_KERNEL);
|
||||
if (!name_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(name_buf, app_name, app_name_len);
|
||||
|
||||
name_buf_phys = dma_map_single(__scm->dev, name_buf, name_buf_size, DMA_TO_DEVICE);
|
||||
status = dma_mapping_error(__scm->dev, name_buf_phys);
|
||||
if (status) {
|
||||
kfree(name_buf);
|
||||
dev_err(__scm->dev, "qseecom: failed to map dma address\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
desc.owner = QSEECOM_TZ_OWNER_QSEE_OS;
|
||||
desc.svc = QSEECOM_TZ_SVC_APP_MGR;
|
||||
desc.cmd = QSEECOM_TZ_CMD_APP_LOOKUP;
|
||||
desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_RW, QCOM_SCM_VAL);
|
||||
desc.args[0] = name_buf_phys;
|
||||
desc.args[1] = app_name_len;
|
||||
|
||||
status = qcom_scm_qseecom_call(&desc, &res);
|
||||
dma_unmap_single(__scm->dev, name_buf_phys, name_buf_size, DMA_TO_DEVICE);
|
||||
kfree(name_buf);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (res.result == QSEECOM_RESULT_FAILURE)
|
||||
return -ENOENT;
|
||||
|
||||
if (res.result != QSEECOM_RESULT_SUCCESS)
|
||||
return -EINVAL;
|
||||
|
||||
if (res.resp_type != QSEECOM_SCM_RES_APP_ID)
|
||||
return -EINVAL;
|
||||
|
||||
*app_id = res.data;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_scm_qseecom_app_get_id);
|
||||
|
||||
/**
|
||||
* qcom_scm_qseecom_app_send() - Send to and receive data from a given QSEE app.
|
||||
* @app_id: The ID of the target app.
|
||||
* @req: Request buffer sent to the app (must be DMA-mappable).
|
||||
* @req_size: Size of the request buffer.
|
||||
* @rsp: Response buffer, written to by the app (must be DMA-mappable).
|
||||
* @rsp_size: Size of the response buffer.
|
||||
*
|
||||
* Sends a request to the QSEE app associated with the given ID and read back
|
||||
* its response. The caller must provide two DMA memory regions, one for the
|
||||
* request and one for the response, and fill out the @req region with the
|
||||
* respective (app-specific) request data. The QSEE app reads this and returns
|
||||
* its response in the @rsp region.
|
||||
*
|
||||
* Return: Zero on success, nonzero on failure.
|
||||
*/
|
||||
int qcom_scm_qseecom_app_send(u32 app_id, void *req, size_t req_size, void *rsp,
|
||||
size_t rsp_size)
|
||||
{
|
||||
struct qcom_scm_qseecom_resp res = {};
|
||||
struct qcom_scm_desc desc = {};
|
||||
dma_addr_t req_phys;
|
||||
dma_addr_t rsp_phys;
|
||||
int status;
|
||||
|
||||
/* Map request buffer */
|
||||
req_phys = dma_map_single(__scm->dev, req, req_size, DMA_TO_DEVICE);
|
||||
status = dma_mapping_error(__scm->dev, req_phys);
|
||||
if (status) {
|
||||
dev_err(__scm->dev, "qseecom: failed to map request buffer\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Map response buffer */
|
||||
rsp_phys = dma_map_single(__scm->dev, rsp, rsp_size, DMA_FROM_DEVICE);
|
||||
status = dma_mapping_error(__scm->dev, rsp_phys);
|
||||
if (status) {
|
||||
dma_unmap_single(__scm->dev, req_phys, req_size, DMA_TO_DEVICE);
|
||||
dev_err(__scm->dev, "qseecom: failed to map response buffer\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Set up SCM call data */
|
||||
desc.owner = QSEECOM_TZ_OWNER_TZ_APPS;
|
||||
desc.svc = QSEECOM_TZ_SVC_APP_ID_PLACEHOLDER;
|
||||
desc.cmd = QSEECOM_TZ_CMD_APP_SEND;
|
||||
desc.arginfo = QCOM_SCM_ARGS(5, QCOM_SCM_VAL,
|
||||
QCOM_SCM_RW, QCOM_SCM_VAL,
|
||||
QCOM_SCM_RW, QCOM_SCM_VAL);
|
||||
desc.args[0] = app_id;
|
||||
desc.args[1] = req_phys;
|
||||
desc.args[2] = req_size;
|
||||
desc.args[3] = rsp_phys;
|
||||
desc.args[4] = rsp_size;
|
||||
|
||||
/* Perform call */
|
||||
status = qcom_scm_qseecom_call(&desc, &res);
|
||||
|
||||
/* Unmap buffers */
|
||||
dma_unmap_single(__scm->dev, rsp_phys, rsp_size, DMA_FROM_DEVICE);
|
||||
dma_unmap_single(__scm->dev, req_phys, req_size, DMA_TO_DEVICE);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (res.result != QSEECOM_RESULT_SUCCESS)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_scm_qseecom_app_send);
|
||||
|
||||
/*
|
||||
* We do not yet support re-entrant calls via the qseecom interface. To prevent
|
||||
+ any potential issues with this, only allow validated machines for now.
|
||||
*/
|
||||
static const struct of_device_id qcom_scm_qseecom_allowlist[] = {
|
||||
{ .compatible = "lenovo,thinkpad-x13s", },
|
||||
{ }
|
||||
};
|
||||
|
||||
static bool qcom_scm_qseecom_machine_is_allowed(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
bool match;
|
||||
|
||||
np = of_find_node_by_path("/");
|
||||
if (!np)
|
||||
return false;
|
||||
|
||||
match = of_match_node(qcom_scm_qseecom_allowlist, np);
|
||||
of_node_put(np);
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
static void qcom_scm_qseecom_free(void *data)
|
||||
{
|
||||
struct platform_device *qseecom_dev = data;
|
||||
|
||||
platform_device_del(qseecom_dev);
|
||||
platform_device_put(qseecom_dev);
|
||||
}
|
||||
|
||||
static int qcom_scm_qseecom_init(struct qcom_scm *scm)
|
||||
{
|
||||
struct platform_device *qseecom_dev;
|
||||
u32 version;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Note: We do two steps of validation here: First, we try to query the
|
||||
* QSEECOM version as a check to see if the interface exists on this
|
||||
* device. Second, we check against known good devices due to current
|
||||
* driver limitations (see comment in qcom_scm_qseecom_allowlist).
|
||||
*
|
||||
* Note that we deliberately do the machine check after the version
|
||||
* check so that we can log potentially supported devices. This should
|
||||
* be safe as downstream sources indicate that the version query is
|
||||
* neither blocking nor reentrant.
|
||||
*/
|
||||
ret = qcom_scm_qseecom_get_version(&version);
|
||||
if (ret)
|
||||
return 0;
|
||||
|
||||
dev_info(scm->dev, "qseecom: found qseecom with version 0x%x\n", version);
|
||||
|
||||
if (!qcom_scm_qseecom_machine_is_allowed()) {
|
||||
dev_info(scm->dev, "qseecom: untested machine, skipping\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up QSEECOM interface device. All application clients will be
|
||||
* set up and managed by the corresponding driver for it.
|
||||
*/
|
||||
qseecom_dev = platform_device_alloc("qcom_qseecom", -1);
|
||||
if (!qseecom_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
qseecom_dev->dev.parent = scm->dev;
|
||||
|
||||
ret = platform_device_add(qseecom_dev);
|
||||
if (ret) {
|
||||
platform_device_put(qseecom_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return devm_add_action_or_reset(scm->dev, qcom_scm_qseecom_free, qseecom_dev);
|
||||
}
|
||||
|
||||
#else /* CONFIG_QCOM_QSEECOM */
|
||||
|
||||
static int qcom_scm_qseecom_init(struct qcom_scm *scm)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_QCOM_QSEECOM */
|
||||
|
||||
/**
|
||||
* qcom_scm_is_available() - Checks if SCM is available
|
||||
*/
|
||||
@ -1468,6 +1880,26 @@ static int qcom_scm_probe(struct platform_device *pdev)
|
||||
if (download_mode)
|
||||
qcom_scm_set_download_mode(true);
|
||||
|
||||
|
||||
/*
|
||||
* Disable SDI if indicated by DT that it is enabled by default.
|
||||
*/
|
||||
if (of_property_read_bool(pdev->dev.of_node, "qcom,sdi-enabled"))
|
||||
qcom_scm_disable_sdi();
|
||||
|
||||
/*
|
||||
* Initialize the QSEECOM interface.
|
||||
*
|
||||
* Note: QSEECOM is fairly self-contained and this only adds the
|
||||
* interface device (the driver of which does most of the heavy
|
||||
* lifting). So any errors returned here should be either -ENOMEM or
|
||||
* -EINVAL (with the latter only in case there's a bug in our code).
|
||||
* This means that there is no need to bring down the whole SCM driver.
|
||||
* Just log the error instead and let SCM live.
|
||||
*/
|
||||
ret = qcom_scm_qseecom_init(scm);
|
||||
WARN(ret < 0, "failed to initialize qseecom: %d\n", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
#ifndef __QCOM_SCM_INT_H
|
||||
#define __QCOM_SCM_INT_H
|
||||
|
||||
struct device;
|
||||
|
||||
enum qcom_scm_convention {
|
||||
SMC_CONVENTION_UNKNOWN,
|
||||
SMC_CONVENTION_LEGACY,
|
||||
@ -64,22 +66,22 @@ int qcom_scm_wait_for_wq_completion(u32 wq_ctx);
|
||||
int scm_get_wq_ctx(u32 *wq_ctx, u32 *flags, u32 *more_pending);
|
||||
|
||||
#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,
|
||||
enum qcom_scm_convention qcom_convention,
|
||||
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);
|
||||
#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,
|
||||
const struct qcom_scm_desc *desc,
|
||||
struct qcom_scm_res *res);
|
||||
extern int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc,
|
||||
int scm_legacy_call_atomic(struct device *dev, const struct qcom_scm_desc *desc,
|
||||
struct qcom_scm_res *res);
|
||||
int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc,
|
||||
struct qcom_scm_res *res);
|
||||
|
||||
#define QCOM_SCM_SVC_BOOT 0x01
|
||||
#define QCOM_SCM_BOOT_SET_ADDR 0x01
|
||||
#define QCOM_SCM_BOOT_TERMINATE_PC 0x02
|
||||
#define QCOM_SCM_BOOT_SDI_CONFIG 0x09
|
||||
#define QCOM_SCM_BOOT_SET_DLOAD_MODE 0x10
|
||||
#define QCOM_SCM_BOOT_SET_ADDR_MC 0x11
|
||||
#define QCOM_SCM_BOOT_SET_REMOTE_STATE 0x0a
|
@ -378,6 +378,7 @@ EXPORT_SYMBOL_GPL(rpi_firmware_get);
|
||||
|
||||
/**
|
||||
* devm_rpi_firmware_get - Get pointer to rpi_firmware structure.
|
||||
* @dev: The firmware device structure
|
||||
* @firmware_node: Pointer to the firmware Device Tree node.
|
||||
*
|
||||
* Returns NULL is the firmware device is not ready.
|
||||
|
@ -313,6 +313,8 @@ static ssize_t tegra_bpmp_channel_write(struct tegra_bpmp_channel *channel,
|
||||
return __tegra_bpmp_channel_write(channel, mrq, flags, data, size);
|
||||
}
|
||||
|
||||
static int __maybe_unused tegra_bpmp_resume(struct device *dev);
|
||||
|
||||
int tegra_bpmp_transfer_atomic(struct tegra_bpmp *bpmp,
|
||||
struct tegra_bpmp_message *msg)
|
||||
{
|
||||
@ -325,6 +327,14 @@ int tegra_bpmp_transfer_atomic(struct tegra_bpmp *bpmp,
|
||||
if (!tegra_bpmp_message_valid(msg))
|
||||
return -EINVAL;
|
||||
|
||||
if (bpmp->suspended) {
|
||||
/* Reset BPMP IPC channels during resume based on flags passed */
|
||||
if (msg->flags & TEGRA_BPMP_MESSAGE_RESET)
|
||||
tegra_bpmp_resume(bpmp->dev);
|
||||
else
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
channel = bpmp->tx_channel;
|
||||
|
||||
spin_lock(&bpmp->atomic_tx_lock);
|
||||
@ -364,6 +374,14 @@ int tegra_bpmp_transfer(struct tegra_bpmp *bpmp,
|
||||
if (!tegra_bpmp_message_valid(msg))
|
||||
return -EINVAL;
|
||||
|
||||
if (bpmp->suspended) {
|
||||
/* Reset BPMP IPC channels during resume based on flags passed */
|
||||
if (msg->flags & TEGRA_BPMP_MESSAGE_RESET)
|
||||
tegra_bpmp_resume(bpmp->dev);
|
||||
else
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
channel = tegra_bpmp_write_threaded(bpmp, msg->mrq, msg->tx.data,
|
||||
msg->tx.size);
|
||||
if (IS_ERR(channel))
|
||||
@ -796,10 +814,21 @@ deinit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __maybe_unused tegra_bpmp_suspend(struct device *dev)
|
||||
{
|
||||
struct tegra_bpmp *bpmp = dev_get_drvdata(dev);
|
||||
|
||||
bpmp->suspended = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused tegra_bpmp_resume(struct device *dev)
|
||||
{
|
||||
struct tegra_bpmp *bpmp = dev_get_drvdata(dev);
|
||||
|
||||
bpmp->suspended = false;
|
||||
|
||||
if (bpmp->soc->ops->resume)
|
||||
return bpmp->soc->ops->resume(bpmp);
|
||||
else
|
||||
@ -807,6 +836,7 @@ static int __maybe_unused tegra_bpmp_resume(struct device *dev)
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops tegra_bpmp_pm_ops = {
|
||||
.suspend_noirq = tegra_bpmp_suspend,
|
||||
.resume_noirq = tegra_bpmp_resume,
|
||||
};
|
||||
|
||||
|
@ -16,7 +16,10 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mailbox_client.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/semaphore.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/soc/ti/ti-msgmgr.h>
|
||||
@ -190,19 +193,6 @@ static int ti_sci_debugfs_create(struct platform_device *pdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_sci_debugfs_destroy() - clean up log debug file
|
||||
* @pdev: platform device pointer
|
||||
* @info: Pointer to SCI entity information
|
||||
*/
|
||||
static void ti_sci_debugfs_destroy(struct platform_device *pdev,
|
||||
struct ti_sci_info *info)
|
||||
{
|
||||
if (IS_ERR(info->debug_region))
|
||||
return;
|
||||
|
||||
debugfs_remove(info->d);
|
||||
}
|
||||
#else /* CONFIG_DEBUG_FS */
|
||||
static inline int ti_sci_debugfs_create(struct platform_device *dev,
|
||||
struct ti_sci_info *info)
|
||||
@ -485,7 +475,7 @@ static int ti_sci_cmd_get_revision(struct ti_sci_info *info)
|
||||
ver->abi_major = rev_info->abi_major;
|
||||
ver->abi_minor = rev_info->abi_minor;
|
||||
ver->firmware_revision = rev_info->firmware_revision;
|
||||
strncpy(ver->firmware_description, rev_info->firmware_description,
|
||||
strscpy(ver->firmware_description, rev_info->firmware_description,
|
||||
sizeof(ver->firmware_description));
|
||||
|
||||
fail:
|
||||
@ -2886,7 +2876,6 @@ static void ti_sci_setup_ops(struct ti_sci_info *info)
|
||||
const struct ti_sci_handle *ti_sci_get_handle(struct device *dev)
|
||||
{
|
||||
struct device_node *ti_sci_np;
|
||||
struct list_head *p;
|
||||
struct ti_sci_handle *handle = NULL;
|
||||
struct ti_sci_info *info;
|
||||
|
||||
@ -2901,8 +2890,7 @@ const struct ti_sci_handle *ti_sci_get_handle(struct device *dev)
|
||||
}
|
||||
|
||||
mutex_lock(&ti_sci_list_mutex);
|
||||
list_for_each(p, &ti_sci_list) {
|
||||
info = list_entry(p, struct ti_sci_info, node);
|
||||
list_for_each_entry(info, &ti_sci_list, node) {
|
||||
if (ti_sci_np == info->dev->of_node) {
|
||||
handle = &info->handle;
|
||||
info->users++;
|
||||
@ -3012,7 +3000,6 @@ const struct ti_sci_handle *ti_sci_get_by_phandle(struct device_node *np,
|
||||
struct ti_sci_handle *handle = NULL;
|
||||
struct device_node *ti_sci_np;
|
||||
struct ti_sci_info *info;
|
||||
struct list_head *p;
|
||||
|
||||
if (!np) {
|
||||
pr_err("I need a device pointer\n");
|
||||
@ -3024,8 +3011,7 @@ const struct ti_sci_handle *ti_sci_get_by_phandle(struct device_node *np,
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
mutex_lock(&ti_sci_list_mutex);
|
||||
list_for_each(p, &ti_sci_list) {
|
||||
info = list_entry(p, struct ti_sci_info, node);
|
||||
list_for_each_entry(info, &ti_sci_list, node) {
|
||||
if (ti_sci_np == info->dev->of_node) {
|
||||
handle = &info->handle;
|
||||
info->users++;
|
||||
@ -3310,7 +3296,6 @@ MODULE_DEVICE_TABLE(of, ti_sci_of_match);
|
||||
static int ti_sci_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct of_device_id *of_id;
|
||||
const struct ti_sci_desc *desc;
|
||||
struct ti_sci_xfer *xfer;
|
||||
struct ti_sci_info *info = NULL;
|
||||
@ -3321,12 +3306,7 @@ static int ti_sci_probe(struct platform_device *pdev)
|
||||
int reboot = 0;
|
||||
u32 h_id;
|
||||
|
||||
of_id = of_match_device(ti_sci_of_match, dev);
|
||||
if (!of_id) {
|
||||
dev_err(dev, "OF data missing\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
desc = of_id->data;
|
||||
desc = device_get_match_data(dev);
|
||||
|
||||
info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
@ -3449,43 +3429,12 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ti_sci_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ti_sci_info *info;
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret = 0;
|
||||
|
||||
of_platform_depopulate(dev);
|
||||
|
||||
info = platform_get_drvdata(pdev);
|
||||
|
||||
if (info->nb.notifier_call)
|
||||
unregister_restart_handler(&info->nb);
|
||||
|
||||
mutex_lock(&ti_sci_list_mutex);
|
||||
if (info->users)
|
||||
ret = -EBUSY;
|
||||
else
|
||||
list_del(&info->node);
|
||||
mutex_unlock(&ti_sci_list_mutex);
|
||||
|
||||
if (!ret) {
|
||||
ti_sci_debugfs_destroy(pdev, info);
|
||||
|
||||
/* Safe to free channels since no more users */
|
||||
mbox_free_channel(info->chan_tx);
|
||||
mbox_free_channel(info->chan_rx);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_driver ti_sci_driver = {
|
||||
.probe = ti_sci_probe,
|
||||
.remove = ti_sci_remove,
|
||||
.driver = {
|
||||
.name = "ti-sci",
|
||||
.of_match_table = of_match_ptr(ti_sci_of_match),
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
module_platform_driver(ti_sci_driver);
|
||||
|
@ -12,7 +12,10 @@
|
||||
#include <linux/mfd/syscon/atmel-matrix.h>
|
||||
#include <linux/mfd/syscon/atmel-smc.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <soc/at91/atmel-sfr.h>
|
||||
|
||||
@ -30,7 +33,7 @@ struct atmel_ebi_dev {
|
||||
struct atmel_ebi *ebi;
|
||||
u32 mode;
|
||||
int numcs;
|
||||
struct atmel_ebi_dev_config configs[];
|
||||
struct atmel_ebi_dev_config configs[] __counted_by(numcs);
|
||||
};
|
||||
|
||||
struct atmel_ebi_caps {
|
||||
@ -515,16 +518,11 @@ static int atmel_ebi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *child, *np = dev->of_node, *smc_np;
|
||||
const struct of_device_id *match;
|
||||
struct atmel_ebi *ebi;
|
||||
int ret, reg_cells;
|
||||
struct clk *clk;
|
||||
u32 val;
|
||||
|
||||
match = of_match_device(atmel_ebi_id_table, dev);
|
||||
if (!match || !match->data)
|
||||
return -EINVAL;
|
||||
|
||||
ebi = devm_kzalloc(dev, sizeof(*ebi), GFP_KERNEL);
|
||||
if (!ebi)
|
||||
return -ENOMEM;
|
||||
@ -532,7 +530,9 @@ static int atmel_ebi_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, ebi);
|
||||
|
||||
INIT_LIST_HEAD(&ebi->devs);
|
||||
ebi->caps = match->data;
|
||||
ebi->caps = device_get_match_data(dev);
|
||||
if (!ebi->caps)
|
||||
return -EINVAL;
|
||||
ebi->dev = dev;
|
||||
|
||||
clk = devm_clk_get(dev, NULL);
|
||||
|
@ -8,8 +8,9 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
|
||||
#define REG_MEMC_CNTRLR_CONFIG 0x00
|
||||
#define CNTRLR_CONFIG_LPDDR4_SHIFT 5
|
||||
@ -121,12 +122,9 @@ static struct attribute_group dev_attr_group = {
|
||||
.attrs = dev_attrs,
|
||||
};
|
||||
|
||||
static const struct of_device_id brcmstb_memc_of_match[];
|
||||
|
||||
static int brcmstb_memc_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct brcmstb_memc_data *memc_data;
|
||||
const struct of_device_id *of_id;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct brcmstb_memc *memc;
|
||||
int ret;
|
||||
@ -137,8 +135,7 @@ static int brcmstb_memc_probe(struct platform_device *pdev)
|
||||
|
||||
dev_set_drvdata(dev, memc);
|
||||
|
||||
of_id = of_match_device(brcmstb_memc_of_match, dev);
|
||||
memc_data = of_id->data;
|
||||
memc_data = device_get_match_data(dev);
|
||||
memc->srpd_offset = memc_data->srpd_offset;
|
||||
|
||||
memc->ddr_ctrl = devm_platform_ioremap_resource(pdev, 0);
|
||||
|
@ -10,10 +10,8 @@
|
||||
#include <linux/irq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
|
||||
enum ccf_version {
|
||||
CCF1,
|
||||
@ -172,14 +170,9 @@ out:
|
||||
static int ccf_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ccf_private *ccf;
|
||||
const struct of_device_id *match;
|
||||
u32 errinten;
|
||||
int ret, irq;
|
||||
|
||||
match = of_match_device(ccf_matches, &pdev->dev);
|
||||
if (WARN_ON(!match))
|
||||
return -ENODEV;
|
||||
|
||||
ccf = devm_kzalloc(&pdev->dev, sizeof(*ccf), GFP_KERNEL);
|
||||
if (!ccf)
|
||||
return -ENOMEM;
|
||||
@ -189,7 +182,7 @@ static int ccf_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(ccf->regs);
|
||||
|
||||
ccf->dev = &pdev->dev;
|
||||
ccf->info = match->data;
|
||||
ccf->info = device_get_match_data(&pdev->dev);
|
||||
ccf->err_regs = ccf->regs + ccf->info->err_reg_offs;
|
||||
|
||||
if (ccf->info->has_brr) {
|
||||
|
@ -449,6 +449,18 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
|
||||
.security = 0x38c,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_VIW,
|
||||
.name = "viw",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_VI,
|
||||
.type = TEGRA_ICC_ISO_VI,
|
||||
.sid = TEGRA234_SID_ISO_VI,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x390,
|
||||
.security = 0x394,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_NVDECSRD,
|
||||
.name = "nvdecsrd",
|
||||
@ -621,6 +633,30 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
|
||||
.security = 0x50c,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_VIFALR,
|
||||
.name = "vifalr",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_VIFAL,
|
||||
.type = TEGRA_ICC_ISO_VIFAL,
|
||||
.sid = TEGRA234_SID_ISO_VIFALC,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x5e0,
|
||||
.security = 0x5e4,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_VIFALW,
|
||||
.name = "vifalw",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_VIFAL,
|
||||
.type = TEGRA_ICC_ISO_VIFAL,
|
||||
.sid = TEGRA234_SID_ISO_VIFALC,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x5e8,
|
||||
.security = 0x5ec,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_DLA0RDA,
|
||||
.name = "dla0rda",
|
||||
@ -701,6 +737,30 @@ static const struct tegra_mc_client tegra234_mc_clients[] = {
|
||||
.security = 0x62c,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_RCER,
|
||||
.name = "rcer",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_RCE,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA234_SID_RCE,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x690,
|
||||
.security = 0x694,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_RCEW,
|
||||
.name = "rcew",
|
||||
.bpmp_id = TEGRA_ICC_BPMP_RCE,
|
||||
.type = TEGRA_ICC_NISO,
|
||||
.sid = TEGRA234_SID_RCE,
|
||||
.regs = {
|
||||
.sid = {
|
||||
.override = 0x698,
|
||||
.security = 0x69c,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
.id = TEGRA234_MEMORY_CLIENT_PCIE0R,
|
||||
.name = "pcie0r",
|
||||
@ -986,6 +1046,10 @@ static int tegra234_mc_icc_set(struct icc_node *src, struct icc_node *dst)
|
||||
msg.rx.data = &bwmgr_resp;
|
||||
msg.rx.size = sizeof(bwmgr_resp);
|
||||
|
||||
if (pclient->bpmp_id >= TEGRA_ICC_BPMP_CPU_CLUSTER0 &&
|
||||
pclient->bpmp_id <= TEGRA_ICC_BPMP_CPU_CLUSTER2)
|
||||
msg.flags = TEGRA_BPMP_MESSAGE_RESET;
|
||||
|
||||
ret = tegra_bpmp_transfer(mc->bpmp, &msg);
|
||||
if (ret < 0) {
|
||||
dev_err(mc->dev, "BPMP transfer failed: %d\n", ret);
|
||||
|
@ -2,6 +2,7 @@
|
||||
obj-y += actions/
|
||||
obj-y += amlogic/
|
||||
obj-y += apple/
|
||||
obj-y += arm/
|
||||
obj-y += bcm/
|
||||
obj-y += imx/
|
||||
obj-y += mediatek/
|
||||
|
4
drivers/pmdomain/arm/Makefile
Normal file
4
drivers/pmdomain/arm/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
obj-$(CONFIG_ARM_SCMI_PERF_DOMAIN) += scmi_perf_domain.o
|
||||
obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o
|
184
drivers/pmdomain/arm/scmi_perf_domain.c
Normal file
184
drivers/pmdomain/arm/scmi_perf_domain.c
Normal file
@ -0,0 +1,184 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* SCMI performance domain support.
|
||||
*
|
||||
* Copyright (C) 2023 Linaro Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/scmi_protocol.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct scmi_perf_domain {
|
||||
struct generic_pm_domain genpd;
|
||||
const struct scmi_perf_proto_ops *perf_ops;
|
||||
const struct scmi_protocol_handle *ph;
|
||||
const struct scmi_perf_domain_info *info;
|
||||
u32 domain_id;
|
||||
};
|
||||
|
||||
#define to_scmi_pd(pd) container_of(pd, struct scmi_perf_domain, genpd)
|
||||
|
||||
static int
|
||||
scmi_pd_set_perf_state(struct generic_pm_domain *genpd, unsigned int state)
|
||||
{
|
||||
struct scmi_perf_domain *pd = to_scmi_pd(genpd);
|
||||
int ret;
|
||||
|
||||
if (!pd->info->set_perf)
|
||||
return 0;
|
||||
|
||||
if (!state)
|
||||
return -EINVAL;
|
||||
|
||||
ret = pd->perf_ops->level_set(pd->ph, pd->domain_id, state, true);
|
||||
if (ret)
|
||||
dev_warn(&genpd->dev, "Failed with %d when trying to set %d perf level",
|
||||
ret, state);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_pd_attach_dev(struct generic_pm_domain *genpd, struct device *dev)
|
||||
{
|
||||
struct scmi_perf_domain *pd = to_scmi_pd(genpd);
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Allow the device to be attached, but don't add the OPP table unless
|
||||
* the performance level can be changed.
|
||||
*/
|
||||
if (!pd->info->set_perf)
|
||||
return 0;
|
||||
|
||||
ret = pd->perf_ops->device_opps_add(pd->ph, dev, pd->domain_id);
|
||||
if (ret)
|
||||
dev_warn(dev, "failed to add OPPs for the device\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
scmi_pd_detach_dev(struct generic_pm_domain *genpd, struct device *dev)
|
||||
{
|
||||
struct scmi_perf_domain *pd = to_scmi_pd(genpd);
|
||||
|
||||
if (!pd->info->set_perf)
|
||||
return;
|
||||
|
||||
dev_pm_opp_remove_all_dynamic(dev);
|
||||
}
|
||||
|
||||
static int scmi_perf_domain_probe(struct scmi_device *sdev)
|
||||
{
|
||||
struct device *dev = &sdev->dev;
|
||||
const struct scmi_handle *handle = sdev->handle;
|
||||
const struct scmi_perf_proto_ops *perf_ops;
|
||||
struct scmi_protocol_handle *ph;
|
||||
struct scmi_perf_domain *scmi_pd;
|
||||
struct genpd_onecell_data *scmi_pd_data;
|
||||
struct generic_pm_domain **domains;
|
||||
int num_domains, i, ret = 0;
|
||||
|
||||
if (!handle)
|
||||
return -ENODEV;
|
||||
|
||||
/* The OF node must specify us as a power-domain provider. */
|
||||
if (!of_find_property(dev->of_node, "#power-domain-cells", NULL))
|
||||
return 0;
|
||||
|
||||
perf_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_PERF, &ph);
|
||||
if (IS_ERR(perf_ops))
|
||||
return PTR_ERR(perf_ops);
|
||||
|
||||
num_domains = perf_ops->num_domains_get(ph);
|
||||
if (num_domains < 0) {
|
||||
dev_warn(dev, "Failed with %d when getting num perf domains\n",
|
||||
num_domains);
|
||||
return num_domains;
|
||||
} else if (!num_domains) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
scmi_pd = devm_kcalloc(dev, num_domains, sizeof(*scmi_pd), GFP_KERNEL);
|
||||
if (!scmi_pd)
|
||||
return -ENOMEM;
|
||||
|
||||
scmi_pd_data = devm_kzalloc(dev, sizeof(*scmi_pd_data), GFP_KERNEL);
|
||||
if (!scmi_pd_data)
|
||||
return -ENOMEM;
|
||||
|
||||
domains = devm_kcalloc(dev, num_domains, sizeof(*domains), GFP_KERNEL);
|
||||
if (!domains)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < num_domains; i++, scmi_pd++) {
|
||||
scmi_pd->info = perf_ops->info_get(ph, i);
|
||||
|
||||
scmi_pd->domain_id = i;
|
||||
scmi_pd->perf_ops = perf_ops;
|
||||
scmi_pd->ph = ph;
|
||||
scmi_pd->genpd.name = scmi_pd->info->name;
|
||||
scmi_pd->genpd.flags = GENPD_FLAG_ALWAYS_ON |
|
||||
GENPD_FLAG_OPP_TABLE_FW;
|
||||
scmi_pd->genpd.set_performance_state = scmi_pd_set_perf_state;
|
||||
scmi_pd->genpd.attach_dev = scmi_pd_attach_dev;
|
||||
scmi_pd->genpd.detach_dev = scmi_pd_detach_dev;
|
||||
|
||||
ret = pm_genpd_init(&scmi_pd->genpd, NULL, false);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
domains[i] = &scmi_pd->genpd;
|
||||
}
|
||||
|
||||
scmi_pd_data->domains = domains;
|
||||
scmi_pd_data->num_domains = num_domains;
|
||||
|
||||
ret = of_genpd_add_provider_onecell(dev->of_node, scmi_pd_data);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
dev_set_drvdata(dev, scmi_pd_data);
|
||||
dev_info(dev, "Initialized %d performance domains", num_domains);
|
||||
return 0;
|
||||
err:
|
||||
for (i--; i >= 0; i--)
|
||||
pm_genpd_remove(domains[i]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void scmi_perf_domain_remove(struct scmi_device *sdev)
|
||||
{
|
||||
struct device *dev = &sdev->dev;
|
||||
struct genpd_onecell_data *scmi_pd_data = dev_get_drvdata(dev);
|
||||
int i;
|
||||
|
||||
of_genpd_del_provider(dev->of_node);
|
||||
|
||||
for (i = 0; i < scmi_pd_data->num_domains; i++)
|
||||
pm_genpd_remove(scmi_pd_data->domains[i]);
|
||||
}
|
||||
|
||||
static const struct scmi_device_id scmi_id_table[] = {
|
||||
{ SCMI_PROTOCOL_PERF, "perf" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(scmi, scmi_id_table);
|
||||
|
||||
static struct scmi_driver scmi_perf_domain_driver = {
|
||||
.name = "scmi-perf-domain",
|
||||
.probe = scmi_perf_domain_probe,
|
||||
.remove = scmi_perf_domain_remove,
|
||||
.id_table = scmi_id_table,
|
||||
};
|
||||
module_scmi_driver(scmi_perf_domain_driver);
|
||||
|
||||
MODULE_AUTHOR("Ulf Hansson <ulf.hansson@linaro.org>");
|
||||
MODULE_DESCRIPTION("ARM SCMI perf domain driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -332,14 +332,12 @@ err:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int aspeed_lpc_ctrl_remove(struct platform_device *pdev)
|
||||
static void aspeed_lpc_ctrl_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct aspeed_lpc_ctrl *lpc_ctrl = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
misc_deregister(&lpc_ctrl->miscdev);
|
||||
clk_disable_unprepare(lpc_ctrl->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id aspeed_lpc_ctrl_match[] = {
|
||||
@ -355,7 +353,7 @@ static struct platform_driver aspeed_lpc_ctrl_driver = {
|
||||
.of_match_table = aspeed_lpc_ctrl_match,
|
||||
},
|
||||
.probe = aspeed_lpc_ctrl_probe,
|
||||
.remove = aspeed_lpc_ctrl_remove,
|
||||
.remove_new = aspeed_lpc_ctrl_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(aspeed_lpc_ctrl_driver);
|
||||
|
@ -331,7 +331,7 @@ err:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int aspeed_lpc_snoop_remove(struct platform_device *pdev)
|
||||
static void aspeed_lpc_snoop_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct aspeed_lpc_snoop *lpc_snoop = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
@ -340,8 +340,6 @@ static int aspeed_lpc_snoop_remove(struct platform_device *pdev)
|
||||
aspeed_lpc_disable_snoop(lpc_snoop, 1);
|
||||
|
||||
clk_disable_unprepare(lpc_snoop->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct aspeed_lpc_snoop_model_data ast2400_model_data = {
|
||||
@ -368,7 +366,7 @@ static struct platform_driver aspeed_lpc_snoop_driver = {
|
||||
.of_match_table = aspeed_lpc_snoop_match,
|
||||
},
|
||||
.probe = aspeed_lpc_snoop_probe,
|
||||
.remove = aspeed_lpc_snoop_remove,
|
||||
.remove_new = aspeed_lpc_snoop_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(aspeed_lpc_snoop_driver);
|
||||
|
@ -383,13 +383,11 @@ static int aspeed_p2a_ctrl_probe(struct platform_device *pdev)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int aspeed_p2a_ctrl_remove(struct platform_device *pdev)
|
||||
static void aspeed_p2a_ctrl_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct aspeed_p2a_ctrl *p2a_ctrl = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
misc_deregister(&p2a_ctrl->miscdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SCU2C_DRAM BIT(25)
|
||||
@ -433,7 +431,7 @@ static struct platform_driver aspeed_p2a_ctrl_driver = {
|
||||
.of_match_table = aspeed_p2a_ctrl_match,
|
||||
},
|
||||
.probe = aspeed_p2a_ctrl_probe,
|
||||
.remove = aspeed_p2a_ctrl_remove,
|
||||
.remove_new = aspeed_p2a_ctrl_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(aspeed_p2a_ctrl_driver);
|
||||
|
@ -565,14 +565,12 @@ static int aspeed_uart_routing_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aspeed_uart_routing_remove(struct platform_device *pdev)
|
||||
static void aspeed_uart_routing_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct aspeed_uart_routing *uart_routing = platform_get_drvdata(pdev);
|
||||
|
||||
sysfs_remove_group(&dev->kobj, uart_routing->attr_grp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id aspeed_uart_routing_table[] = {
|
||||
@ -591,7 +589,7 @@ static struct platform_driver aspeed_uart_routing_driver = {
|
||||
.of_match_table = aspeed_uart_routing_table,
|
||||
},
|
||||
.probe = aspeed_uart_routing_probe,
|
||||
.remove = aspeed_uart_routing_remove,
|
||||
.remove_new = aspeed_uart_routing_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(aspeed_uart_routing_driver);
|
||||
|
@ -3,7 +3,7 @@ menu "Broadcom SoC drivers"
|
||||
|
||||
config SOC_BRCMSTB
|
||||
bool "Broadcom STB SoC drivers"
|
||||
depends on ARM || ARM64 || BMIPS_GENERIC || COMPILE_TEST
|
||||
depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST
|
||||
select SOC_BUS
|
||||
help
|
||||
Enables drivers for the Broadcom Set-Top Box (STB) series of chips.
|
||||
|
@ -410,13 +410,16 @@ int __init dove_init_pmu(void)
|
||||
struct pmu_domain *domain;
|
||||
|
||||
domain = kzalloc(sizeof(*domain), GFP_KERNEL);
|
||||
if (!domain)
|
||||
if (!domain) {
|
||||
of_node_put(np);
|
||||
break;
|
||||
}
|
||||
|
||||
domain->pmu = pmu;
|
||||
domain->base.name = kasprintf(GFP_KERNEL, "%pOFn", np);
|
||||
if (!domain->base.name) {
|
||||
kfree(domain);
|
||||
of_node_put(np);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -300,12 +300,10 @@ err_register_mc:
|
||||
return error;
|
||||
}
|
||||
|
||||
static int dpaa2_console_remove(struct platform_device *pdev)
|
||||
static void dpaa2_console_remove(struct platform_device *pdev)
|
||||
{
|
||||
misc_deregister(&dpaa2_mc_console_dev);
|
||||
misc_deregister(&dpaa2_aiop_console_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id dpaa2_console_match_table[] = {
|
||||
@ -322,7 +320,7 @@ static struct platform_driver dpaa2_console_driver = {
|
||||
.of_match_table = dpaa2_console_match_table,
|
||||
},
|
||||
.probe = dpaa2_console_probe,
|
||||
.remove = dpaa2_console_remove,
|
||||
.remove_new = dpaa2_console_remove,
|
||||
};
|
||||
module_platform_driver(dpaa2_console_driver);
|
||||
|
||||
|
@ -1415,7 +1415,7 @@ err_tsa_serial_disconnect:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qmc_remove(struct platform_device *pdev)
|
||||
static void qmc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct qmc *qmc = platform_get_drvdata(pdev);
|
||||
|
||||
@ -1427,8 +1427,6 @@ static int qmc_remove(struct platform_device *pdev)
|
||||
|
||||
/* Disconnect the serial from TSA */
|
||||
tsa_serial_disconnect(qmc->tsa_serial);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id qmc_id_table[] = {
|
||||
@ -1443,7 +1441,7 @@ static struct platform_driver qmc_driver = {
|
||||
.of_match_table = of_match_ptr(qmc_id_table),
|
||||
},
|
||||
.probe = qmc_probe,
|
||||
.remove = qmc_remove,
|
||||
.remove_new = qmc_remove,
|
||||
};
|
||||
module_platform_driver(qmc_driver);
|
||||
|
||||
|
@ -706,7 +706,7 @@ static int tsa_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tsa_remove(struct platform_device *pdev)
|
||||
static void tsa_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tsa *tsa = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
@ -729,7 +729,6 @@ static int tsa_remove(struct platform_device *pdev)
|
||||
clk_put(tsa->tdm[i].l1rclk_clk);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id tsa_id_table[] = {
|
||||
@ -744,7 +743,7 @@ static struct platform_driver tsa_driver = {
|
||||
.of_match_table = of_match_ptr(tsa_id_table),
|
||||
},
|
||||
.probe = tsa_probe,
|
||||
.remove = tsa_remove,
|
||||
.remove_new = tsa_remove,
|
||||
};
|
||||
module_platform_driver(tsa_driver);
|
||||
|
||||
|
@ -116,7 +116,7 @@ static int a64fx_diag_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int a64fx_diag_remove(struct platform_device *pdev)
|
||||
static void a64fx_diag_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct a64fx_diag_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
@ -127,8 +127,6 @@ static int a64fx_diag_remove(struct platform_device *pdev)
|
||||
free_nmi(priv->irq, NULL);
|
||||
else
|
||||
free_irq(priv->irq, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id a64fx_diag_acpi_match[] = {
|
||||
@ -144,7 +142,7 @@ static struct platform_driver a64fx_diag_driver = {
|
||||
.acpi_match_table = ACPI_PTR(a64fx_diag_acpi_match),
|
||||
},
|
||||
.probe = a64fx_diag_probe,
|
||||
.remove = a64fx_diag_remove,
|
||||
.remove_new = a64fx_diag_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(a64fx_diag_driver);
|
||||
|
@ -1240,14 +1240,12 @@ unregister_pcc_chan:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int hccs_remove(struct platform_device *pdev)
|
||||
static void hccs_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct hccs_dev *hdev = platform_get_drvdata(pdev);
|
||||
|
||||
hccs_remove_topo_dirs(hdev);
|
||||
hccs_unregister_pcc_channel(hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id hccs_acpi_match[] = {
|
||||
@ -1258,7 +1256,7 @@ MODULE_DEVICE_TABLE(acpi, hccs_acpi_match);
|
||||
|
||||
static struct platform_driver hccs_driver = {
|
||||
.probe = hccs_probe,
|
||||
.remove = hccs_remove,
|
||||
.remove_new = hccs_remove,
|
||||
.driver = {
|
||||
.name = "kunpeng_hccs",
|
||||
.acpi_match_table = hccs_acpi_match,
|
||||
|
@ -736,7 +736,7 @@ static int ixp4xx_npe_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ixp4xx_npe_remove(struct platform_device *pdev)
|
||||
static void ixp4xx_npe_remove(struct platform_device *pdev)
|
||||
{
|
||||
int i;
|
||||
|
||||
@ -744,8 +744,6 @@ static int ixp4xx_npe_remove(struct platform_device *pdev)
|
||||
if (npe_tab[i].regs) {
|
||||
npe_reset(&npe_tab[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ixp4xx_npe_of_match[] = {
|
||||
@ -761,7 +759,7 @@ static struct platform_driver ixp4xx_npe_driver = {
|
||||
.of_match_table = ixp4xx_npe_of_match,
|
||||
},
|
||||
.probe = ixp4xx_npe_probe,
|
||||
.remove = ixp4xx_npe_remove,
|
||||
.remove_new = ixp4xx_npe_remove,
|
||||
};
|
||||
module_platform_driver(ixp4xx_npe_driver);
|
||||
|
||||
|
@ -442,11 +442,10 @@ static int ixp4xx_qmgr_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ixp4xx_qmgr_remove(struct platform_device *pdev)
|
||||
static void ixp4xx_qmgr_remove(struct platform_device *pdev)
|
||||
{
|
||||
synchronize_irq(qmgr_irq_1);
|
||||
synchronize_irq(qmgr_irq_2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ixp4xx_qmgr_of_match[] = {
|
||||
@ -462,7 +461,7 @@ static struct platform_driver ixp4xx_qmgr_driver = {
|
||||
.of_match_table = ixp4xx_qmgr_of_match,
|
||||
},
|
||||
.probe = ixp4xx_qmgr_probe,
|
||||
.remove = ixp4xx_qmgr_remove,
|
||||
.remove_new = ixp4xx_qmgr_remove,
|
||||
};
|
||||
module_platform_driver(ixp4xx_qmgr_driver);
|
||||
|
||||
|
@ -120,12 +120,11 @@ static int litex_soc_ctrl_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int litex_soc_ctrl_remove(struct platform_device *pdev)
|
||||
static void litex_soc_ctrl_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct litex_soc_ctrl_device *soc_ctrl_dev = platform_get_drvdata(pdev);
|
||||
|
||||
unregister_restart_handler(&soc_ctrl_dev->reset_nb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver litex_soc_ctrl_driver = {
|
||||
@ -134,7 +133,7 @@ static struct platform_driver litex_soc_ctrl_driver = {
|
||||
.of_match_table = of_match_ptr(litex_soc_ctrl_of_match)
|
||||
},
|
||||
.probe = litex_soc_ctrl_probe,
|
||||
.remove = litex_soc_ctrl_remove,
|
||||
.remove_new = litex_soc_ctrl_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(litex_soc_ctrl_driver);
|
||||
|
@ -148,11 +148,9 @@ static int loongson2_guts_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int loongson2_guts_remove(struct platform_device *dev)
|
||||
static void loongson2_guts_remove(struct platform_device *dev)
|
||||
{
|
||||
soc_device_unregister(soc_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -171,7 +169,7 @@ static struct platform_driver loongson2_guts_driver = {
|
||||
.of_match_table = loongson2_guts_of_match,
|
||||
},
|
||||
.probe = loongson2_guts_probe,
|
||||
.remove = loongson2_guts_remove,
|
||||
.remove_new = loongson2_guts_remove,
|
||||
};
|
||||
|
||||
static int __init loongson2_guts_init(void)
|
||||
|
@ -292,18 +292,16 @@ static int mtk_devapc_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_devapc_remove(struct platform_device *pdev)
|
||||
static void mtk_devapc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mtk_devapc_context *ctx = platform_get_drvdata(pdev);
|
||||
|
||||
stop_devapc(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver mtk_devapc_driver = {
|
||||
.probe = mtk_devapc_probe,
|
||||
.remove = mtk_devapc_remove,
|
||||
.remove_new = mtk_devapc_remove,
|
||||
.driver = {
|
||||
.name = "mtk-devapc",
|
||||
.of_match_table = mtk_devapc_dt_match,
|
||||
|
@ -410,14 +410,12 @@ out_probe_done:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_mmsys_remove(struct platform_device *pdev)
|
||||
static void mtk_mmsys_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mtk_mmsys *mmsys = platform_get_drvdata(pdev);
|
||||
|
||||
platform_device_unregister(mmsys->drm_pdev);
|
||||
platform_device_unregister(mmsys->clks_pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id of_match_mtk_mmsys[] = {
|
||||
@ -449,7 +447,7 @@ static struct platform_driver mtk_mmsys_drv = {
|
||||
.of_match_table = of_match_mtk_mmsys,
|
||||
},
|
||||
.probe = mtk_mmsys_probe,
|
||||
.remove = mtk_mmsys_remove,
|
||||
.remove_new = mtk_mmsys_remove,
|
||||
};
|
||||
module_platform_driver(mtk_mmsys_drv);
|
||||
|
||||
|
@ -407,6 +407,7 @@ struct svs_platform_data {
|
||||
* @dcbdet: svs efuse data
|
||||
* @dcmdet: svs efuse data
|
||||
* @turn_pt: 2-line turn point tells which opp_volt calculated by high/low bank
|
||||
* @vbin_turn_pt: voltage bin turn point helps know which svsb_volt should be overridden
|
||||
* @type: bank type to represent it is 2-line (high/low) bank or 1-line bank
|
||||
*
|
||||
* Svs bank will generate suitalbe voltages by below general math equation
|
||||
@ -469,6 +470,7 @@ struct svs_bank {
|
||||
u32 dcbdet;
|
||||
u32 dcmdet;
|
||||
u32 turn_pt;
|
||||
u32 vbin_turn_pt;
|
||||
u32 type;
|
||||
};
|
||||
|
||||
@ -751,11 +753,12 @@ static int svs_status_debug_show(struct seq_file *m, void *v)
|
||||
|
||||
ret = thermal_zone_get_temp(svsb->tzd, &tzone_temp);
|
||||
if (ret)
|
||||
seq_printf(m, "%s: temperature ignore, turn_pt = %u\n",
|
||||
svsb->name, svsb->turn_pt);
|
||||
seq_printf(m, "%s: temperature ignore, vbin_turn_pt = %u, turn_pt = %u\n",
|
||||
svsb->name, svsb->vbin_turn_pt, svsb->turn_pt);
|
||||
else
|
||||
seq_printf(m, "%s: temperature = %d, turn_pt = %u\n",
|
||||
svsb->name, tzone_temp, svsb->turn_pt);
|
||||
seq_printf(m, "%s: temperature = %d, vbin_turn_pt = %u, turn_pt = %u\n",
|
||||
svsb->name, tzone_temp, svsb->vbin_turn_pt,
|
||||
svsb->turn_pt);
|
||||
|
||||
for (i = 0; i < svsb->opp_count; i++) {
|
||||
opp = dev_pm_opp_find_freq_exact(svsb->opp_dev,
|
||||
@ -952,6 +955,29 @@ static void svs_get_bank_volts_v3(struct svs_platform *svsp)
|
||||
for (i = opp_start; i < opp_stop; i++)
|
||||
if (svsb->volt_flags & SVSB_REMOVE_DVTFIXED_VOLT)
|
||||
svsb->volt[i] -= svsb->dvt_fixed;
|
||||
|
||||
/* For voltage bin support */
|
||||
if (svsb->opp_dfreq[0] > svsb->freq_base) {
|
||||
svsb->volt[0] = svs_opp_volt_to_bank_volt(svsb->opp_dvolt[0],
|
||||
svsb->volt_step,
|
||||
svsb->volt_base);
|
||||
|
||||
/* Find voltage bin turn point */
|
||||
for (i = 0; i < svsb->opp_count; i++) {
|
||||
if (svsb->opp_dfreq[i] <= svsb->freq_base) {
|
||||
svsb->vbin_turn_pt = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Override svs bank voltages */
|
||||
for (i = 1; i < svsb->vbin_turn_pt; i++)
|
||||
svsb->volt[i] = interpolate(svsb->freq_pct[0],
|
||||
svsb->freq_pct[svsb->vbin_turn_pt],
|
||||
svsb->volt[0],
|
||||
svsb->volt[svsb->vbin_turn_pt],
|
||||
svsb->freq_pct[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void svs_set_bank_freq_pct_v3(struct svs_platform *svsp)
|
||||
@ -1069,6 +1095,29 @@ static void svs_get_bank_volts_v2(struct svs_platform *svsp)
|
||||
|
||||
for (i = 0; i < svsb->opp_count; i++)
|
||||
svsb->volt[i] += svsb->volt_od;
|
||||
|
||||
/* For voltage bin support */
|
||||
if (svsb->opp_dfreq[0] > svsb->freq_base) {
|
||||
svsb->volt[0] = svs_opp_volt_to_bank_volt(svsb->opp_dvolt[0],
|
||||
svsb->volt_step,
|
||||
svsb->volt_base);
|
||||
|
||||
/* Find voltage bin turn point */
|
||||
for (i = 0; i < svsb->opp_count; i++) {
|
||||
if (svsb->opp_dfreq[i] <= svsb->freq_base) {
|
||||
svsb->vbin_turn_pt = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Override svs bank voltages */
|
||||
for (i = 1; i < svsb->vbin_turn_pt; i++)
|
||||
svsb->volt[i] = interpolate(svsb->freq_pct[0],
|
||||
svsb->freq_pct[svsb->vbin_turn_pt],
|
||||
svsb->volt[0],
|
||||
svsb->volt[svsb->vbin_turn_pt],
|
||||
svsb->freq_pct[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void svs_set_bank_freq_pct_v2(struct svs_platform *svsp)
|
||||
@ -1808,6 +1857,66 @@ static bool svs_mt8192_efuse_parsing(struct svs_platform *svsp)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool svs_mt8188_efuse_parsing(struct svs_platform *svsp)
|
||||
{
|
||||
struct svs_bank *svsb;
|
||||
u32 idx, i, golden_temp;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < svsp->efuse_max; i++)
|
||||
if (svsp->efuse[i])
|
||||
dev_info(svsp->dev, "M_HW_RES%d: 0x%08x\n",
|
||||
i, svsp->efuse[i]);
|
||||
|
||||
if (!svsp->efuse[5]) {
|
||||
dev_notice(svsp->dev, "svs_efuse[5] = 0x0?\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Svs efuse parsing */
|
||||
for (idx = 0; idx < svsp->bank_max; idx++) {
|
||||
svsb = &svsp->banks[idx];
|
||||
|
||||
if (svsb->type == SVSB_LOW) {
|
||||
svsb->mtdes = svsp->efuse[5] & GENMASK(7, 0);
|
||||
svsb->bdes = (svsp->efuse[5] >> 16) & GENMASK(7, 0);
|
||||
svsb->mdes = (svsp->efuse[5] >> 24) & GENMASK(7, 0);
|
||||
svsb->dcbdet = (svsp->efuse[15] >> 16) & GENMASK(7, 0);
|
||||
svsb->dcmdet = (svsp->efuse[15] >> 24) & GENMASK(7, 0);
|
||||
} else if (svsb->type == SVSB_HIGH) {
|
||||
svsb->mtdes = svsp->efuse[4] & GENMASK(7, 0);
|
||||
svsb->bdes = (svsp->efuse[4] >> 16) & GENMASK(7, 0);
|
||||
svsb->mdes = (svsp->efuse[4] >> 24) & GENMASK(7, 0);
|
||||
svsb->dcbdet = svsp->efuse[14] & GENMASK(7, 0);
|
||||
svsb->dcmdet = (svsp->efuse[14] >> 8) & GENMASK(7, 0);
|
||||
}
|
||||
|
||||
svsb->vmax += svsb->dvt_fixed;
|
||||
}
|
||||
|
||||
ret = svs_get_efuse_data(svsp, "t-calibration-data",
|
||||
&svsp->tefuse, &svsp->tefuse_max);
|
||||
if (ret)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < svsp->tefuse_max; i++)
|
||||
if (svsp->tefuse[i] != 0)
|
||||
break;
|
||||
|
||||
if (i == svsp->tefuse_max)
|
||||
golden_temp = 50; /* All thermal efuse data are 0 */
|
||||
else
|
||||
golden_temp = (svsp->tefuse[0] >> 24) & GENMASK(7, 0);
|
||||
|
||||
for (idx = 0; idx < svsp->bank_max; idx++) {
|
||||
svsb = &svsp->banks[idx];
|
||||
svsb->mts = 500;
|
||||
svsb->bts = (((500 * golden_temp + 250460) / 1000) - 25) * 4;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool svs_mt8183_efuse_parsing(struct svs_platform *svsp)
|
||||
{
|
||||
struct svs_bank *svsb;
|
||||
@ -2173,6 +2282,61 @@ static struct svs_bank svs_mt8192_banks[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct svs_bank svs_mt8188_banks[] = {
|
||||
{
|
||||
.sw_id = SVSB_GPU,
|
||||
.type = SVSB_LOW,
|
||||
.set_freq_pct = svs_set_bank_freq_pct_v3,
|
||||
.get_volts = svs_get_bank_volts_v3,
|
||||
.volt_flags = SVSB_REMOVE_DVTFIXED_VOLT,
|
||||
.mode_support = SVSB_MODE_INIT02,
|
||||
.opp_count = MAX_OPP_ENTRIES,
|
||||
.freq_base = 640000000,
|
||||
.turn_freq_base = 640000000,
|
||||
.volt_step = 6250,
|
||||
.volt_base = 400000,
|
||||
.vmax = 0x38,
|
||||
.vmin = 0x1c,
|
||||
.age_config = 0x555555,
|
||||
.dc_config = 0x555555,
|
||||
.dvt_fixed = 0x1,
|
||||
.vco = 0x10,
|
||||
.chk_shift = 0x87,
|
||||
.core_sel = 0x0fff0000,
|
||||
.int_st = BIT(0),
|
||||
.ctl0 = 0x00100003,
|
||||
},
|
||||
{
|
||||
.sw_id = SVSB_GPU,
|
||||
.type = SVSB_HIGH,
|
||||
.set_freq_pct = svs_set_bank_freq_pct_v3,
|
||||
.get_volts = svs_get_bank_volts_v3,
|
||||
.tzone_name = "gpu1",
|
||||
.volt_flags = SVSB_REMOVE_DVTFIXED_VOLT |
|
||||
SVSB_MON_VOLT_IGNORE,
|
||||
.mode_support = SVSB_MODE_INIT02 | SVSB_MODE_MON,
|
||||
.opp_count = MAX_OPP_ENTRIES,
|
||||
.freq_base = 880000000,
|
||||
.turn_freq_base = 640000000,
|
||||
.volt_step = 6250,
|
||||
.volt_base = 400000,
|
||||
.vmax = 0x38,
|
||||
.vmin = 0x1c,
|
||||
.age_config = 0x555555,
|
||||
.dc_config = 0x555555,
|
||||
.dvt_fixed = 0x4,
|
||||
.vco = 0x10,
|
||||
.chk_shift = 0x87,
|
||||
.core_sel = 0x0fff0001,
|
||||
.int_st = BIT(1),
|
||||
.ctl0 = 0x00100003,
|
||||
.tzone_htemp = 85000,
|
||||
.tzone_htemp_voffset = 0,
|
||||
.tzone_ltemp = 25000,
|
||||
.tzone_ltemp_voffset = 7,
|
||||
},
|
||||
};
|
||||
|
||||
static struct svs_bank svs_mt8183_banks[] = {
|
||||
{
|
||||
.sw_id = SVSB_CPU_LITTLE,
|
||||
@ -2286,6 +2450,15 @@ static const struct svs_platform_data svs_mt8192_platform_data = {
|
||||
.bank_max = ARRAY_SIZE(svs_mt8192_banks),
|
||||
};
|
||||
|
||||
static const struct svs_platform_data svs_mt8188_platform_data = {
|
||||
.name = "mt8188-svs",
|
||||
.banks = svs_mt8188_banks,
|
||||
.efuse_parsing = svs_mt8188_efuse_parsing,
|
||||
.probe = svs_mt8192_platform_probe,
|
||||
.regs = svs_regs_v2,
|
||||
.bank_max = ARRAY_SIZE(svs_mt8188_banks),
|
||||
};
|
||||
|
||||
static const struct svs_platform_data svs_mt8183_platform_data = {
|
||||
.name = "mt8183-svs",
|
||||
.banks = svs_mt8183_banks,
|
||||
@ -2299,6 +2472,9 @@ static const struct of_device_id svs_of_match[] = {
|
||||
{
|
||||
.compatible = "mediatek,mt8192-svs",
|
||||
.data = &svs_mt8192_platform_data,
|
||||
}, {
|
||||
.compatible = "mediatek,mt8188-svs",
|
||||
.data = &svs_mt8188_platform_data,
|
||||
}, {
|
||||
.compatible = "mediatek,mt8183-svs",
|
||||
.data = &svs_mt8183_platform_data,
|
||||
|
@ -149,13 +149,11 @@ static int mpfs_sys_controller_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpfs_sys_controller_remove(struct platform_device *pdev)
|
||||
static void mpfs_sys_controller_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mpfs_sys_controller *sys_controller = platform_get_drvdata(pdev);
|
||||
|
||||
mpfs_sys_controller_put(sys_controller);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id mpfs_sys_controller_of_match[] = {
|
||||
@ -207,7 +205,7 @@ static struct platform_driver mpfs_sys_controller_driver = {
|
||||
.of_match_table = mpfs_sys_controller_of_match,
|
||||
},
|
||||
.probe = mpfs_sys_controller_probe,
|
||||
.remove = mpfs_sys_controller_remove,
|
||||
.remove_new = mpfs_sys_controller_remove,
|
||||
};
|
||||
module_platform_driver(mpfs_sys_controller_driver);
|
||||
|
||||
|
@ -176,15 +176,13 @@ static int pxa_ssp_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pxa_ssp_remove(struct platform_device *pdev)
|
||||
static void pxa_ssp_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ssp_device *ssp = platform_get_drvdata(pdev);
|
||||
|
||||
mutex_lock(&ssp_lock);
|
||||
list_del(&ssp->node);
|
||||
mutex_unlock(&ssp_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id ssp_id_table[] = {
|
||||
@ -199,7 +197,7 @@ static const struct platform_device_id ssp_id_table[] = {
|
||||
|
||||
static struct platform_driver pxa_ssp_driver = {
|
||||
.probe = pxa_ssp_probe,
|
||||
.remove = pxa_ssp_remove,
|
||||
.remove_new = pxa_ssp_remove,
|
||||
.driver = {
|
||||
.name = "pxa2xx-ssp",
|
||||
.of_match_table = of_match_ptr(pxa_ssp_of_ids),
|
||||
|
@ -41,7 +41,7 @@ struct packet_router {
|
||||
struct apr_rx_buf {
|
||||
struct list_head node;
|
||||
int len;
|
||||
uint8_t buf[];
|
||||
uint8_t buf[] __counted_by(len);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -171,7 +171,7 @@ static int apr_callback(struct rpmsg_device *rpdev, void *buf,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
abuf = kzalloc(sizeof(*abuf) + len, GFP_ATOMIC);
|
||||
abuf = kzalloc(struct_size(abuf, buf, len), GFP_ATOMIC);
|
||||
if (!abuf)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -133,7 +133,7 @@ int cmd_db_ready(void)
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(cmd_db_ready);
|
||||
EXPORT_SYMBOL_GPL(cmd_db_ready);
|
||||
|
||||
static int cmd_db_get_header(const char *id, const struct entry_header **eh,
|
||||
const struct rsc_hdr **rh)
|
||||
@ -193,7 +193,7 @@ u32 cmd_db_read_addr(const char *id)
|
||||
|
||||
return ret < 0 ? 0 : le32_to_cpu(ent->addr);
|
||||
}
|
||||
EXPORT_SYMBOL(cmd_db_read_addr);
|
||||
EXPORT_SYMBOL_GPL(cmd_db_read_addr);
|
||||
|
||||
/**
|
||||
* cmd_db_read_aux_data() - Query command db for aux data.
|
||||
@ -218,7 +218,7 @@ const void *cmd_db_read_aux_data(const char *id, size_t *len)
|
||||
|
||||
return rsc_offset(rsc_hdr, ent);
|
||||
}
|
||||
EXPORT_SYMBOL(cmd_db_read_aux_data);
|
||||
EXPORT_SYMBOL_GPL(cmd_db_read_aux_data);
|
||||
|
||||
/**
|
||||
* cmd_db_read_slave_id - Get the slave ID for a given resource address
|
||||
@ -240,7 +240,7 @@ enum cmd_db_hw_type cmd_db_read_slave_id(const char *id)
|
||||
addr = le32_to_cpu(ent->addr);
|
||||
return (addr >> SLAVE_ID_SHIFT) & SLAVE_ID_MASK;
|
||||
}
|
||||
EXPORT_SYMBOL(cmd_db_read_slave_id);
|
||||
EXPORT_SYMBOL_GPL(cmd_db_read_slave_id);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static int cmd_db_debugfs_dump(struct seq_file *seq, void *p)
|
||||
|
@ -793,13 +793,11 @@ static int bwmon_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bwmon_remove(struct platform_device *pdev)
|
||||
static void bwmon_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct icc_bwmon *bwmon = platform_get_drvdata(pdev);
|
||||
|
||||
bwmon_disable(bwmon);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct icc_bwmon_data msm8998_bwmon_data = {
|
||||
@ -862,7 +860,7 @@ MODULE_DEVICE_TABLE(of, bwmon_of_match);
|
||||
|
||||
static struct platform_driver bwmon_driver = {
|
||||
.probe = bwmon_probe,
|
||||
.remove = bwmon_remove,
|
||||
.remove_new = bwmon_remove,
|
||||
.driver = {
|
||||
.name = "qcom-bwmon",
|
||||
.of_match_table = bwmon_of_match,
|
||||
|
@ -32,7 +32,7 @@ void kryo_l2_set_indirect_reg(u64 reg, u64 val)
|
||||
isb();
|
||||
raw_spin_unlock_irqrestore(&l2_access_lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(kryo_l2_set_indirect_reg);
|
||||
EXPORT_SYMBOL_GPL(kryo_l2_set_indirect_reg);
|
||||
|
||||
/**
|
||||
* kryo_l2_get_indirect_reg() - read an L2 register value
|
||||
@ -54,4 +54,4 @@ u64 kryo_l2_get_indirect_reg(u64 reg)
|
||||
|
||||
return val;
|
||||
}
|
||||
EXPORT_SYMBOL(kryo_l2_get_indirect_reg);
|
||||
EXPORT_SYMBOL_GPL(kryo_l2_get_indirect_reg);
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/nvmem-consumer.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/sizes.h>
|
||||
@ -126,6 +127,11 @@ struct qcom_llcc_config {
|
||||
bool no_edac;
|
||||
};
|
||||
|
||||
struct qcom_sct_config {
|
||||
const struct qcom_llcc_config *llcc_config;
|
||||
int num_config;
|
||||
};
|
||||
|
||||
enum llcc_reg_offset {
|
||||
LLCC_COMMON_HW_INFO,
|
||||
LLCC_COMMON_STATUS0,
|
||||
@ -185,7 +191,7 @@ static const struct llcc_slice_config sc8280xp_data[] = {
|
||||
{ LLCC_MMUHWT, 13, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 },
|
||||
{ LLCC_DISP, 16, 6144, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0 },
|
||||
{ LLCC_AUDHW, 22, 2048, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0 },
|
||||
{ LLCC_DRE, 26, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0 },
|
||||
{ LLCC_ECC, 26, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0 },
|
||||
{ LLCC_CVP, 28, 512, 3, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0 },
|
||||
{ LLCC_APTCM, 30, 1024, 3, 1, 0x0, 0x1, 1, 0, 0, 1, 0, 0 },
|
||||
{ LLCC_WRCACHE, 31, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 },
|
||||
@ -356,6 +362,36 @@ static const struct llcc_slice_config sm8550_data[] = {
|
||||
{LLCC_VIDVSP, 28, 256, 4, 1, 0xFFFFFF, 0x0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
};
|
||||
|
||||
static const struct llcc_slice_config qdu1000_data_2ch[] = {
|
||||
{ LLCC_MDMHPGRW, 7, 512, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0, 0 },
|
||||
{ LLCC_MODHW, 9, 256, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0, 0 },
|
||||
{ LLCC_MDMPNG, 21, 256, 0, 1, 0x3, 0x0, 0, 0, 0, 1, 0, 0, 0 },
|
||||
{ LLCC_ECC, 26, 512, 3, 1, 0xffc, 0x0, 0, 0, 0, 0, 1, 0, 0 },
|
||||
{ LLCC_MODPE, 29, 256, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0, 0 },
|
||||
{ LLCC_APTCM, 30, 256, 3, 1, 0x0, 0xc, 1, 0, 0, 1, 0, 0, 0 },
|
||||
{ LLCC_WRCACHE, 31, 128, 1, 1, 0x3, 0x0, 0, 0, 0, 0, 1, 0, 0 },
|
||||
};
|
||||
|
||||
static const struct llcc_slice_config qdu1000_data_4ch[] = {
|
||||
{ LLCC_MDMHPGRW, 7, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0, 0 },
|
||||
{ LLCC_MODHW, 9, 512, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0, 0 },
|
||||
{ LLCC_MDMPNG, 21, 512, 0, 1, 0x3, 0x0, 0, 0, 0, 1, 0, 0, 0 },
|
||||
{ LLCC_ECC, 26, 1024, 3, 1, 0xffc, 0x0, 0, 0, 0, 0, 1, 0, 0 },
|
||||
{ LLCC_MODPE, 29, 512, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0, 0 },
|
||||
{ LLCC_APTCM, 30, 512, 3, 1, 0x0, 0xc, 1, 0, 0, 1, 0, 0, 0 },
|
||||
{ LLCC_WRCACHE, 31, 256, 1, 1, 0x3, 0x0, 0, 0, 0, 0, 1, 0, 0 },
|
||||
};
|
||||
|
||||
static const struct llcc_slice_config qdu1000_data_8ch[] = {
|
||||
{ LLCC_MDMHPGRW, 7, 2048, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0, 0 },
|
||||
{ LLCC_MODHW, 9, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0, 0 },
|
||||
{ LLCC_MDMPNG, 21, 1024, 0, 1, 0x3, 0x0, 0, 0, 0, 1, 0, 0, 0 },
|
||||
{ LLCC_ECC, 26, 2048, 3, 1, 0xffc, 0x0, 0, 0, 0, 0, 1, 0, 0 },
|
||||
{ LLCC_MODPE, 29, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0, 0 },
|
||||
{ LLCC_APTCM, 30, 1024, 3, 1, 0x0, 0xc, 1, 0, 0, 1, 0, 0, 0 },
|
||||
{ LLCC_WRCACHE, 31, 512, 1, 1, 0x3, 0x0, 0, 0, 0, 0, 1, 0, 0 },
|
||||
};
|
||||
|
||||
static const struct llcc_edac_reg_offset llcc_v1_edac_reg_offset = {
|
||||
.trp_ecc_error_status0 = 0x20344,
|
||||
.trp_ecc_error_status1 = 0x20348,
|
||||
@ -422,101 +458,221 @@ static const u32 llcc_v2_1_reg_offset[] = {
|
||||
[LLCC_COMMON_STATUS0] = 0x0003400c,
|
||||
};
|
||||
|
||||
static const struct qcom_llcc_config sc7180_cfg = {
|
||||
.sct_data = sc7180_data,
|
||||
.size = ARRAY_SIZE(sc7180_data),
|
||||
.need_llcc_cfg = true,
|
||||
.reg_offset = llcc_v1_reg_offset,
|
||||
.edac_reg_offset = &llcc_v1_edac_reg_offset,
|
||||
static const struct qcom_llcc_config qdu1000_cfg[] = {
|
||||
{
|
||||
.sct_data = qdu1000_data_8ch,
|
||||
.size = ARRAY_SIZE(qdu1000_data_8ch),
|
||||
.need_llcc_cfg = true,
|
||||
.reg_offset = llcc_v2_1_reg_offset,
|
||||
.edac_reg_offset = &llcc_v2_1_edac_reg_offset,
|
||||
},
|
||||
{
|
||||
.sct_data = qdu1000_data_4ch,
|
||||
.size = ARRAY_SIZE(qdu1000_data_4ch),
|
||||
.need_llcc_cfg = true,
|
||||
.reg_offset = llcc_v2_1_reg_offset,
|
||||
.edac_reg_offset = &llcc_v2_1_edac_reg_offset,
|
||||
},
|
||||
{
|
||||
.sct_data = qdu1000_data_4ch,
|
||||
.size = ARRAY_SIZE(qdu1000_data_4ch),
|
||||
.need_llcc_cfg = true,
|
||||
.reg_offset = llcc_v2_1_reg_offset,
|
||||
.edac_reg_offset = &llcc_v2_1_edac_reg_offset,
|
||||
},
|
||||
{
|
||||
.sct_data = qdu1000_data_2ch,
|
||||
.size = ARRAY_SIZE(qdu1000_data_2ch),
|
||||
.need_llcc_cfg = true,
|
||||
.reg_offset = llcc_v2_1_reg_offset,
|
||||
.edac_reg_offset = &llcc_v2_1_edac_reg_offset,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct qcom_llcc_config sc7280_cfg = {
|
||||
.sct_data = sc7280_data,
|
||||
.size = ARRAY_SIZE(sc7280_data),
|
||||
.need_llcc_cfg = true,
|
||||
.reg_offset = llcc_v1_reg_offset,
|
||||
.edac_reg_offset = &llcc_v1_edac_reg_offset,
|
||||
static const struct qcom_llcc_config sc7180_cfg[] = {
|
||||
{
|
||||
.sct_data = sc7180_data,
|
||||
.size = ARRAY_SIZE(sc7180_data),
|
||||
.need_llcc_cfg = true,
|
||||
.reg_offset = llcc_v1_reg_offset,
|
||||
.edac_reg_offset = &llcc_v1_edac_reg_offset,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct qcom_llcc_config sc8180x_cfg = {
|
||||
.sct_data = sc8180x_data,
|
||||
.size = ARRAY_SIZE(sc8180x_data),
|
||||
.need_llcc_cfg = true,
|
||||
.reg_offset = llcc_v1_reg_offset,
|
||||
.edac_reg_offset = &llcc_v1_edac_reg_offset,
|
||||
static const struct qcom_llcc_config sc7280_cfg[] = {
|
||||
{
|
||||
.sct_data = sc7280_data,
|
||||
.size = ARRAY_SIZE(sc7280_data),
|
||||
.need_llcc_cfg = true,
|
||||
.reg_offset = llcc_v1_reg_offset,
|
||||
.edac_reg_offset = &llcc_v1_edac_reg_offset,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct qcom_llcc_config sc8280xp_cfg = {
|
||||
.sct_data = sc8280xp_data,
|
||||
.size = ARRAY_SIZE(sc8280xp_data),
|
||||
.need_llcc_cfg = true,
|
||||
.reg_offset = llcc_v1_reg_offset,
|
||||
.edac_reg_offset = &llcc_v1_edac_reg_offset,
|
||||
static const struct qcom_llcc_config sc8180x_cfg[] = {
|
||||
{
|
||||
.sct_data = sc8180x_data,
|
||||
.size = ARRAY_SIZE(sc8180x_data),
|
||||
.need_llcc_cfg = true,
|
||||
.reg_offset = llcc_v1_reg_offset,
|
||||
.edac_reg_offset = &llcc_v1_edac_reg_offset,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct qcom_llcc_config sdm845_cfg = {
|
||||
.sct_data = sdm845_data,
|
||||
.size = ARRAY_SIZE(sdm845_data),
|
||||
.need_llcc_cfg = false,
|
||||
.reg_offset = llcc_v1_reg_offset,
|
||||
.edac_reg_offset = &llcc_v1_edac_reg_offset,
|
||||
.no_edac = true,
|
||||
static const struct qcom_llcc_config sc8280xp_cfg[] = {
|
||||
{
|
||||
.sct_data = sc8280xp_data,
|
||||
.size = ARRAY_SIZE(sc8280xp_data),
|
||||
.need_llcc_cfg = true,
|
||||
.reg_offset = llcc_v1_reg_offset,
|
||||
.edac_reg_offset = &llcc_v1_edac_reg_offset,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct qcom_llcc_config sm6350_cfg = {
|
||||
.sct_data = sm6350_data,
|
||||
.size = ARRAY_SIZE(sm6350_data),
|
||||
.need_llcc_cfg = true,
|
||||
.reg_offset = llcc_v1_reg_offset,
|
||||
.edac_reg_offset = &llcc_v1_edac_reg_offset,
|
||||
static const struct qcom_llcc_config sdm845_cfg[] = {
|
||||
{
|
||||
.sct_data = sdm845_data,
|
||||
.size = ARRAY_SIZE(sdm845_data),
|
||||
.need_llcc_cfg = false,
|
||||
.reg_offset = llcc_v1_reg_offset,
|
||||
.edac_reg_offset = &llcc_v1_edac_reg_offset,
|
||||
.no_edac = true,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct qcom_llcc_config sm7150_cfg = {
|
||||
.sct_data = sm7150_data,
|
||||
.size = ARRAY_SIZE(sm7150_data),
|
||||
.need_llcc_cfg = true,
|
||||
.reg_offset = llcc_v1_reg_offset,
|
||||
.edac_reg_offset = &llcc_v1_edac_reg_offset,
|
||||
static const struct qcom_llcc_config sm6350_cfg[] = {
|
||||
{
|
||||
.sct_data = sm6350_data,
|
||||
.size = ARRAY_SIZE(sm6350_data),
|
||||
.need_llcc_cfg = true,
|
||||
.reg_offset = llcc_v1_reg_offset,
|
||||
.edac_reg_offset = &llcc_v1_edac_reg_offset,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct qcom_llcc_config sm8150_cfg = {
|
||||
.sct_data = sm8150_data,
|
||||
.size = ARRAY_SIZE(sm8150_data),
|
||||
.need_llcc_cfg = true,
|
||||
.reg_offset = llcc_v1_reg_offset,
|
||||
.edac_reg_offset = &llcc_v1_edac_reg_offset,
|
||||
static const struct qcom_llcc_config sm7150_cfg[] = {
|
||||
{
|
||||
.sct_data = sm7150_data,
|
||||
.size = ARRAY_SIZE(sm7150_data),
|
||||
.need_llcc_cfg = true,
|
||||
.reg_offset = llcc_v1_reg_offset,
|
||||
.edac_reg_offset = &llcc_v1_edac_reg_offset,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct qcom_llcc_config sm8250_cfg = {
|
||||
.sct_data = sm8250_data,
|
||||
.size = ARRAY_SIZE(sm8250_data),
|
||||
.need_llcc_cfg = true,
|
||||
.reg_offset = llcc_v1_reg_offset,
|
||||
.edac_reg_offset = &llcc_v1_edac_reg_offset,
|
||||
static const struct qcom_llcc_config sm8150_cfg[] = {
|
||||
{
|
||||
.sct_data = sm8150_data,
|
||||
.size = ARRAY_SIZE(sm8150_data),
|
||||
.need_llcc_cfg = true,
|
||||
.reg_offset = llcc_v1_reg_offset,
|
||||
.edac_reg_offset = &llcc_v1_edac_reg_offset,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct qcom_llcc_config sm8350_cfg = {
|
||||
.sct_data = sm8350_data,
|
||||
.size = ARRAY_SIZE(sm8350_data),
|
||||
.need_llcc_cfg = true,
|
||||
.reg_offset = llcc_v1_reg_offset,
|
||||
.edac_reg_offset = &llcc_v1_edac_reg_offset,
|
||||
static const struct qcom_llcc_config sm8250_cfg[] = {
|
||||
{
|
||||
.sct_data = sm8250_data,
|
||||
.size = ARRAY_SIZE(sm8250_data),
|
||||
.need_llcc_cfg = true,
|
||||
.reg_offset = llcc_v1_reg_offset,
|
||||
.edac_reg_offset = &llcc_v1_edac_reg_offset,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct qcom_llcc_config sm8450_cfg = {
|
||||
.sct_data = sm8450_data,
|
||||
.size = ARRAY_SIZE(sm8450_data),
|
||||
.need_llcc_cfg = true,
|
||||
.reg_offset = llcc_v2_1_reg_offset,
|
||||
.edac_reg_offset = &llcc_v2_1_edac_reg_offset,
|
||||
static const struct qcom_llcc_config sm8350_cfg[] = {
|
||||
{
|
||||
.sct_data = sm8350_data,
|
||||
.size = ARRAY_SIZE(sm8350_data),
|
||||
.need_llcc_cfg = true,
|
||||
.reg_offset = llcc_v1_reg_offset,
|
||||
.edac_reg_offset = &llcc_v1_edac_reg_offset,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct qcom_llcc_config sm8550_cfg = {
|
||||
.sct_data = sm8550_data,
|
||||
.size = ARRAY_SIZE(sm8550_data),
|
||||
.need_llcc_cfg = true,
|
||||
.reg_offset = llcc_v2_1_reg_offset,
|
||||
.edac_reg_offset = &llcc_v2_1_edac_reg_offset,
|
||||
static const struct qcom_llcc_config sm8450_cfg[] = {
|
||||
{
|
||||
.sct_data = sm8450_data,
|
||||
.size = ARRAY_SIZE(sm8450_data),
|
||||
.need_llcc_cfg = true,
|
||||
.reg_offset = llcc_v2_1_reg_offset,
|
||||
.edac_reg_offset = &llcc_v2_1_edac_reg_offset,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct qcom_llcc_config sm8550_cfg[] = {
|
||||
{
|
||||
.sct_data = sm8550_data,
|
||||
.size = ARRAY_SIZE(sm8550_data),
|
||||
.need_llcc_cfg = true,
|
||||
.reg_offset = llcc_v2_1_reg_offset,
|
||||
.edac_reg_offset = &llcc_v2_1_edac_reg_offset,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct qcom_sct_config qdu1000_cfgs = {
|
||||
.llcc_config = qdu1000_cfg,
|
||||
.num_config = ARRAY_SIZE(qdu1000_cfg),
|
||||
};
|
||||
|
||||
static const struct qcom_sct_config sc7180_cfgs = {
|
||||
.llcc_config = sc7180_cfg,
|
||||
.num_config = ARRAY_SIZE(sc7180_cfg),
|
||||
};
|
||||
|
||||
static const struct qcom_sct_config sc7280_cfgs = {
|
||||
.llcc_config = sc7280_cfg,
|
||||
.num_config = ARRAY_SIZE(sc7280_cfg),
|
||||
};
|
||||
|
||||
static const struct qcom_sct_config sc8180x_cfgs = {
|
||||
.llcc_config = sc8180x_cfg,
|
||||
.num_config = ARRAY_SIZE(sc8180x_cfg),
|
||||
};
|
||||
|
||||
static const struct qcom_sct_config sc8280xp_cfgs = {
|
||||
.llcc_config = sc8280xp_cfg,
|
||||
.num_config = ARRAY_SIZE(sc8280xp_cfg),
|
||||
};
|
||||
|
||||
static const struct qcom_sct_config sdm845_cfgs = {
|
||||
.llcc_config = sdm845_cfg,
|
||||
.num_config = ARRAY_SIZE(sdm845_cfg),
|
||||
};
|
||||
|
||||
static const struct qcom_sct_config sm6350_cfgs = {
|
||||
.llcc_config = sm6350_cfg,
|
||||
.num_config = ARRAY_SIZE(sm6350_cfg),
|
||||
};
|
||||
|
||||
static const struct qcom_sct_config sm7150_cfgs = {
|
||||
.llcc_config = sm7150_cfg,
|
||||
.num_config = ARRAY_SIZE(sm7150_cfg),
|
||||
};
|
||||
|
||||
static const struct qcom_sct_config sm8150_cfgs = {
|
||||
.llcc_config = sm8150_cfg,
|
||||
.num_config = ARRAY_SIZE(sm8150_cfg),
|
||||
};
|
||||
|
||||
static const struct qcom_sct_config sm8250_cfgs = {
|
||||
.llcc_config = sm8250_cfg,
|
||||
.num_config = ARRAY_SIZE(sm8250_cfg),
|
||||
};
|
||||
|
||||
static const struct qcom_sct_config sm8350_cfgs = {
|
||||
.llcc_config = sm8350_cfg,
|
||||
.num_config = ARRAY_SIZE(sm8350_cfg),
|
||||
};
|
||||
|
||||
static const struct qcom_sct_config sm8450_cfgs = {
|
||||
.llcc_config = sm8450_cfg,
|
||||
.num_config = ARRAY_SIZE(sm8450_cfg),
|
||||
};
|
||||
|
||||
static const struct qcom_sct_config sm8550_cfgs = {
|
||||
.llcc_config = sm8550_cfg,
|
||||
.num_config = ARRAY_SIZE(sm8550_cfg),
|
||||
};
|
||||
|
||||
static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER;
|
||||
@ -906,11 +1062,28 @@ static int qcom_llcc_cfg_program(struct platform_device *pdev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qcom_llcc_remove(struct platform_device *pdev)
|
||||
static int qcom_llcc_get_cfg_index(struct platform_device *pdev, u8 *cfg_index, int num_config)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = nvmem_cell_read_u8(&pdev->dev, "multi-chan-ddr", cfg_index);
|
||||
if (ret == -ENOENT || ret == -EOPNOTSUPP) {
|
||||
if (num_config > 1)
|
||||
return -EINVAL;
|
||||
*cfg_index = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ret && *cfg_index >= num_config)
|
||||
ret = -EINVAL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void qcom_llcc_remove(struct platform_device *pdev)
|
||||
{
|
||||
/* Set the global pointer to a error code to avoid referencing it */
|
||||
drv_data = ERR_PTR(-ENODEV);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev, u8 index,
|
||||
@ -938,12 +1111,17 @@ static int qcom_llcc_probe(struct platform_device *pdev)
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret, i;
|
||||
struct platform_device *llcc_edac;
|
||||
const struct qcom_sct_config *cfgs;
|
||||
const struct qcom_llcc_config *cfg;
|
||||
const struct llcc_slice_config *llcc_cfg;
|
||||
u32 sz;
|
||||
u8 cfg_index;
|
||||
u32 version;
|
||||
struct regmap *regmap;
|
||||
|
||||
if (!IS_ERR(drv_data))
|
||||
return -EBUSY;
|
||||
|
||||
drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL);
|
||||
if (!drv_data) {
|
||||
ret = -ENOMEM;
|
||||
@ -957,7 +1135,15 @@ static int qcom_llcc_probe(struct platform_device *pdev)
|
||||
goto err;
|
||||
}
|
||||
|
||||
cfg = of_device_get_match_data(&pdev->dev);
|
||||
cfgs = of_device_get_match_data(&pdev->dev);
|
||||
if (!cfgs) {
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
ret = qcom_llcc_get_cfg_index(pdev, &cfg_index, cfgs->num_config);
|
||||
if (ret)
|
||||
goto err;
|
||||
cfg = &cfgs->llcc_config[cfg_index];
|
||||
|
||||
ret = regmap_read(regmap, cfg->reg_offset[LLCC_COMMON_STATUS0], &num_banks);
|
||||
if (ret)
|
||||
@ -1050,18 +1236,19 @@ err:
|
||||
}
|
||||
|
||||
static const struct of_device_id qcom_llcc_of_match[] = {
|
||||
{ .compatible = "qcom,sc7180-llcc", .data = &sc7180_cfg },
|
||||
{ .compatible = "qcom,sc7280-llcc", .data = &sc7280_cfg },
|
||||
{ .compatible = "qcom,sc8180x-llcc", .data = &sc8180x_cfg },
|
||||
{ .compatible = "qcom,sc8280xp-llcc", .data = &sc8280xp_cfg },
|
||||
{ .compatible = "qcom,sdm845-llcc", .data = &sdm845_cfg },
|
||||
{ .compatible = "qcom,sm6350-llcc", .data = &sm6350_cfg },
|
||||
{ .compatible = "qcom,sm7150-llcc", .data = &sm7150_cfg },
|
||||
{ .compatible = "qcom,sm8150-llcc", .data = &sm8150_cfg },
|
||||
{ .compatible = "qcom,sm8250-llcc", .data = &sm8250_cfg },
|
||||
{ .compatible = "qcom,sm8350-llcc", .data = &sm8350_cfg },
|
||||
{ .compatible = "qcom,sm8450-llcc", .data = &sm8450_cfg },
|
||||
{ .compatible = "qcom,sm8550-llcc", .data = &sm8550_cfg },
|
||||
{ .compatible = "qcom,qdu1000-llcc", .data = &qdu1000_cfgs},
|
||||
{ .compatible = "qcom,sc7180-llcc", .data = &sc7180_cfgs },
|
||||
{ .compatible = "qcom,sc7280-llcc", .data = &sc7280_cfgs },
|
||||
{ .compatible = "qcom,sc8180x-llcc", .data = &sc8180x_cfgs },
|
||||
{ .compatible = "qcom,sc8280xp-llcc", .data = &sc8280xp_cfgs },
|
||||
{ .compatible = "qcom,sdm845-llcc", .data = &sdm845_cfgs },
|
||||
{ .compatible = "qcom,sm6350-llcc", .data = &sm6350_cfgs },
|
||||
{ .compatible = "qcom,sm7150-llcc", .data = &sm7150_cfgs },
|
||||
{ .compatible = "qcom,sm8150-llcc", .data = &sm8150_cfgs },
|
||||
{ .compatible = "qcom,sm8250-llcc", .data = &sm8250_cfgs },
|
||||
{ .compatible = "qcom,sm8350-llcc", .data = &sm8350_cfgs },
|
||||
{ .compatible = "qcom,sm8450-llcc", .data = &sm8450_cfgs },
|
||||
{ .compatible = "qcom,sm8550-llcc", .data = &sm8550_cfgs },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, qcom_llcc_of_match);
|
||||
@ -1072,7 +1259,7 @@ static struct platform_driver qcom_llcc_driver = {
|
||||
.of_match_table = qcom_llcc_of_match,
|
||||
},
|
||||
.probe = qcom_llcc_probe,
|
||||
.remove = qcom_llcc_remove,
|
||||
.remove_new = qcom_llcc_remove,
|
||||
};
|
||||
module_platform_driver(qcom_llcc_driver);
|
||||
|
||||
|
@ -211,7 +211,7 @@ struct ocmem *of_get_ocmem(struct device *dev)
|
||||
}
|
||||
return ocmem;
|
||||
}
|
||||
EXPORT_SYMBOL(of_get_ocmem);
|
||||
EXPORT_SYMBOL_GPL(of_get_ocmem);
|
||||
|
||||
struct ocmem_buf *ocmem_allocate(struct ocmem *ocmem, enum ocmem_client client,
|
||||
unsigned long size)
|
||||
@ -267,7 +267,7 @@ err_unlock:
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL(ocmem_allocate);
|
||||
EXPORT_SYMBOL_GPL(ocmem_allocate);
|
||||
|
||||
void ocmem_free(struct ocmem *ocmem, enum ocmem_client client,
|
||||
struct ocmem_buf *buf)
|
||||
@ -294,7 +294,7 @@ void ocmem_free(struct ocmem *ocmem, enum ocmem_client client,
|
||||
|
||||
clear_bit_unlock(BIT(client), &ocmem->active_allocations);
|
||||
}
|
||||
EXPORT_SYMBOL(ocmem_free);
|
||||
EXPORT_SYMBOL_GPL(ocmem_free);
|
||||
|
||||
static int ocmem_dev_probe(struct platform_device *pdev)
|
||||
{
|
||||
@ -416,14 +416,12 @@ err_clk_disable:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ocmem_dev_remove(struct platform_device *pdev)
|
||||
static void ocmem_dev_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ocmem *ocmem = platform_get_drvdata(pdev);
|
||||
|
||||
clk_disable_unprepare(ocmem->core_clk);
|
||||
clk_disable_unprepare(ocmem->iface_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ocmem_config ocmem_8226_config = {
|
||||
@ -446,7 +444,7 @@ MODULE_DEVICE_TABLE(of, ocmem_of_match);
|
||||
|
||||
static struct platform_driver ocmem_driver = {
|
||||
.probe = ocmem_dev_probe,
|
||||
.remove = ocmem_dev_remove,
|
||||
.remove_new = ocmem_dev_remove,
|
||||
.driver = {
|
||||
.name = "ocmem",
|
||||
.of_match_table = ocmem_of_match,
|
||||
|
@ -554,7 +554,7 @@ err:
|
||||
kfree(pds);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL(pdr_add_lookup);
|
||||
EXPORT_SYMBOL_GPL(pdr_add_lookup);
|
||||
|
||||
/**
|
||||
* pdr_restart_pd() - restart PD
|
||||
@ -634,7 +634,7 @@ int pdr_restart_pd(struct pdr_handle *pdr, struct pdr_service *pds)
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(pdr_restart_pd);
|
||||
EXPORT_SYMBOL_GPL(pdr_restart_pd);
|
||||
|
||||
/**
|
||||
* pdr_handle_alloc() - initialize the PDR client handle
|
||||
@ -715,7 +715,7 @@ free_pdr_handle:
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL(pdr_handle_alloc);
|
||||
EXPORT_SYMBOL_GPL(pdr_handle_alloc);
|
||||
|
||||
/**
|
||||
* pdr_handle_release() - release the PDR client handle
|
||||
@ -749,7 +749,7 @@ void pdr_handle_release(struct pdr_handle *pdr)
|
||||
|
||||
kfree(pdr);
|
||||
}
|
||||
EXPORT_SYMBOL(pdr_handle_release);
|
||||
EXPORT_SYMBOL_GPL(pdr_handle_release);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("Qualcomm Protection Domain Restart helpers");
|
||||
|
@ -318,7 +318,7 @@ out_release_ucsi_aux:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pmic_glink_remove(struct platform_device *pdev)
|
||||
static void pmic_glink_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct pmic_glink *pg = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
@ -334,8 +334,6 @@ static int pmic_glink_remove(struct platform_device *pdev)
|
||||
mutex_lock(&__pmic_glink_lock);
|
||||
__pmic_glink = NULL;
|
||||
mutex_unlock(&__pmic_glink_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const unsigned long pmic_glink_sm8450_client_mask = BIT(PMIC_GLINK_CLIENT_BATT) |
|
||||
@ -352,7 +350,7 @@ MODULE_DEVICE_TABLE(of, pmic_glink_of_match);
|
||||
|
||||
static struct platform_driver pmic_glink_driver = {
|
||||
.probe = pmic_glink_probe,
|
||||
.remove = pmic_glink_remove,
|
||||
.remove_new = pmic_glink_remove,
|
||||
.driver = {
|
||||
.name = "qcom_pmic_glink",
|
||||
.of_match_table = pmic_glink_of_match,
|
||||
|
@ -160,7 +160,7 @@ static void pmic_glink_altmode_enable_dp(struct pmic_glink_altmode *altmode,
|
||||
|
||||
ret = typec_mux_set(port->typec_mux, &port->state);
|
||||
if (ret)
|
||||
dev_err(altmode->dev, "failed to switch mux to DP\n");
|
||||
dev_err(altmode->dev, "failed to switch mux to DP: %d\n", ret);
|
||||
|
||||
port->retimer_state.alt = &port->dp_alt;
|
||||
port->retimer_state.data = &dp_data;
|
||||
@ -168,7 +168,7 @@ static void pmic_glink_altmode_enable_dp(struct pmic_glink_altmode *altmode,
|
||||
|
||||
ret = typec_retimer_set(port->typec_retimer, &port->retimer_state);
|
||||
if (ret)
|
||||
dev_err(altmode->dev, "failed to setup retimer to DP\n");
|
||||
dev_err(altmode->dev, "failed to setup retimer to DP: %d\n", ret);
|
||||
}
|
||||
|
||||
static void pmic_glink_altmode_enable_usb(struct pmic_glink_altmode *altmode,
|
||||
@ -182,7 +182,7 @@ static void pmic_glink_altmode_enable_usb(struct pmic_glink_altmode *altmode,
|
||||
|
||||
ret = typec_mux_set(port->typec_mux, &port->state);
|
||||
if (ret)
|
||||
dev_err(altmode->dev, "failed to switch mux to USB\n");
|
||||
dev_err(altmode->dev, "failed to switch mux to USB: %d\n", ret);
|
||||
|
||||
port->retimer_state.alt = NULL;
|
||||
port->retimer_state.data = NULL;
|
||||
@ -190,7 +190,7 @@ static void pmic_glink_altmode_enable_usb(struct pmic_glink_altmode *altmode,
|
||||
|
||||
ret = typec_retimer_set(port->typec_retimer, &port->retimer_state);
|
||||
if (ret)
|
||||
dev_err(altmode->dev, "failed to setup retimer to USB\n");
|
||||
dev_err(altmode->dev, "failed to setup retimer to USB: %d\n", ret);
|
||||
}
|
||||
|
||||
static void pmic_glink_altmode_safe(struct pmic_glink_altmode *altmode,
|
||||
@ -204,7 +204,7 @@ static void pmic_glink_altmode_safe(struct pmic_glink_altmode *altmode,
|
||||
|
||||
ret = typec_mux_set(port->typec_mux, &port->state);
|
||||
if (ret)
|
||||
dev_err(altmode->dev, "failed to switch mux to safe mode\n");
|
||||
dev_err(altmode->dev, "failed to switch mux to safe mode: %d\n", ret);
|
||||
|
||||
port->retimer_state.alt = NULL;
|
||||
port->retimer_state.data = NULL;
|
||||
@ -212,7 +212,7 @@ static void pmic_glink_altmode_safe(struct pmic_glink_altmode *altmode,
|
||||
|
||||
ret = typec_retimer_set(port->typec_retimer, &port->retimer_state);
|
||||
if (ret)
|
||||
dev_err(altmode->dev, "failed to setup retimer to USB\n");
|
||||
dev_err(altmode->dev, "failed to setup retimer to USB: %d\n", ret);
|
||||
}
|
||||
|
||||
static void pmic_glink_altmode_worker(struct work_struct *work)
|
||||
@ -397,7 +397,7 @@ static void pmic_glink_altmode_enable_worker(struct work_struct *work)
|
||||
|
||||
ret = pmic_glink_altmode_request(altmode, ALTMODE_PAN_EN, 0);
|
||||
if (ret)
|
||||
dev_err(altmode->dev, "failed to request altmode notifications\n");
|
||||
dev_err(altmode->dev, "failed to request altmode notifications: %d\n", ret);
|
||||
}
|
||||
|
||||
static void pmic_glink_altmode_pdr_notify(void *priv, int state)
|
||||
@ -444,6 +444,7 @@ static int pmic_glink_altmode_probe(struct auxiliary_device *adev,
|
||||
ret = fwnode_property_read_u32(fwnode, "reg", &port);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "missing reg property of %pOFn\n", fwnode);
|
||||
fwnode_handle_put(fwnode);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -454,6 +455,7 @@ static int pmic_glink_altmode_probe(struct auxiliary_device *adev,
|
||||
|
||||
if (altmode->ports[port].altmode) {
|
||||
dev_err(dev, "multiple connector definition for port %u\n", port);
|
||||
fwnode_handle_put(fwnode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -465,48 +467,62 @@ static int pmic_glink_altmode_probe(struct auxiliary_device *adev,
|
||||
alt_port->bridge.funcs = &pmic_glink_altmode_bridge_funcs;
|
||||
alt_port->bridge.of_node = to_of_node(fwnode);
|
||||
alt_port->bridge.ops = DRM_BRIDGE_OP_HPD;
|
||||
alt_port->bridge.type = DRM_MODE_CONNECTOR_USB;
|
||||
alt_port->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;
|
||||
|
||||
ret = devm_drm_bridge_add(dev, &alt_port->bridge);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
fwnode_handle_put(fwnode);
|
||||
return ret;
|
||||
}
|
||||
|
||||
alt_port->dp_alt.svid = USB_TYPEC_DP_SID;
|
||||
alt_port->dp_alt.mode = USB_TYPEC_DP_MODE;
|
||||
alt_port->dp_alt.active = 1;
|
||||
|
||||
alt_port->typec_mux = fwnode_typec_mux_get(fwnode);
|
||||
if (IS_ERR(alt_port->typec_mux))
|
||||
if (IS_ERR(alt_port->typec_mux)) {
|
||||
fwnode_handle_put(fwnode);
|
||||
return dev_err_probe(dev, PTR_ERR(alt_port->typec_mux),
|
||||
"failed to acquire mode-switch for port: %d\n",
|
||||
port);
|
||||
}
|
||||
|
||||
ret = devm_add_action_or_reset(dev, pmic_glink_altmode_put_mux,
|
||||
alt_port->typec_mux);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
fwnode_handle_put(fwnode);
|
||||
return ret;
|
||||
}
|
||||
|
||||
alt_port->typec_retimer = fwnode_typec_retimer_get(fwnode);
|
||||
if (IS_ERR(alt_port->typec_retimer))
|
||||
if (IS_ERR(alt_port->typec_retimer)) {
|
||||
fwnode_handle_put(fwnode);
|
||||
return dev_err_probe(dev, PTR_ERR(alt_port->typec_retimer),
|
||||
"failed to acquire retimer-switch for port: %d\n",
|
||||
port);
|
||||
}
|
||||
|
||||
ret = devm_add_action_or_reset(dev, pmic_glink_altmode_put_retimer,
|
||||
alt_port->typec_retimer);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
fwnode_handle_put(fwnode);
|
||||
return ret;
|
||||
}
|
||||
|
||||
alt_port->typec_switch = fwnode_typec_switch_get(fwnode);
|
||||
if (IS_ERR(alt_port->typec_switch))
|
||||
if (IS_ERR(alt_port->typec_switch)) {
|
||||
fwnode_handle_put(fwnode);
|
||||
return dev_err_probe(dev, PTR_ERR(alt_port->typec_switch),
|
||||
"failed to acquire orientation-switch for port: %d\n",
|
||||
port);
|
||||
}
|
||||
|
||||
ret = devm_add_action_or_reset(dev, pmic_glink_altmode_put_switch,
|
||||
alt_port->typec_switch);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
fwnode_handle_put(fwnode);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
altmode->client = devm_pmic_glink_register_client(dev,
|
||||
|
@ -199,7 +199,7 @@ u32 geni_se_get_qup_hw_version(struct geni_se *se)
|
||||
|
||||
return readl_relaxed(wrapper->base + QUP_HW_VER_REG);
|
||||
}
|
||||
EXPORT_SYMBOL(geni_se_get_qup_hw_version);
|
||||
EXPORT_SYMBOL_GPL(geni_se_get_qup_hw_version);
|
||||
|
||||
static void geni_se_io_set_mode(void __iomem *base)
|
||||
{
|
||||
@ -272,7 +272,7 @@ void geni_se_init(struct geni_se *se, u32 rx_wm, u32 rx_rfr)
|
||||
val |= S_COMMON_GENI_S_IRQ_EN;
|
||||
writel_relaxed(val, se->base + SE_GENI_S_IRQ_EN);
|
||||
}
|
||||
EXPORT_SYMBOL(geni_se_init);
|
||||
EXPORT_SYMBOL_GPL(geni_se_init);
|
||||
|
||||
static void geni_se_select_fifo_mode(struct geni_se *se)
|
||||
{
|
||||
@ -364,7 +364,7 @@ void geni_se_select_mode(struct geni_se *se, enum geni_se_xfer_mode mode)
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(geni_se_select_mode);
|
||||
EXPORT_SYMBOL_GPL(geni_se_select_mode);
|
||||
|
||||
/**
|
||||
* DOC: Overview
|
||||
@ -481,7 +481,7 @@ void geni_se_config_packing(struct geni_se *se, int bpw, int pack_words,
|
||||
if (pack_words || bpw == 32)
|
||||
writel_relaxed(bpw / 16, se->base + SE_GENI_BYTE_GRAN);
|
||||
}
|
||||
EXPORT_SYMBOL(geni_se_config_packing);
|
||||
EXPORT_SYMBOL_GPL(geni_se_config_packing);
|
||||
|
||||
static void geni_se_clks_off(struct geni_se *se)
|
||||
{
|
||||
@ -512,7 +512,7 @@ int geni_se_resources_off(struct geni_se *se)
|
||||
geni_se_clks_off(se);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(geni_se_resources_off);
|
||||
EXPORT_SYMBOL_GPL(geni_se_resources_off);
|
||||
|
||||
static int geni_se_clks_on(struct geni_se *se)
|
||||
{
|
||||
@ -553,7 +553,7 @@ int geni_se_resources_on(struct geni_se *se)
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(geni_se_resources_on);
|
||||
EXPORT_SYMBOL_GPL(geni_se_resources_on);
|
||||
|
||||
/**
|
||||
* geni_se_clk_tbl_get() - Get the clock table to program DFS
|
||||
@ -594,7 +594,7 @@ int geni_se_clk_tbl_get(struct geni_se *se, unsigned long **tbl)
|
||||
*tbl = se->clk_perf_tbl;
|
||||
return se->num_clk_levels;
|
||||
}
|
||||
EXPORT_SYMBOL(geni_se_clk_tbl_get);
|
||||
EXPORT_SYMBOL_GPL(geni_se_clk_tbl_get);
|
||||
|
||||
/**
|
||||
* geni_se_clk_freq_match() - Get the matching or closest SE clock frequency
|
||||
@ -656,7 +656,7 @@ int geni_se_clk_freq_match(struct geni_se *se, unsigned long req_freq,
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(geni_se_clk_freq_match);
|
||||
EXPORT_SYMBOL_GPL(geni_se_clk_freq_match);
|
||||
|
||||
#define GENI_SE_DMA_DONE_EN BIT(0)
|
||||
#define GENI_SE_DMA_EOT_EN BIT(1)
|
||||
@ -684,7 +684,7 @@ void geni_se_tx_init_dma(struct geni_se *se, dma_addr_t iova, size_t len)
|
||||
writel_relaxed(GENI_SE_DMA_EOT_BUF, se->base + SE_DMA_TX_ATTR);
|
||||
writel(len, se->base + SE_DMA_TX_LEN);
|
||||
}
|
||||
EXPORT_SYMBOL(geni_se_tx_init_dma);
|
||||
EXPORT_SYMBOL_GPL(geni_se_tx_init_dma);
|
||||
|
||||
/**
|
||||
* geni_se_tx_dma_prep() - Prepare the serial engine for TX DMA transfer
|
||||
@ -712,7 +712,7 @@ int geni_se_tx_dma_prep(struct geni_se *se, void *buf, size_t len,
|
||||
geni_se_tx_init_dma(se, *iova, len);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(geni_se_tx_dma_prep);
|
||||
EXPORT_SYMBOL_GPL(geni_se_tx_dma_prep);
|
||||
|
||||
/**
|
||||
* geni_se_rx_init_dma() - Initiate RX DMA transfer on the serial engine
|
||||
@ -736,7 +736,7 @@ void geni_se_rx_init_dma(struct geni_se *se, dma_addr_t iova, size_t len)
|
||||
writel_relaxed(0, se->base + SE_DMA_RX_ATTR);
|
||||
writel(len, se->base + SE_DMA_RX_LEN);
|
||||
}
|
||||
EXPORT_SYMBOL(geni_se_rx_init_dma);
|
||||
EXPORT_SYMBOL_GPL(geni_se_rx_init_dma);
|
||||
|
||||
/**
|
||||
* geni_se_rx_dma_prep() - Prepare the serial engine for RX DMA transfer
|
||||
@ -764,7 +764,7 @@ int geni_se_rx_dma_prep(struct geni_se *se, void *buf, size_t len,
|
||||
geni_se_rx_init_dma(se, *iova, len);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(geni_se_rx_dma_prep);
|
||||
EXPORT_SYMBOL_GPL(geni_se_rx_dma_prep);
|
||||
|
||||
/**
|
||||
* geni_se_tx_dma_unprep() - Unprepare the serial engine after TX DMA transfer
|
||||
@ -781,7 +781,7 @@ void geni_se_tx_dma_unprep(struct geni_se *se, dma_addr_t iova, size_t len)
|
||||
if (!dma_mapping_error(wrapper->dev, iova))
|
||||
dma_unmap_single(wrapper->dev, iova, len, DMA_TO_DEVICE);
|
||||
}
|
||||
EXPORT_SYMBOL(geni_se_tx_dma_unprep);
|
||||
EXPORT_SYMBOL_GPL(geni_se_tx_dma_unprep);
|
||||
|
||||
/**
|
||||
* geni_se_rx_dma_unprep() - Unprepare the serial engine after RX DMA transfer
|
||||
@ -798,7 +798,7 @@ void geni_se_rx_dma_unprep(struct geni_se *se, dma_addr_t iova, size_t len)
|
||||
if (!dma_mapping_error(wrapper->dev, iova))
|
||||
dma_unmap_single(wrapper->dev, iova, len, DMA_FROM_DEVICE);
|
||||
}
|
||||
EXPORT_SYMBOL(geni_se_rx_dma_unprep);
|
||||
EXPORT_SYMBOL_GPL(geni_se_rx_dma_unprep);
|
||||
|
||||
int geni_icc_get(struct geni_se *se, const char *icc_ddr)
|
||||
{
|
||||
@ -827,7 +827,7 @@ err:
|
||||
return err;
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL(geni_icc_get);
|
||||
EXPORT_SYMBOL_GPL(geni_icc_get);
|
||||
|
||||
int geni_icc_set_bw(struct geni_se *se)
|
||||
{
|
||||
@ -845,7 +845,7 @@ int geni_icc_set_bw(struct geni_se *se)
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(geni_icc_set_bw);
|
||||
EXPORT_SYMBOL_GPL(geni_icc_set_bw);
|
||||
|
||||
void geni_icc_set_tag(struct geni_se *se, u32 tag)
|
||||
{
|
||||
@ -854,7 +854,7 @@ void geni_icc_set_tag(struct geni_se *se, u32 tag)
|
||||
for (i = 0; i < ARRAY_SIZE(se->icc_paths); i++)
|
||||
icc_set_tag(se->icc_paths[i].path, tag);
|
||||
}
|
||||
EXPORT_SYMBOL(geni_icc_set_tag);
|
||||
EXPORT_SYMBOL_GPL(geni_icc_set_tag);
|
||||
|
||||
/* To do: Replace this by icc_bulk_enable once it's implemented in ICC core */
|
||||
int geni_icc_enable(struct geni_se *se)
|
||||
@ -872,7 +872,7 @@ int geni_icc_enable(struct geni_se *se)
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(geni_icc_enable);
|
||||
EXPORT_SYMBOL_GPL(geni_icc_enable);
|
||||
|
||||
int geni_icc_disable(struct geni_se *se)
|
||||
{
|
||||
@ -889,7 +889,7 @@ int geni_icc_disable(struct geni_se *se)
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(geni_icc_disable);
|
||||
EXPORT_SYMBOL_GPL(geni_icc_disable);
|
||||
|
||||
static int geni_se_probe(struct platform_device *pdev)
|
||||
{
|
||||
|
@ -260,7 +260,7 @@ int qmp_send(struct qmp *qmp, const char *fmt, ...)
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(qmp_send);
|
||||
EXPORT_SYMBOL_GPL(qmp_send);
|
||||
|
||||
static int qmp_qdss_clk_prepare(struct clk_hw *hw)
|
||||
{
|
||||
@ -458,7 +458,7 @@ struct qmp *qmp_get(struct device *dev)
|
||||
}
|
||||
return qmp;
|
||||
}
|
||||
EXPORT_SYMBOL(qmp_get);
|
||||
EXPORT_SYMBOL_GPL(qmp_get);
|
||||
|
||||
/**
|
||||
* qmp_put() - release a qmp handle
|
||||
@ -473,7 +473,7 @@ void qmp_put(struct qmp *qmp)
|
||||
if (!IS_ERR_OR_NULL(qmp))
|
||||
put_device(qmp->dev);
|
||||
}
|
||||
EXPORT_SYMBOL(qmp_put);
|
||||
EXPORT_SYMBOL_GPL(qmp_put);
|
||||
|
||||
static int qmp_probe(struct platform_device *pdev)
|
||||
{
|
||||
@ -533,7 +533,7 @@ err_free_mbox:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qmp_remove(struct platform_device *pdev)
|
||||
static void qmp_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct qmp *qmp = platform_get_drvdata(pdev);
|
||||
|
||||
@ -542,8 +542,6 @@ static int qmp_remove(struct platform_device *pdev)
|
||||
|
||||
qmp_close(qmp);
|
||||
mbox_free_channel(qmp->mbox_chan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id qmp_dt_match[] = {
|
||||
@ -565,7 +563,7 @@ static struct platform_driver qmp_driver = {
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = qmp_probe,
|
||||
.remove = qmp_remove,
|
||||
.remove_new = qmp_remove,
|
||||
};
|
||||
module_platform_driver(qmp_driver);
|
||||
|
||||
|
@ -212,13 +212,11 @@ static int gsbi_probe(struct platform_device *pdev)
|
||||
return of_platform_populate(node, NULL, NULL, &pdev->dev);
|
||||
}
|
||||
|
||||
static int gsbi_remove(struct platform_device *pdev)
|
||||
static void gsbi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct gsbi_info *gsbi = platform_get_drvdata(pdev);
|
||||
|
||||
clk_disable_unprepare(gsbi->hclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id gsbi_dt_match[] = {
|
||||
@ -234,7 +232,7 @@ static struct platform_driver gsbi_driver = {
|
||||
.of_match_table = gsbi_dt_match,
|
||||
},
|
||||
.probe = gsbi_probe,
|
||||
.remove = gsbi_remove,
|
||||
.remove_new = gsbi_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(gsbi_driver);
|
||||
|
@ -216,13 +216,11 @@ static int qcom_stats_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_stats_remove(struct platform_device *pdev)
|
||||
static void qcom_stats_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dentry *root = platform_get_drvdata(pdev);
|
||||
|
||||
debugfs_remove_recursive(root);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct stats_config rpm_data = {
|
||||
@ -272,7 +270,7 @@ MODULE_DEVICE_TABLE(of, qcom_stats_table);
|
||||
|
||||
static struct platform_driver qcom_stats = {
|
||||
.probe = qcom_stats_probe,
|
||||
.remove = qcom_stats_remove,
|
||||
.remove_new = qcom_stats_remove,
|
||||
.driver = {
|
||||
.name = "qcom_stats",
|
||||
.of_match_table = qcom_stats_table,
|
||||
|
@ -754,7 +754,7 @@ void *qmi_encode_message(int type, unsigned int msg_id, size_t *len,
|
||||
|
||||
return msg;
|
||||
}
|
||||
EXPORT_SYMBOL(qmi_encode_message);
|
||||
EXPORT_SYMBOL_GPL(qmi_encode_message);
|
||||
|
||||
/**
|
||||
* qmi_decode_message() - Decode QMI encoded message to C structure
|
||||
@ -778,7 +778,7 @@ int qmi_decode_message(const void *buf, size_t len,
|
||||
return qmi_decode(ei, c_struct, buf + sizeof(struct qmi_header),
|
||||
len - sizeof(struct qmi_header), 1);
|
||||
}
|
||||
EXPORT_SYMBOL(qmi_decode_message);
|
||||
EXPORT_SYMBOL_GPL(qmi_decode_message);
|
||||
|
||||
/* Common header in all QMI responses */
|
||||
const struct qmi_elem_info qmi_response_type_v01_ei[] = {
|
||||
@ -810,7 +810,7 @@ const struct qmi_elem_info qmi_response_type_v01_ei[] = {
|
||||
.ei_array = NULL,
|
||||
},
|
||||
};
|
||||
EXPORT_SYMBOL(qmi_response_type_v01_ei);
|
||||
EXPORT_SYMBOL_GPL(qmi_response_type_v01_ei);
|
||||
|
||||
MODULE_DESCRIPTION("QMI encoder/decoder helper");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -223,7 +223,7 @@ int qmi_add_lookup(struct qmi_handle *qmi, unsigned int service,
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(qmi_add_lookup);
|
||||
EXPORT_SYMBOL_GPL(qmi_add_lookup);
|
||||
|
||||
static void qmi_send_new_server(struct qmi_handle *qmi, struct qmi_service *svc)
|
||||
{
|
||||
@ -287,7 +287,7 @@ int qmi_add_server(struct qmi_handle *qmi, unsigned int service,
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(qmi_add_server);
|
||||
EXPORT_SYMBOL_GPL(qmi_add_server);
|
||||
|
||||
/**
|
||||
* qmi_txn_init() - allocate transaction id within the given QMI handle
|
||||
@ -328,7 +328,7 @@ int qmi_txn_init(struct qmi_handle *qmi, struct qmi_txn *txn,
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(qmi_txn_init);
|
||||
EXPORT_SYMBOL_GPL(qmi_txn_init);
|
||||
|
||||
/**
|
||||
* qmi_txn_wait() - wait for a response on a transaction
|
||||
@ -359,7 +359,7 @@ int qmi_txn_wait(struct qmi_txn *txn, unsigned long timeout)
|
||||
else
|
||||
return txn->result;
|
||||
}
|
||||
EXPORT_SYMBOL(qmi_txn_wait);
|
||||
EXPORT_SYMBOL_GPL(qmi_txn_wait);
|
||||
|
||||
/**
|
||||
* qmi_txn_cancel() - cancel an ongoing transaction
|
||||
@ -375,7 +375,7 @@ void qmi_txn_cancel(struct qmi_txn *txn)
|
||||
mutex_unlock(&txn->lock);
|
||||
mutex_unlock(&qmi->txn_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(qmi_txn_cancel);
|
||||
EXPORT_SYMBOL_GPL(qmi_txn_cancel);
|
||||
|
||||
/**
|
||||
* qmi_invoke_handler() - find and invoke a handler for a message
|
||||
@ -676,7 +676,7 @@ err_free_recv_buf:
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(qmi_handle_init);
|
||||
EXPORT_SYMBOL_GPL(qmi_handle_init);
|
||||
|
||||
/**
|
||||
* qmi_handle_release() - release the QMI client handle
|
||||
@ -717,7 +717,7 @@ void qmi_handle_release(struct qmi_handle *qmi)
|
||||
kfree(svc);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(qmi_handle_release);
|
||||
EXPORT_SYMBOL_GPL(qmi_handle_release);
|
||||
|
||||
/**
|
||||
* qmi_send_message() - send a QMI message
|
||||
@ -796,7 +796,7 @@ ssize_t qmi_send_request(struct qmi_handle *qmi, struct sockaddr_qrtr *sq,
|
||||
return qmi_send_message(qmi, sq, txn, QMI_REQUEST, msg_id, len, ei,
|
||||
c_struct);
|
||||
}
|
||||
EXPORT_SYMBOL(qmi_send_request);
|
||||
EXPORT_SYMBOL_GPL(qmi_send_request);
|
||||
|
||||
/**
|
||||
* qmi_send_response() - send a response QMI message
|
||||
@ -817,7 +817,7 @@ ssize_t qmi_send_response(struct qmi_handle *qmi, struct sockaddr_qrtr *sq,
|
||||
return qmi_send_message(qmi, sq, txn, QMI_RESPONSE, msg_id, len, ei,
|
||||
c_struct);
|
||||
}
|
||||
EXPORT_SYMBOL(qmi_send_response);
|
||||
EXPORT_SYMBOL_GPL(qmi_send_response);
|
||||
|
||||
/**
|
||||
* qmi_send_indication() - send an indication QMI message
|
||||
@ -851,4 +851,4 @@ ssize_t qmi_send_indication(struct qmi_handle *qmi, struct sockaddr_qrtr *sq,
|
||||
|
||||
return rval;
|
||||
}
|
||||
EXPORT_SYMBOL(qmi_send_indication);
|
||||
EXPORT_SYMBOL_GPL(qmi_send_indication);
|
||||
|
@ -200,6 +200,15 @@ static int qcom_rmtfs_mem_probe(struct platform_device *pdev)
|
||||
rmtfs_mem->client_id = client_id;
|
||||
rmtfs_mem->size = rmem->size;
|
||||
|
||||
/*
|
||||
* If requested, discard the first and last 4k block in order to ensure
|
||||
* that the rmtfs region isn't adjacent to other protected regions.
|
||||
*/
|
||||
if (of_property_read_bool(node, "qcom,use-guard-pages")) {
|
||||
rmtfs_mem->addr += SZ_4K;
|
||||
rmtfs_mem->size -= 2 * SZ_4K;
|
||||
}
|
||||
|
||||
device_initialize(&rmtfs_mem->dev);
|
||||
rmtfs_mem->dev.parent = &pdev->dev;
|
||||
rmtfs_mem->dev.groups = qcom_rmtfs_mem_groups;
|
||||
@ -281,7 +290,7 @@ put_device:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qcom_rmtfs_mem_remove(struct platform_device *pdev)
|
||||
static void qcom_rmtfs_mem_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct qcom_rmtfs_mem *rmtfs_mem = dev_get_drvdata(&pdev->dev);
|
||||
struct qcom_scm_vmperm perm;
|
||||
@ -296,8 +305,6 @@ static int qcom_rmtfs_mem_remove(struct platform_device *pdev)
|
||||
|
||||
cdev_device_del(&rmtfs_mem->cdev, &rmtfs_mem->dev);
|
||||
put_device(&rmtfs_mem->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id qcom_rmtfs_mem_of_match[] = {
|
||||
@ -308,7 +315,7 @@ MODULE_DEVICE_TABLE(of, qcom_rmtfs_mem_of_match);
|
||||
|
||||
static struct platform_driver qcom_rmtfs_mem_driver = {
|
||||
.probe = qcom_rmtfs_mem_probe,
|
||||
.remove = qcom_rmtfs_mem_remove,
|
||||
.remove_new = qcom_rmtfs_mem_remove,
|
||||
.driver = {
|
||||
.name = "qcom_rmtfs_mem",
|
||||
.of_match_table = qcom_rmtfs_mem_of_match,
|
||||
|
@ -239,7 +239,7 @@ int rpmh_write_async(const struct device *dev, enum rpmh_state state,
|
||||
|
||||
return __rpmh_write(dev, state, rpm_msg);
|
||||
}
|
||||
EXPORT_SYMBOL(rpmh_write_async);
|
||||
EXPORT_SYMBOL_GPL(rpmh_write_async);
|
||||
|
||||
/**
|
||||
* rpmh_write: Write a set of RPMH commands and block until response
|
||||
@ -270,7 +270,7 @@ int rpmh_write(const struct device *dev, enum rpmh_state state,
|
||||
WARN_ON(!ret);
|
||||
return (ret > 0) ? 0 : -ETIMEDOUT;
|
||||
}
|
||||
EXPORT_SYMBOL(rpmh_write);
|
||||
EXPORT_SYMBOL_GPL(rpmh_write);
|
||||
|
||||
static void cache_batch(struct rpmh_ctrlr *ctrlr, struct batch_cache_req *req)
|
||||
{
|
||||
@ -395,7 +395,7 @@ exit:
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(rpmh_write_batch);
|
||||
EXPORT_SYMBOL_GPL(rpmh_write_batch);
|
||||
|
||||
static int is_req_valid(struct cache_req *req)
|
||||
{
|
||||
@ -500,4 +500,4 @@ void rpmh_invalidate(const struct device *dev)
|
||||
ctrlr->dirty = true;
|
||||
spin_unlock_irqrestore(&ctrlr->cache_lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(rpmh_invalidate);
|
||||
EXPORT_SYMBOL_GPL(rpmh_invalidate);
|
||||
|
@ -142,7 +142,7 @@ out:
|
||||
mutex_unlock(&rpm->lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_rpm_smd_write);
|
||||
EXPORT_SYMBOL_GPL(qcom_rpm_smd_write);
|
||||
|
||||
static int qcom_smd_rpm_callback(struct rpmsg_device *rpdev,
|
||||
void *data,
|
||||
|
@ -285,7 +285,7 @@ struct qcom_smem {
|
||||
struct smem_partition partitions[SMEM_HOST_COUNT];
|
||||
|
||||
unsigned num_regions;
|
||||
struct smem_region regions[];
|
||||
struct smem_region regions[] __counted_by(num_regions);
|
||||
};
|
||||
|
||||
static void *
|
||||
@ -368,7 +368,7 @@ bool qcom_smem_is_available(void)
|
||||
{
|
||||
return !!__smem;
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_smem_is_available);
|
||||
EXPORT_SYMBOL_GPL(qcom_smem_is_available);
|
||||
|
||||
static int qcom_smem_alloc_private(struct qcom_smem *smem,
|
||||
struct smem_partition *part,
|
||||
@ -1187,14 +1187,12 @@ static int qcom_smem_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_smem_remove(struct platform_device *pdev)
|
||||
static void qcom_smem_remove(struct platform_device *pdev)
|
||||
{
|
||||
platform_device_unregister(__smem->socinfo);
|
||||
|
||||
hwspin_lock_free(__smem->hwlock);
|
||||
__smem = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id qcom_smem_of_match[] = {
|
||||
@ -1205,7 +1203,7 @@ MODULE_DEVICE_TABLE(of, qcom_smem_of_match);
|
||||
|
||||
static struct platform_driver qcom_smem_driver = {
|
||||
.probe = qcom_smem_probe,
|
||||
.remove = qcom_smem_remove,
|
||||
.remove_new = qcom_smem_remove,
|
||||
.driver = {
|
||||
.name = "qcom-smem",
|
||||
.of_match_table = qcom_smem_of_match,
|
||||
|
@ -660,7 +660,7 @@ report_read_failure:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int qcom_smp2p_remove(struct platform_device *pdev)
|
||||
static void qcom_smp2p_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct qcom_smp2p *smp2p = platform_get_drvdata(pdev);
|
||||
struct smp2p_entry *entry;
|
||||
@ -676,8 +676,6 @@ static int qcom_smp2p_remove(struct platform_device *pdev)
|
||||
mbox_free_channel(smp2p->mbox_chan);
|
||||
|
||||
smp2p->out->valid_entries = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id qcom_smp2p_of_match[] = {
|
||||
@ -688,7 +686,7 @@ MODULE_DEVICE_TABLE(of, qcom_smp2p_of_match);
|
||||
|
||||
static struct platform_driver qcom_smp2p_driver = {
|
||||
.probe = qcom_smp2p_probe,
|
||||
.remove = qcom_smp2p_remove,
|
||||
.remove_new = qcom_smp2p_remove,
|
||||
.driver = {
|
||||
.name = "qcom_smp2p",
|
||||
.of_match_table = qcom_smp2p_of_match,
|
||||
|
@ -613,7 +613,7 @@ out_put:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qcom_smsm_remove(struct platform_device *pdev)
|
||||
static void qcom_smsm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct qcom_smsm *smsm = platform_get_drvdata(pdev);
|
||||
unsigned id;
|
||||
@ -623,8 +623,6 @@ static int qcom_smsm_remove(struct platform_device *pdev)
|
||||
irq_domain_remove(smsm->entries[id].domain);
|
||||
|
||||
qcom_smem_state_unregister(smsm->state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id qcom_smsm_of_match[] = {
|
||||
@ -635,7 +633,7 @@ MODULE_DEVICE_TABLE(of, qcom_smsm_of_match);
|
||||
|
||||
static struct platform_driver qcom_smsm_driver = {
|
||||
.probe = qcom_smsm_probe,
|
||||
.remove = qcom_smsm_remove,
|
||||
.remove_new = qcom_smsm_remove,
|
||||
.driver = {
|
||||
.name = "qcom-smsm",
|
||||
.of_match_table = qcom_smsm_of_match,
|
||||
|
@ -117,6 +117,12 @@ static const char *const pmic_models[] = {
|
||||
[55] = "PM2250",
|
||||
[58] = "PM8450",
|
||||
[65] = "PM8010",
|
||||
[69] = "PM8550VS",
|
||||
[70] = "PM8550VE",
|
||||
[71] = "PM8550B",
|
||||
[72] = "PMR735D",
|
||||
[73] = "PM8550",
|
||||
[74] = "PMK8550",
|
||||
};
|
||||
|
||||
struct socinfo_params {
|
||||
@ -349,6 +355,7 @@ static const struct soc_id soc_id[] = {
|
||||
{ qcom_board_id(SDA439) },
|
||||
{ qcom_board_id(SDA429) },
|
||||
{ qcom_board_id(SM7150) },
|
||||
{ qcom_board_id(SM7150P) },
|
||||
{ qcom_board_id(IPQ8070) },
|
||||
{ qcom_board_id(IPQ8071) },
|
||||
{ qcom_board_id(QM215) },
|
||||
@ -359,6 +366,9 @@ static const struct soc_id soc_id[] = {
|
||||
{ qcom_board_id(SM6125) },
|
||||
{ qcom_board_id(IPQ8070A) },
|
||||
{ qcom_board_id(IPQ8071A) },
|
||||
{ qcom_board_id(IPQ8172) },
|
||||
{ qcom_board_id(IPQ8173) },
|
||||
{ qcom_board_id(IPQ8174) },
|
||||
{ qcom_board_id(IPQ6018) },
|
||||
{ qcom_board_id(IPQ6028) },
|
||||
{ qcom_board_id(SDM429W) },
|
||||
@ -389,6 +399,7 @@ static const struct soc_id soc_id[] = {
|
||||
{ qcom_board_id_named(SM8450_3, "SM8450") },
|
||||
{ qcom_board_id(SC7280) },
|
||||
{ qcom_board_id(SC7180P) },
|
||||
{ qcom_board_id(QCM6490) },
|
||||
{ qcom_board_id(IPQ5000) },
|
||||
{ qcom_board_id(IPQ0509) },
|
||||
{ qcom_board_id(IPQ0518) },
|
||||
@ -776,20 +787,18 @@ static int qcom_socinfo_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_socinfo_remove(struct platform_device *pdev)
|
||||
static void qcom_socinfo_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct qcom_socinfo *qs = platform_get_drvdata(pdev);
|
||||
|
||||
soc_device_unregister(qs->soc_dev);
|
||||
|
||||
socinfo_debugfs_exit(qs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver qcom_socinfo_driver = {
|
||||
.probe = qcom_socinfo_probe,
|
||||
.remove = qcom_socinfo_remove,
|
||||
.remove_new = qcom_socinfo_remove,
|
||||
.driver = {
|
||||
.name = "qcom-socinfo",
|
||||
},
|
||||
|
@ -287,7 +287,7 @@ struct rpmsg_endpoint *qcom_wcnss_open_channel(void *wcnss, const char *name, rp
|
||||
|
||||
return rpmsg_create_ept(_wcnss->channel->rpdev, cb, priv, chinfo);
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_wcnss_open_channel);
|
||||
EXPORT_SYMBOL_GPL(qcom_wcnss_open_channel);
|
||||
|
||||
static void wcnss_async_probe(struct work_struct *work)
|
||||
{
|
||||
@ -355,7 +355,6 @@ static struct rpmsg_driver wcnss_ctrl_driver = {
|
||||
.callback = wcnss_ctrl_smd_callback,
|
||||
.drv = {
|
||||
.name = "qcom_wcnss_ctrl",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = wcnss_ctrl_of_match,
|
||||
},
|
||||
};
|
||||
|
@ -319,6 +319,12 @@ config ARCH_R9A07G054
|
||||
help
|
||||
This enables support for the Renesas RZ/V2L SoC variants.
|
||||
|
||||
config ARCH_R9A08G045
|
||||
bool "ARM64 Platform support for RZ/G3S"
|
||||
select ARCH_RZG2L
|
||||
help
|
||||
This enables support for the Renesas RZ/G3S SoC variants.
|
||||
|
||||
config ARCH_R9A09G011
|
||||
bool "ARM64 Platform support for RZ/V2M"
|
||||
select PM
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include <linux/string.h>
|
||||
#include <linux/sys_soc.h>
|
||||
|
||||
|
||||
struct renesas_family {
|
||||
const char name[16];
|
||||
u32 reg; /* CCCR or PRR, if not in DT */
|
||||
@ -72,6 +71,10 @@ static const struct renesas_family fam_rzg2ul __initconst __maybe_unused = {
|
||||
.name = "RZ/G2UL",
|
||||
};
|
||||
|
||||
static const struct renesas_family fam_rzg3s __initconst __maybe_unused = {
|
||||
.name = "RZ/G3S",
|
||||
};
|
||||
|
||||
static const struct renesas_family fam_rzv2l __initconst __maybe_unused = {
|
||||
.name = "RZ/V2L",
|
||||
};
|
||||
@ -85,7 +88,6 @@ static const struct renesas_family fam_shmobile __initconst __maybe_unused = {
|
||||
.reg = 0xe600101c, /* CCCR (Common Chip Code Register) */
|
||||
};
|
||||
|
||||
|
||||
struct renesas_soc {
|
||||
const struct renesas_family *family;
|
||||
u32 id;
|
||||
@ -170,6 +172,11 @@ static const struct renesas_soc soc_rz_g2ul __initconst __maybe_unused = {
|
||||
.id = 0x8450447,
|
||||
};
|
||||
|
||||
static const struct renesas_soc soc_rz_g3s __initconst __maybe_unused = {
|
||||
.family = &fam_rzg3s,
|
||||
.id = 0x85e0447,
|
||||
};
|
||||
|
||||
static const struct renesas_soc soc_rz_v2l __initconst __maybe_unused = {
|
||||
.family = &fam_rzv2l,
|
||||
.id = 0x8447447,
|
||||
@ -386,6 +393,9 @@ static const struct of_device_id renesas_socs[] __initconst __maybe_unused = {
|
||||
#ifdef CONFIG_ARCH_R9A07G054
|
||||
{ .compatible = "renesas,r9a07g054", .data = &soc_rz_v2l },
|
||||
#endif
|
||||
#ifdef CONFIG_ARCH_R9A08G045
|
||||
{ .compatible = "renesas,r9a08g045", .data = &soc_rz_g3s },
|
||||
#endif
|
||||
#ifdef CONFIG_ARCH_R9A09G011
|
||||
{ .compatible = "renesas,r9a09g011", .data = &soc_rz_v2m },
|
||||
#endif
|
||||
@ -429,6 +439,7 @@ static const struct of_device_id renesas_ids[] __initconst = {
|
||||
{ .compatible = "renesas,r9a07g043-sysc", .data = &id_rzg2l },
|
||||
{ .compatible = "renesas,r9a07g044-sysc", .data = &id_rzg2l },
|
||||
{ .compatible = "renesas,r9a07g054-sysc", .data = &id_rzg2l },
|
||||
{ .compatible = "renesas,r9a08g045-sysc", .data = &id_rzg2l },
|
||||
{ .compatible = "renesas,r9a09g011-sys", .data = &id_rzv2m },
|
||||
{ .compatible = "renesas,prr", .data = &id_prr },
|
||||
{ /* sentinel */ }
|
||||
|
@ -687,7 +687,7 @@ unreg_notify:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rockchip_iodomain_remove(struct platform_device *pdev)
|
||||
static void rockchip_iodomain_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rockchip_iodomain *iod = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
@ -699,13 +699,11 @@ static int rockchip_iodomain_remove(struct platform_device *pdev)
|
||||
regulator_unregister_notifier(io_supply->reg,
|
||||
&io_supply->nb);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver rockchip_iodomain_driver = {
|
||||
.probe = rockchip_iodomain_probe,
|
||||
.remove = rockchip_iodomain_remove,
|
||||
.remove_new = rockchip_iodomain_remove,
|
||||
.driver = {
|
||||
.name = "rockchip-iodomain",
|
||||
.of_match_table = rockchip_iodomain_match,
|
||||
|
@ -158,13 +158,11 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int exynos_chipid_remove(struct platform_device *pdev)
|
||||
static void exynos_chipid_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct soc_device *soc_dev = platform_get_drvdata(pdev);
|
||||
|
||||
soc_device_unregister(soc_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct exynos_chipid_variant exynos4210_chipid_drv_data = {
|
||||
@ -197,7 +195,7 @@ static struct platform_driver exynos_chipid_driver = {
|
||||
.of_match_table = exynos_chipid_of_device_ids,
|
||||
},
|
||||
.probe = exynos_chipid_probe,
|
||||
.remove = exynos_chipid_remove,
|
||||
.remove_new = exynos_chipid_remove,
|
||||
};
|
||||
module_platform_driver(exynos_chipid_driver);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
if SOC_SIFIVE || SOC_STARFIVE
|
||||
if ARCH_SIFIVE || ARCH_STARFIVE
|
||||
|
||||
config SIFIVE_CCACHE
|
||||
bool "Sifive Composable Cache controller"
|
||||
|
@ -2293,7 +2293,7 @@ static int tegra194_cbb_probe(struct platform_device *pdev)
|
||||
return tegra_cbb_register(&cbb->base);
|
||||
}
|
||||
|
||||
static int tegra194_cbb_remove(struct platform_device *pdev)
|
||||
static void tegra194_cbb_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra194_cbb *cbb = platform_get_drvdata(pdev);
|
||||
struct tegra_cbb *noc, *tmp;
|
||||
@ -2311,8 +2311,6 @@ static int tegra194_cbb_remove(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&cbb_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused tegra194_cbb_resume_noirq(struct device *dev)
|
||||
@ -2332,7 +2330,7 @@ static const struct dev_pm_ops tegra194_cbb_pm = {
|
||||
|
||||
static struct platform_driver tegra194_cbb_driver = {
|
||||
.probe = tegra194_cbb_probe,
|
||||
.remove = tegra194_cbb_remove,
|
||||
.remove_new = tegra194_cbb_remove,
|
||||
.driver = {
|
||||
.name = "tegra194-cbb",
|
||||
.of_match_table = of_match_ptr(tegra194_cbb_match),
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user