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:
Linus Torvalds 2023-11-01 14:46:51 -10:00
commit 385903a7ec
122 changed files with 4259 additions and 697 deletions

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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:

View File

@ -40,6 +40,7 @@ patternProperties:
".*@[0-9]+$":
type: object
$ref: mc-peripheral-props.yaml#
additionalProperties: true
required:
- compatible

View File

@ -80,6 +80,8 @@ properties:
patternProperties:
"flash@[0-9a-f]+$":
type: object
additionalProperties: true
properties:
compatible:
contains:

View File

@ -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

View File

@ -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:

View File

@ -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: >

View File

@ -22,6 +22,7 @@ properties:
compatible:
enum:
- mediatek,mt8183-svs
- mediatek,mt8188-svs
- mediatek,mt8192-svs
reg:

View File

@ -52,6 +52,8 @@ properties:
iommus:
maxItems: 1
dma-coherent: true
required:
- compatible
- reg

View File

@ -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

View File

@ -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_

View File

@ -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.

View File

@ -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);

View File

@ -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

View File

@ -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 {

View File

@ -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 {

View File

@ -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;

View File

@ -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"

View File

@ -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/

View File

@ -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);

View File

@ -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();
}

View File

@ -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)

View File

@ -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)

View File

@ -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);
}

View File

@ -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},

View File

@ -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,

View File

@ -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) {

View File

@ -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) {

View File

@ -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 },
{},
};

View File

@ -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;

View 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

View 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

View 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");

View 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");

View File

@ -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;
}

View File

@ -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

View File

@ -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.

View File

@ -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,
};

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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) {

View File

@ -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);

View File

@ -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/

View 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

View 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");

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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.

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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,

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -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,

View File

@ -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);

View File

@ -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,

View File

@ -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);

View File

@ -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),

View File

@ -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;

View File

@ -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)

View File

@ -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,

View File

@ -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);

View File

@ -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);

View File

@ -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,

View File

@ -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");

View File

@ -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,

View File

@ -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,

View File

@ -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)
{

View File

@ -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);

View File

@ -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);

View File

@ -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,

View File

@ -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");

View File

@ -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);

View File

@ -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,

View File

@ -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);

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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",
},

View File

@ -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,
},
};

View File

@ -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

View File

@ -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 */ }

View File

@ -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,

View File

@ -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);

View File

@ -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"

View File

@ -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