Pull ARM driver updates from Arnd Bergmann: "These are the usual updates for SoC specific device drivers and related subsystems that don't have their own top-level maintainers: - ARM SCMI/SCPI updates to allow pluggable transport layers - TEE subsystem cleanups - A new driver for the Amlogic secure power domain controller - Various driver updates for the NXP Layerscape DPAA2, NXP i.MX SCU and TI OMAP2+ sysc drivers. - Qualcomm SoC driver updates, including a new library module for "protection domain" notifications - Lots of smaller bugfixes and cleanups in other drivers" * tag 'arm-drivers-5.7' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (70 commits) soc: fsl: qe: fix sparse warnings for ucc_slow.c soc: fsl: qe: ucc_slow: remove 0 assignment for kzalloc'ed structure soc: fsl: qe: fix sparse warnings for ucc_fast.c soc: fsl: qe: fix sparse warnings for qe_ic.c soc: fsl: qe: fix sparse warnings for ucc.c soc: fsl: qe: fix sparse warning for qe_common.c soc: fsl: qe: fix sparse warnings for qe.c soc: qcom: Fix QCOM_APR dependencies soc: qcom: pdr: Avoid uninitialized use of found in pdr_indication_cb soc: imx: drop COMPILE_TEST for IMX_SCU_SOC firmware: imx: add COMPILE_TEST for IMX_SCU driver soc: imx: gpc: fix power up sequencing soc: imx: increase build coverage for imx8m soc driver soc: qcom: apr: Add avs/audio tracking functionality dt-bindings: soc: qcom: apr: Add protection domain bindings soc: qcom: Introduce Protection Domain Restart helpers devicetree: bindings: firmware: add ipq806x to qcom_scm memory: tegra: Correct debugfs clk rate-range on Tegra124 memory: tegra: Correct debugfs clk rate-range on Tegra30 memory: tegra: Correct debugfs clk rate-range on Tegra20 ...
Unified · Split
| @@ -38,6 +38,7 @@ Required standard properties: · 37 unmodified lines | ||
| 38 | 38 | "ti,sysc-dra7-mcasp" |
| 39 | 39 | "ti,sysc-usb-host-fs" |
| 40 | 40 | "ti,sysc-dra7-mcan" |
| 41 | + "ti,sysc-pruss" | |
| 41 | 42 | |
| 42 | 43 | - reg shall have register areas implemented for the interconnect |
| 43 | 44 | target module in question such as revision, sysc and syss |
| @@ -10,6 +10,7 @@ Required properties: · 9 unmodified lines | ||
| 10 | 10 | * "qcom,scm-apq8064" |
| 11 | 11 | * "qcom,scm-apq8084" |
| 12 | 12 | * "qcom,scm-ipq4019" |
| 13 | + * "qcom,scm-ipq806x" | |
| 13 | 14 | * "qcom,scm-msm8660" |
| 14 | 15 | * "qcom,scm-msm8916" |
| 15 | 16 | * "qcom,scm-msm8960" |
| @@ -0,0 +1,40 @@ | ||
| 1 | +# SPDX-License-Identifier: (GPL-2.0+ OR MIT) | |
| 2 | +# Copyright (c) 2019 Amlogic, Inc | |
| 3 | +# Author: Jianxin Pan <jianxin.pan@amlogic.com> | |
| 4 | +%YAML 1.2 | |
| 5 | +--- | |
| 6 | +$id: "http://devicetree.org/schemas/power/amlogic,meson-sec-pwrc.yaml#" | |
| 7 | +$schema: "http://devicetree.org/meta-schemas/core.yaml#" | |
| 8 | + | |
| 9 | +title: Amlogic Meson Secure Power Domains | |
| 10 | + | |
| 11 | +maintainers: | |
| 12 | + - Jianxin Pan <jianxin.pan@amlogic.com> | |
| 13 | + | |
| 14 | +description: |+ | |
| 15 | + Secure Power Domains used in Meson A1/C1 SoCs, and should be the child node | |
| 16 | + of secure-monitor. | |
| 17 | + | |
| 18 | +properties: | |
| 19 | + compatible: | |
| 20 | + enum: | |
| 21 | + - amlogic,meson-a1-pwrc | |
| 22 | + | |
| 23 | + "#power-domain-cells": | |
| 24 | + const: 1 | |
| 25 | + | |
| 26 | +required: | |
| 27 | + - compatible | |
| 28 | + - "#power-domain-cells" | |
| 29 | + | |
| 30 | +examples: | |
| 31 | + - | | |
| 32 | + secure-monitor { | |
| 33 | + compatible = "amlogic,meson-gxbb-sm"; | |
| 34 | + | |
| 35 | + pwrc: power-controller { | |
| 36 | + compatible = "amlogic,meson-a1-pwrc"; | |
| 37 | + #power-domain-cells = <1>; | |
| 38 | + }; | |
| 39 | + }; | |
| 40 | + | |
| @@ -45,6 +45,18 @@ by the individual bindings for the specific service · 44 unmodified lines | ||
| 45 | 45 | 12 - Ultrasound stream manager. |
| 46 | 46 | 13 - Listen stream manager. |
| 47 | 47 | |
| 48 | +- qcom,protection-domain | |
| 49 | + Usage: optional | |
| 50 | + Value type: <stringlist> | |
| 51 | + Definition: Must list the protection domain service name and path | |
| 52 | + that the particular apr service has a dependency on. | |
| 53 | + Possible values are : | |
| 54 | + "avs/audio", "msm/adsp/audio_pd". | |
| 55 | + "kernel/elf_loader", "msm/modem/wlan_pd". | |
| 56 | + "tms/servreg", "msm/adsp/audio_pd". | |
| 57 | + "tms/servreg", "msm/modem/wlan_pd". | |
| 58 | + "tms/servreg", "msm/slpi/sensor_pd". | |
| 59 | + | |
| 48 | 60 | = EXAMPLE |
| 49 | 61 | The following example represents a QDSP based sound card on a MSM8996 device |
| 50 | 62 | which uses apr as communication between Apps and QDSP. |
| @@ -82,3 +94,41 @@ which uses apr as communication between Apps and QDSP. · 31 unmodified lines | ||
| 82 | 94 | ... |
| 83 | 95 | }; |
| 84 | 96 | }; |
| 97 | + | |
| 98 | += EXAMPLE 2 | |
| 99 | +The following example represents a QDSP based sound card with protection domain | |
| 100 | +dependencies specified. Here some of the apr services are dependent on services | |
| 101 | +running on protection domain hosted on ADSP/SLPI remote processors while others | |
| 102 | +have no such dependency. | |
| 103 | + | |
| 104 | + apr { | |
| 105 | + compatible = "qcom,apr-v2"; | |
| 106 | + qcom,glink-channels = "apr_audio_svc"; | |
| 107 | + qcom,apr-domain = <APR_DOMAIN_ADSP>; | |
| 108 | + | |
| 109 | + q6core { | |
| 110 | + compatible = "qcom,q6core"; | |
| 111 | + reg = <APR_SVC_ADSP_CORE>; | |
| 112 | + }; | |
| 113 | + | |
| 114 | + q6afe: q6afe { | |
| 115 | + compatible = "qcom,q6afe"; | |
| 116 | + reg = <APR_SVC_AFE>; | |
| 117 | + qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd"; | |
| 118 | + ... | |
| 119 | + }; | |
| 120 | + | |
| 121 | + q6asm: q6asm { | |
| 122 | + compatible = "qcom,q6asm"; | |
| 123 | + reg = <APR_SVC_ASM>; | |
| 124 | + qcom,protection-domain = "tms/servreg", "msm/slpi/sensor_pd"; | |
| 125 | + ... | |
| 126 | + }; | |
| 127 | + | |
| 128 | + q6adm: q6adm { | |
| 129 | + compatible = "qcom,q6adm"; | |
| 130 | + reg = <APR_SVC_ADM>; | |
| 131 | + qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd"; | |
| 132 | + ... | |
| 133 | + }; | |
| 134 | + }; | |
| @@ -397,10 +397,16 @@ static int ti_sysc_shutdown_module(struct device *dev, · 396 unmodified lines | ||
| 397 | 397 | return omap_hwmod_shutdown(cookie->data); |
| 398 | 398 | } |
| 399 | 399 | |
| 400 | +static bool ti_sysc_soc_type_gp(void) | |
| 401 | +{ | |
| 402 | + return omap_type() == OMAP2_DEVICE_TYPE_GP; | |
| 403 | +} | |
| 404 | + | |
| 400 | 405 | static struct of_dev_auxdata omap_auxdata_lookup[]; |
| 401 | 406 | |
| 402 | 407 | static struct ti_sysc_platform_data ti_sysc_pdata = { |
| 403 | 408 | .auxdata = omap_auxdata_lookup, |
| 409 | + .soc_type_gp = ti_sysc_soc_type_gp, | |
| 404 | 410 | .init_clockdomain = ti_sysc_clkdm_init, |
| 405 | 411 | .clkdm_deny_idle = ti_sysc_clkdm_deny_idle, |
| 406 | 412 | .clkdm_allow_idle = ti_sysc_clkdm_allow_idle, |
| @@ -357,6 +357,26 @@ static int hisi_lpc_acpi_xlat_io_res(struct acpi_device *adev, · 356 unmodified lines | ||
| 357 | 357 | return 0; |
| 358 | 358 | } |
| 359 | 359 | |
| 360 | +/* | |
| 361 | + * Released firmware describes the IO port max address as 0x3fff, which is | |
| 362 | + * the max host bus address. Fixup to a proper range. This will probably | |
| 363 | + * never be fixed in firmware. | |
| 364 | + */ | |
| 365 | +static void hisi_lpc_acpi_fixup_child_resource(struct device *hostdev, | |
| 366 | + struct resource *r) | |
| 367 | +{ | |
| 368 | + if (r->end != 0x3fff) | |
| 369 | + return; | |
| 370 | + | |
| 371 | + if (r->start == 0xe4) | |
| 372 | + r->end = 0xe4 + 0x04 - 1; | |
| 373 | + else if (r->start == 0x2f8) | |
| 374 | + r->end = 0x2f8 + 0x08 - 1; | |
| 375 | + else | |
| 376 | + dev_warn(hostdev, "unrecognised resource %pR to fixup, ignoring\n", | |
| 377 | + r); | |
| 378 | +} | |
| 379 | + | |
| 360 | 380 | /* |
| 361 | 381 | * hisi_lpc_acpi_set_io_res - set the resources for a child |
| 362 | 382 | * @child: the device node to be updated the I/O resource |
| @@ -418,8 +438,11 @@ static int hisi_lpc_acpi_set_io_res(struct device *child, · 55 unmodified lines | ||
| 418 | 438 | return -ENOMEM; |
| 419 | 439 | } |
| 420 | 440 | count = 0; |
| 421 | − list_for_each_entry(rentry, &resource_list, node) | |
| 422 | − resources[count++] = *rentry->res; | |
| 441 | + list_for_each_entry(rentry, &resource_list, node) { | |
| 442 | + resources[count] = *rentry->res; | |
| 443 | + hisi_lpc_acpi_fixup_child_resource(hostdev, &resources[count]); | |
| 444 | + count++; | |
| 445 | + } | |
| 423 | 446 | |
| 424 | 447 | acpi_dev_free_resource_list(&resource_list); |
| 425 | 448 | |
| @@ -7,6 +7,7 @@ · 6 unmodified lines | ||
| 7 | 7 | #include <linux/clk.h> |
| 8 | 8 | #include <linux/clkdev.h> |
| 9 | 9 | #include <linux/delay.h> |
| 10 | +#include <linux/list.h> | |
| 10 | 11 | #include <linux/module.h> |
| 11 | 12 | #include <linux/platform_device.h> |
| 12 | 13 | #include <linux/pm_domain.h> |
| @@ -15,15 +16,47 @@ · 2 unmodified lines | ||
| 15 | 16 | #include <linux/of_address.h> |
| 16 | 17 | #include <linux/of_platform.h> |
| 17 | 18 | #include <linux/slab.h> |
| 19 | +#include <linux/sys_soc.h> | |
| 18 | 20 | #include <linux/iopoll.h> |
| 19 | 21 | |
| 20 | 22 | #include <linux/platform_data/ti-sysc.h> |
| 21 | 23 | |
| 22 | 24 | #include <dt-bindings/bus/ti-sysc.h> |
| 23 | 25 | |
| 26 | +#define DIS_ISP BIT(2) | |
| 27 | +#define DIS_IVA BIT(1) | |
| 28 | +#define DIS_SGX BIT(0) | |
| 29 | + | |
| 30 | +#define SOC_FLAG(match, flag) { .machine = match, .data = (void *)(flag), } | |
| 31 | + | |
| 24 | 32 | #define MAX_MODULE_SOFTRESET_WAIT 10000 |
| 25 | 33 | |
| 26 | −static const char * const reg_names[] = { "rev", "sysc", "syss", }; | |
| 34 | +enum sysc_soc { | |
| 35 | + SOC_UNKNOWN, | |
| 36 | + SOC_2420, | |
| 37 | + SOC_2430, | |
| 38 | + SOC_3430, | |
| 39 | + SOC_3630, | |
| 40 | + SOC_4430, | |
| 41 | + SOC_4460, | |
| 42 | + SOC_4470, | |
| 43 | + SOC_5430, | |
| 44 | + SOC_AM3, | |
| 45 | + SOC_AM4, | |
| 46 | + SOC_DRA7, | |
| 47 | +}; | |
| 48 | + | |
| 49 | +struct sysc_address { | |
| 50 | + unsigned long base; | |
| 51 | + struct list_head node; | |
| 52 | +}; | |
| 53 | + | |
| 54 | +struct sysc_soc_info { | |
| 55 | + unsigned long general_purpose:1; | |
| 56 | + enum sysc_soc soc; | |
| 57 | + struct mutex list_lock; /* disabled modules list lock */ | |
| 58 | + struct list_head disabled_modules; | |
| 59 | +}; | |
| 27 | 60 | |
| 28 | 61 | enum sysc_clocks { |
| 29 | 62 | SYSC_FCK, |
| @@ -39,6 +72,8 @@ enum sysc_clocks { · 9 unmodified lines | ||
| 39 | 72 | SYSC_MAX_CLOCKS, |
| 40 | 73 | }; |
| 41 | 74 | |
| 75 | +static struct sysc_soc_info *sysc_soc; | |
| 76 | +static const char * const reg_names[] = { "rev", "sysc", "syss", }; | |
| 42 | 77 | static const char * const clock_names[SYSC_MAX_CLOCKS] = { |
| 43 | 78 | "fck", "ick", "opt0", "opt1", "opt2", "opt3", "opt4", |
| 44 | 79 | "opt5", "opt6", "opt7", |
| @@ -70,11 +105,13 @@ static const char * const clock_names[SYSC_MAX_CLOCKS] = { · 25 unmodified lines | ||
| 70 | 105 | * @child_needs_resume: runtime resume needed for child on resume from suspend |
| 71 | 106 | * @disable_on_idle: status flag used for disabling modules with resets |
| 72 | 107 | * @idle_work: work structure used to perform delayed idle on a module |
| 73 | − * @clk_enable_quirk: module specific clock enable quirk | |
| 74 | − * @clk_disable_quirk: module specific clock disable quirk | |
| 108 | + * @pre_reset_quirk: module specific pre-reset quirk | |
| 109 | + * @post_reset_quirk: module specific post-reset quirk | |
| 75 | 110 | * @reset_done_quirk: module specific reset done quirk |
| 76 | 111 | * @module_enable_quirk: module specific enable quirk |
| 77 | 112 | * @module_disable_quirk: module specific disable quirk |
| 113 | + * @module_unlock_quirk: module specific sysconfig unlock quirk | |
| 114 | + * @module_lock_quirk: module specific sysconfig lock quirk | |
| 78 | 115 | */ |
| 79 | 116 | struct sysc { |
| 80 | 117 | struct device *dev; |
| @@ -97,11 +134,13 @@ struct sysc { · 16 unmodified lines | ||
| 97 | 134 | unsigned int needs_resume:1; |
| 98 | 135 | unsigned int child_needs_resume:1; |
| 99 | 136 | struct delayed_work idle_work; |
| 100 | − void (*clk_enable_quirk)(struct sysc *sysc); | |
| 101 | − void (*clk_disable_quirk)(struct sysc *sysc); | |
| 137 | + void (*pre_reset_quirk)(struct sysc *sysc); | |
| 138 | + void (*post_reset_quirk)(struct sysc *sysc); | |
| 102 | 139 | void (*reset_done_quirk)(struct sysc *sysc); |
| 103 | 140 | void (*module_enable_quirk)(struct sysc *sysc); |
| 104 | 141 | void (*module_disable_quirk)(struct sysc *sysc); |
| 142 | + void (*module_unlock_quirk)(struct sysc *sysc); | |
| 143 | + void (*module_lock_quirk)(struct sysc *sysc); | |
| 105 | 144 | }; |
| 106 | 145 | |
| 107 | 146 | static void sysc_parse_dts_quirks(struct sysc *ddata, struct device_node *np, |
| @@ -624,7 +663,7 @@ static void sysc_check_one_child(struct sysc *ddata, · 516 unmodified lines | ||
| 624 | 663 | const char *name; |
| 625 | 664 | |
| 626 | 665 | name = of_get_property(np, "ti,hwmods", NULL); |
| 627 | − if (name) | |
| 666 | + if (name && !of_device_is_compatible(np, "ti,sysc")) | |
| 628 | 667 | dev_warn(ddata->dev, "really a child ti,hwmods property?"); |
| 629 | 668 | |
| 630 | 669 | sysc_check_quirk_stdout(ddata, np); |
| @@ -861,6 +900,22 @@ static void sysc_show_registers(struct sysc *ddata) · 230 unmodified lines | ||
| 861 | 900 | buf); |
| 862 | 901 | } |
| 863 | 902 | |
| 903 | +/** | |
| 904 | + * sysc_write_sysconfig - handle sysconfig quirks for register write | |
| 905 | + * @ddata: device driver data | |
| 906 | + * @value: register value | |
| 907 | + */ | |
| 908 | +static void sysc_write_sysconfig(struct sysc *ddata, u32 value) | |
| 909 | +{ | |
| 910 | + if (ddata->module_unlock_quirk) | |
| 911 | + ddata->module_unlock_quirk(ddata); | |
| 912 | + | |
| 913 | + sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], value); | |
| 914 | + | |
| 915 | + if (ddata->module_lock_quirk) | |
| 916 | + ddata->module_lock_quirk(ddata); | |
| 917 | +} | |
| 918 | + | |
| 864 | 919 | #define SYSC_IDLE_MASK (SYSC_NR_IDLEMODES - 1) |
| 865 | 920 | #define SYSC_CLOCACT_ICK 2 |
| 866 | 921 | |
| @@ -907,7 +962,7 @@ static int sysc_enable_module(struct device *dev) · 40 unmodified lines | ||
| 907 | 962 | |
| 908 | 963 | reg &= ~(SYSC_IDLE_MASK << regbits->sidle_shift); |
| 909 | 964 | reg |= best_mode << regbits->sidle_shift; |
| 910 | − sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg); | |
| 965 | + sysc_write_sysconfig(ddata, reg); | |
| 911 | 966 | |
| 912 | 967 | set_midle: |
| 913 | 968 | /* Set MIDLE mode */ |
| @@ -926,14 +981,14 @@ set_midle: · 12 unmodified lines | ||
| 926 | 981 | |
| 927 | 982 | reg &= ~(SYSC_IDLE_MASK << regbits->midle_shift); |
| 928 | 983 | reg |= best_mode << regbits->midle_shift; |
| 929 | − sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg); | |
| 984 | + sysc_write_sysconfig(ddata, reg); | |
| 930 | 985 | |
| 931 | 986 | set_autoidle: |
| 932 | 987 | /* Autoidle bit must enabled separately if available */ |
| 933 | 988 | if (regbits->autoidle_shift >= 0 && |
| 934 | 989 | ddata->cfg.sysc_val & BIT(regbits->autoidle_shift)) { |
| 935 | 990 | reg |= 1 << regbits->autoidle_shift; |
| 936 | − sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg); | |
| 991 | + sysc_write_sysconfig(ddata, reg); | |
| 937 | 992 | } |
| 938 | 993 | |
| 939 | 994 | if (ddata->module_enable_quirk) |
| @@ -991,7 +1046,7 @@ static int sysc_disable_module(struct device *dev) · 51 unmodified lines | ||
| 991 | 1046 | |
| 992 | 1047 | reg &= ~(SYSC_IDLE_MASK << regbits->midle_shift); |
| 993 | 1048 | reg |= best_mode << regbits->midle_shift; |
| 994 | − sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg); | |
| 1049 | + sysc_write_sysconfig(ddata, reg); | |
| 995 | 1050 | |
| 996 | 1051 | set_sidle: |
| 997 | 1052 | /* Set SIDLE mode */ |
| @@ -1014,7 +1069,7 @@ set_sidle: · 16 unmodified lines | ||
| 1014 | 1069 | if (regbits->autoidle_shift >= 0 && |
| 1015 | 1070 | ddata->cfg.sysc_val & BIT(regbits->autoidle_shift)) |
| 1016 | 1071 | reg |= 1 << regbits->autoidle_shift; |
| 1017 | − sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg); | |
| 1072 | + sysc_write_sysconfig(ddata, reg); | |
| 1018 | 1073 | |
| 1019 | 1074 | return 0; |
| 1020 | 1075 | } |
| @@ -1216,16 +1271,16 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = { · 195 unmodified lines | ||
| 1216 | 1271 | SYSC_QUIRK_LEGACY_IDLE | SYSC_QUIRK_OPT_CLKS_IN_RESET), |
| 1217 | 1272 | SYSC_QUIRK("sham", 0, 0x100, 0x110, 0x114, 0x40000c03, 0xffffffff, |
| 1218 | 1273 | SYSC_QUIRK_LEGACY_IDLE), |
| 1219 | − SYSC_QUIRK("smartreflex", 0, -1, 0x24, -1, 0x00000000, 0xffffffff, | |
| 1274 | + SYSC_QUIRK("smartreflex", 0, -ENODEV, 0x24, -ENODEV, 0x00000000, 0xffffffff, | |
| 1220 | 1275 | SYSC_QUIRK_LEGACY_IDLE), |
| 1221 | − SYSC_QUIRK("smartreflex", 0, -1, 0x38, -1, 0x00000000, 0xffffffff, | |
| 1276 | + SYSC_QUIRK("smartreflex", 0, -ENODEV, 0x38, -ENODEV, 0x00000000, 0xffffffff, | |
| 1222 | 1277 | SYSC_QUIRK_LEGACY_IDLE), |
| 1223 | 1278 | SYSC_QUIRK("timer", 0, 0, 0x10, 0x14, 0x00000015, 0xffffffff, |
| 1224 | 1279 | 0), |
| 1225 | 1280 | /* Some timers on omap4 and later */ |
| 1226 | − SYSC_QUIRK("timer", 0, 0, 0x10, -1, 0x50002100, 0xffffffff, | |
| 1281 | + SYSC_QUIRK("timer", 0, 0, 0x10, -ENODEV, 0x50002100, 0xffffffff, | |
| 1227 | 1282 | 0), |
| 1228 | − SYSC_QUIRK("timer", 0, 0, 0x10, -1, 0x4fff1301, 0xffff00ff, | |
| 1283 | + SYSC_QUIRK("timer", 0, 0, 0x10, -ENODEV, 0x4fff1301, 0xffff00ff, | |
| 1229 | 1284 | 0), |
| 1230 | 1285 | SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000046, 0xffffffff, |
| 1231 | 1286 | SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_LEGACY_IDLE), |
| @@ -1238,19 +1293,27 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = { · 6 unmodified lines | ||
| 1238 | 1293 | SYSC_QUIRK_SWSUP_SIDLE_ACT | SYSC_QUIRK_LEGACY_IDLE), |
| 1239 | 1294 | |
| 1240 | 1295 | /* Quirks that need to be set based on the module address */ |
| 1241 | − SYSC_QUIRK("mcpdm", 0x40132000, 0, 0x10, -1, 0x50000800, 0xffffffff, | |
| 1296 | + SYSC_QUIRK("mcpdm", 0x40132000, 0, 0x10, -ENODEV, 0x50000800, 0xffffffff, | |
| 1242 | 1297 | SYSC_QUIRK_EXT_OPT_CLOCK | SYSC_QUIRK_NO_RESET_ON_INIT | |
| 1243 | 1298 | SYSC_QUIRK_SWSUP_SIDLE), |
| 1244 | 1299 | |
| 1245 | 1300 | /* Quirks that need to be set based on detected module */ |
| 1246 | − SYSC_QUIRK("aess", 0, 0, 0x10, -1, 0x40000000, 0xffffffff, | |
| 1301 | + SYSC_QUIRK("aess", 0, 0, 0x10, -ENODEV, 0x40000000, 0xffffffff, | |
| 1247 | 1302 | SYSC_MODULE_QUIRK_AESS), |
| 1248 | − SYSC_QUIRK("dcan", 0x48480000, 0x20, -1, -1, 0xa3170504, 0xffffffff, | |
| 1303 | + SYSC_QUIRK("dcan", 0x48480000, 0x20, -ENODEV, -ENODEV, 0xa3170504, 0xffffffff, | |
| 1249 | 1304 | SYSC_QUIRK_CLKDM_NOAUTO), |
| 1250 | − SYSC_QUIRK("dwc3", 0x48880000, 0, 0x10, -1, 0x500a0200, 0xffffffff, | |
| 1305 | + SYSC_QUIRK("dss", 0x4832a000, 0, 0x10, 0x14, 0x00000020, 0xffffffff, | |
| 1306 | + SYSC_QUIRK_OPT_CLKS_IN_RESET | SYSC_MODULE_QUIRK_DSS_RESET), | |
| 1307 | + SYSC_QUIRK("dss", 0x58000000, 0, -ENODEV, 0x14, 0x00000040, 0xffffffff, | |
| 1308 | + SYSC_QUIRK_OPT_CLKS_IN_RESET | SYSC_MODULE_QUIRK_DSS_RESET), | |
| 1309 | + SYSC_QUIRK("dss", 0x58000000, 0, -ENODEV, 0x14, 0x00000061, 0xffffffff, | |
| 1310 | + SYSC_QUIRK_OPT_CLKS_IN_RESET | SYSC_MODULE_QUIRK_DSS_RESET), | |
| 1311 | + SYSC_QUIRK("dwc3", 0x48880000, 0, 0x10, -ENODEV, 0x500a0200, 0xffffffff, | |
| 1251 | 1312 | SYSC_QUIRK_CLKDM_NOAUTO), |
| 1252 | − SYSC_QUIRK("dwc3", 0x488c0000, 0, 0x10, -1, 0x500a0200, 0xffffffff, | |
| 1313 | + SYSC_QUIRK("dwc3", 0x488c0000, 0, 0x10, -ENODEV, 0x500a0200, 0xffffffff, | |
| 1253 | 1314 | SYSC_QUIRK_CLKDM_NOAUTO), |
| 1315 | + SYSC_QUIRK("hdmi", 0, 0, 0x10, -ENODEV, 0x50030200, 0xffffffff, | |
| 1316 | + SYSC_QUIRK_OPT_CLKS_NEEDED), | |
| 1254 | 1317 | SYSC_QUIRK("hdq1w", 0, 0, 0x14, 0x18, 0x00000006, 0xffffffff, |
| 1255 | 1318 | SYSC_MODULE_QUIRK_HDQ1W), |
| 1256 | 1319 | SYSC_QUIRK("hdq1w", 0, 0, 0x14, 0x18, 0x0000000a, 0xffffffff, |
| @@ -1263,72 +1326,92 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = { · 6 unmodified lines | ||
| 1263 | 1326 | SYSC_MODULE_QUIRK_I2C), |
| 1264 | 1327 | SYSC_QUIRK("i2c", 0, 0, 0x10, 0x90, 0x5040000a, 0xfffff0f0, |
| 1265 | 1328 | SYSC_MODULE_QUIRK_I2C), |
| 1266 | − SYSC_QUIRK("gpu", 0x50000000, 0x14, -1, -1, 0x00010201, 0xffffffff, 0), | |
| 1267 | − SYSC_QUIRK("gpu", 0x50000000, 0xfe00, 0xfe10, -1, 0x40000000 , 0xffffffff, | |
| 1329 | + SYSC_QUIRK("gpu", 0x50000000, 0x14, -ENODEV, -ENODEV, 0x00010201, 0xffffffff, 0), | |
| 1330 | + SYSC_QUIRK("gpu", 0x50000000, 0xfe00, 0xfe10, -ENODEV, 0x40000000 , 0xffffffff, | |
| 1268 | 1331 | SYSC_MODULE_QUIRK_SGX), |
| 1269 | − SYSC_QUIRK("lcdc", 0, 0, 0x54, -1, 0x4f201000, 0xffffffff, | |
| 1332 | + SYSC_QUIRK("lcdc", 0, 0, 0x54, -ENODEV, 0x4f201000, 0xffffffff, | |
| 1333 | + SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY), | |
| 1334 | + SYSC_QUIRK("rtc", 0, 0x74, 0x78, -ENODEV, 0x4eb01908, 0xffff00f0, | |
| 1335 | + SYSC_MODULE_QUIRK_RTC_UNLOCK), | |
| 1336 | + SYSC_QUIRK("tptc", 0, 0, 0x10, -ENODEV, 0x40006c00, 0xffffefff, | |
| 1337 | + SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY), | |
| 1338 | + SYSC_QUIRK("tptc", 0, 0, -ENODEV, -ENODEV, 0x40007c00, 0xffffffff, | |
| 1270 | 1339 | SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY), |
| 1271 | 1340 | SYSC_QUIRK("usb_otg_hs", 0, 0x400, 0x404, 0x408, 0x00000050, |
| 1272 | 1341 | 0xffffffff, SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY), |
| 1273 | − SYSC_QUIRK("usb_otg_hs", 0, 0, 0x10, -1, 0x4ea2080d, 0xffffffff, | |
| 1342 | + SYSC_QUIRK("usb_otg_hs", 0, 0, 0x10, -ENODEV, 0x4ea2080d, 0xffffffff, | |
| 1274 | 1343 | SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY), |
| 1275 | 1344 | SYSC_QUIRK("wdt", 0, 0, 0x10, 0x14, 0x502a0500, 0xfffff0f0, |
| 1276 | 1345 | SYSC_MODULE_QUIRK_WDT), |
| 1346 | + /* PRUSS on am3, am4 and am5 */ | |
| 1347 | + SYSC_QUIRK("pruss", 0, 0x26000, 0x26004, -ENODEV, 0x47000000, 0xff000000, | |
| 1348 | + SYSC_MODULE_QUIRK_PRUSS), | |
| 1277 | 1349 | /* Watchdog on am3 and am4 */ |
| 1278 | 1350 | SYSC_QUIRK("wdt", 0x44e35000, 0, 0x10, 0x14, 0x502a0500, 0xfffff0f0, |
| 1279 | 1351 | SYSC_MODULE_QUIRK_WDT | SYSC_QUIRK_SWSUP_SIDLE), |
| 1280 | 1352 | |
| 1281 | 1353 | #ifdef DEBUG |
| 1282 | − SYSC_QUIRK("adc", 0, 0, 0x10, -1, 0x47300001, 0xffffffff, 0), | |
| 1283 | − SYSC_QUIRK("atl", 0, 0, -1, -1, 0x0a070100, 0xffffffff, 0), | |
| 1284 | − SYSC_QUIRK("cm", 0, 0, -1, -1, 0x40000301, 0xffffffff, 0), | |
| 1285 | − SYSC_QUIRK("control", 0, 0, 0x10, -1, 0x40000900, 0xffffffff, 0), | |
| 1354 | + SYSC_QUIRK("adc", 0, 0, 0x10, -ENODEV, 0x47300001, 0xffffffff, 0), | |
| 1355 | + SYSC_QUIRK("atl", 0, 0, -ENODEV, -ENODEV, 0x0a070100, 0xffffffff, 0), | |
| 1356 | + SYSC_QUIRK("cm", 0, 0, -ENODEV, -ENODEV, 0x40000301, 0xffffffff, 0), | |
| 1357 | + SYSC_QUIRK("control", 0, 0, 0x10, -ENODEV, 0x40000900, 0xffffffff, 0), | |
| 1286 | 1358 | SYSC_QUIRK("cpgmac", 0, 0x1200, 0x1208, 0x1204, 0x4edb1902, |
| 1287 | 1359 | 0xffff00f0, 0), |
| 1288 | − SYSC_QUIRK("dcan", 0, 0x20, -1, -1, 0xa3170504, 0xffffffff, 0), | |
| 1289 | − SYSC_QUIRK("dcan", 0, 0x20, -1, -1, 0x4edb1902, 0xffffffff, 0), | |
| 1290 | − SYSC_QUIRK("dmic", 0, 0, 0x10, -1, 0x50010000, 0xffffffff, 0), | |
| 1291 | − SYSC_QUIRK("dwc3", 0, 0, 0x10, -1, 0x500a0200, 0xffffffff, 0), | |
| 1360 | + SYSC_QUIRK("dcan", 0, 0x20, -ENODEV, -ENODEV, 0xa3170504, 0xffffffff, 0), | |
| 1361 | + SYSC_QUIRK("dcan", 0, 0x20, -ENODEV, -ENODEV, 0x4edb1902, 0xffffffff, 0), | |
| 1362 | + SYSC_QUIRK("dispc", 0x4832a400, 0, 0x10, 0x14, 0x00000030, 0xffffffff, 0), | |
| 1363 | + SYSC_QUIRK("dispc", 0x58001000, 0, 0x10, 0x14, 0x00000040, 0xffffffff, 0), | |
| 1364 | + SYSC_QUIRK("dispc", 0x58001000, 0, 0x10, 0x14, 0x00000051, 0xffffffff, 0), | |
| 1365 | + SYSC_QUIRK("dmic", 0, 0, 0x10, -ENODEV, 0x50010000, 0xffffffff, 0), | |
| 1366 | + SYSC_QUIRK("dsi", 0x58004000, 0, 0x10, 0x14, 0x00000030, 0xffffffff, 0), | |
| 1367 | + SYSC_QUIRK("dsi", 0x58005000, 0, 0x10, 0x14, 0x00000030, 0xffffffff, 0), | |
| 1368 | + SYSC_QUIRK("dsi", 0x58005000, 0, 0x10, 0x14, 0x00000040, 0xffffffff, 0), | |
| 1369 | + SYSC_QUIRK("dsi", 0x58009000, 0, 0x10, 0x14, 0x00000040, 0xffffffff, 0), | |
| 1370 | + SYSC_QUIRK("dwc3", 0, 0, 0x10, -ENODEV, 0x500a0200, 0xffffffff, 0), | |
| 1292 | 1371 | SYSC_QUIRK("d2d", 0x4a0b6000, 0, 0x10, 0x14, 0x00000010, 0xffffffff, 0), |
| 1293 | 1372 | SYSC_QUIRK("d2d", 0x4a0cd000, 0, 0x10, 0x14, 0x00000010, 0xffffffff, 0), |
| 1294 | − SYSC_QUIRK("epwmss", 0, 0, 0x4, -1, 0x47400001, 0xffffffff, 0), | |
| 1295 | − SYSC_QUIRK("gpu", 0, 0x1fc00, 0x1fc10, -1, 0, 0, 0), | |
| 1296 | − SYSC_QUIRK("gpu", 0, 0xfe00, 0xfe10, -1, 0x40000000 , 0xffffffff, 0), | |
| 1373 | + SYSC_QUIRK("epwmss", 0, 0, 0x4, -ENODEV, 0x47400001, 0xffffffff, 0), | |
| 1374 | + SYSC_QUIRK("gpu", 0, 0x1fc00, 0x1fc10, -ENODEV, 0, 0, 0), | |
| 1375 | + SYSC_QUIRK("gpu", 0, 0xfe00, 0xfe10, -ENODEV, 0x40000000 , 0xffffffff, 0), | |
| 1376 | + SYSC_QUIRK("hdmi", 0, 0, 0x10, -ENODEV, 0x50031d00, 0xffffffff, 0), | |
| 1297 | 1377 | SYSC_QUIRK("hsi", 0, 0, 0x10, 0x14, 0x50043101, 0xffffffff, 0), |
| 1298 | − SYSC_QUIRK("iss", 0, 0, 0x10, -1, 0x40000101, 0xffffffff, 0), | |
| 1299 | − SYSC_QUIRK("mcasp", 0, 0, 0x4, -1, 0x44306302, 0xffffffff, 0), | |
| 1300 | − SYSC_QUIRK("mcasp", 0, 0, 0x4, -1, 0x44307b02, 0xffffffff, 0), | |
| 1301 | − SYSC_QUIRK("mcbsp", 0, -1, 0x8c, -1, 0, 0, 0), | |
| 1302 | − SYSC_QUIRK("mcspi", 0, 0, 0x10, -1, 0x40300a0b, 0xffff00ff, 0), | |
| 1378 | + SYSC_QUIRK("iss", 0, 0, 0x10, -ENODEV, 0x40000101, 0xffffffff, 0), | |
| 1379 | + SYSC_QUIRK("mcasp", 0, 0, 0x4, -ENODEV, 0x44306302, 0xffffffff, 0), | |
| 1380 | + SYSC_QUIRK("mcasp", 0, 0, 0x4, -ENODEV, 0x44307b02, 0xffffffff, 0), | |
| 1381 | + SYSC_QUIRK("mcbsp", 0, -ENODEV, 0x8c, -ENODEV, 0, 0, 0), | |
| 1382 | + SYSC_QUIRK("mcspi", 0, 0, 0x10, -ENODEV, 0x40300a0b, 0xffff00ff, 0), | |
| 1303 | 1383 | SYSC_QUIRK("mcspi", 0, 0, 0x110, 0x114, 0x40300a0b, 0xffffffff, 0), |
| 1304 | − SYSC_QUIRK("mailbox", 0, 0, 0x10, -1, 0x00000400, 0xffffffff, 0), | |
| 1305 | − SYSC_QUIRK("m3", 0, 0, -1, -1, 0x5f580105, 0x0fff0f00, 0), | |
| 1384 | + SYSC_QUIRK("mailbox", 0, 0, 0x10, -ENODEV, 0x00000400, 0xffffffff, 0), | |
| 1385 | + SYSC_QUIRK("m3", 0, 0, -ENODEV, -ENODEV, 0x5f580105, 0x0fff0f00, 0), | |
| 1306 | 1386 | SYSC_QUIRK("ocp2scp", 0, 0, 0x10, 0x14, 0x50060005, 0xfffffff0, 0), |
| 1307 | − SYSC_QUIRK("ocp2scp", 0, 0, -1, -1, 0x50060007, 0xffffffff, 0), | |
| 1308 | − SYSC_QUIRK("padconf", 0, 0, 0x10, -1, 0x4fff0800, 0xffffffff, 0), | |
| 1309 | − SYSC_QUIRK("padconf", 0, 0, -1, -1, 0x40001100, 0xffffffff, 0), | |
| 1310 | − SYSC_QUIRK("prcm", 0, 0, -1, -1, 0x40000100, 0xffffffff, 0), | |
| 1311 | − SYSC_QUIRK("prcm", 0, 0, -1, -1, 0x00004102, 0xffffffff, 0), | |
| 1312 | − SYSC_QUIRK("prcm", 0, 0, -1, -1, 0x40000400, 0xffffffff, 0), | |
| 1313 | − SYSC_QUIRK("scm", 0, 0, 0x10, -1, 0x40000900, 0xffffffff, 0), | |
| 1314 | − SYSC_QUIRK("scm", 0, 0, -1, -1, 0x4e8b0100, 0xffffffff, 0), | |
| 1315 | − SYSC_QUIRK("scm", 0, 0, -1, -1, 0x4f000100, 0xffffffff, 0), | |
| 1316 | − SYSC_QUIRK("scm", 0, 0, -1, -1, 0x40000900, 0xffffffff, 0), | |
| 1317 | − SYSC_QUIRK("scrm", 0, 0, -1, -1, 0x00000010, 0xffffffff, 0), | |
| 1318 | − SYSC_QUIRK("sdio", 0, 0, 0x10, -1, 0x40202301, 0xffff0ff0, 0), | |
| 1387 | + SYSC_QUIRK("ocp2scp", 0, 0, -ENODEV, -ENODEV, 0x50060007, 0xffffffff, 0), | |
| 1388 | + SYSC_QUIRK("padconf", 0, 0, 0x10, -ENODEV, 0x4fff0800, 0xffffffff, 0), | |
| 1389 | + SYSC_QUIRK("padconf", 0, 0, -ENODEV, -ENODEV, 0x40001100, 0xffffffff, 0), | |
| 1390 | + SYSC_QUIRK("prcm", 0, 0, -ENODEV, -ENODEV, 0x40000100, 0xffffffff, 0), | |
| 1391 | + SYSC_QUIRK("prcm", 0, 0, -ENODEV, -ENODEV, 0x00004102, 0xffffffff, 0), | |
| 1392 | + SYSC_QUIRK("prcm", 0, 0, -ENODEV, -ENODEV, 0x40000400, 0xffffffff, 0), | |
| 1393 | + SYSC_QUIRK("rfbi", 0x4832a800, 0, 0x10, 0x14, 0x00000010, 0xffffffff, 0), | |
| 1394 | + SYSC_QUIRK("rfbi", 0x58002000, 0, 0x10, 0x14, 0x00000010, 0xffffffff, 0), | |
| 1395 | + SYSC_QUIRK("scm", 0, 0, 0x10, -ENODEV, 0x40000900, 0xffffffff, 0), | |
| 1396 | + SYSC_QUIRK("scm", 0, 0, -ENODEV, -ENODEV, 0x4e8b0100, 0xffffffff, 0), | |
| 1397 | + SYSC_QUIRK("scm", 0, 0, -ENODEV, -ENODEV, 0x4f000100, 0xffffffff, 0), | |
| 1398 | + SYSC_QUIRK("scm", 0, 0, -ENODEV, -ENODEV, 0x40000900, 0xffffffff, 0), | |
| 1399 | + SYSC_QUIRK("scrm", 0, 0, -ENODEV, -ENODEV, 0x00000010, 0xffffffff, 0), | |
| 1400 | + SYSC_QUIRK("sdio", 0, 0, 0x10, -ENODEV, 0x40202301, 0xffff0ff0, 0), | |
| 1319 | 1401 | SYSC_QUIRK("sdio", 0, 0x2fc, 0x110, 0x114, 0x31010000, 0xffffffff, 0), |
| 1320 | 1402 | SYSC_QUIRK("sdma", 0, 0, 0x2c, 0x28, 0x00010900, 0xffffffff, 0), |
| 1321 | − SYSC_QUIRK("slimbus", 0, 0, 0x10, -1, 0x40000902, 0xffffffff, 0), | |
| 1322 | − SYSC_QUIRK("slimbus", 0, 0, 0x10, -1, 0x40002903, 0xffffffff, 0), | |
| 1323 | − SYSC_QUIRK("spinlock", 0, 0, 0x10, -1, 0x50020000, 0xffffffff, 0), | |
| 1324 | − SYSC_QUIRK("rng", 0, 0x1fe0, 0x1fe4, -1, 0x00000020, 0xffffffff, 0), | |
| 1325 | − SYSC_QUIRK("rtc", 0, 0x74, 0x78, -1, 0x4eb01908, 0xffff00f0, 0), | |
| 1326 | − SYSC_QUIRK("timer32k", 0, 0, 0x4, -1, 0x00000060, 0xffffffff, 0), | |
| 1403 | + SYSC_QUIRK("slimbus", 0, 0, 0x10, -ENODEV, 0x40000902, 0xffffffff, 0), | |
| 1404 | + SYSC_QUIRK("slimbus", 0, 0, 0x10, -ENODEV, 0x40002903, 0xffffffff, 0), | |
| 1405 | + SYSC_QUIRK("spinlock", 0, 0, 0x10, -ENODEV, 0x50020000, 0xffffffff, 0), | |
| 1406 | + SYSC_QUIRK("rng", 0, 0x1fe0, 0x1fe4, -ENODEV, 0x00000020, 0xffffffff, 0), | |
| 1407 | + SYSC_QUIRK("timer32k", 0, 0, 0x4, -ENODEV, 0x00000060, 0xffffffff, 0), | |
| 1408 | + SYSC_QUIRK("tpcc", 0, 0, -ENODEV, -ENODEV, 0x40014c00, 0xffffffff, 0), | |
| 1327 | 1409 | SYSC_QUIRK("usbhstll", 0, 0, 0x10, 0x14, 0x00000004, 0xffffffff, 0), |
| 1328 | 1410 | SYSC_QUIRK("usbhstll", 0, 0, 0x10, 0x14, 0x00000008, 0xffffffff, 0), |
| 1329 | 1411 | SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, 0x14, 0x50700100, 0xffffffff, 0), |
| 1330 | − SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, -1, 0x50700101, 0xffffffff, 0), | |
| 1331 | − SYSC_QUIRK("vfpe", 0, 0, 0x104, -1, 0x4d001200, 0xffffffff, 0), | |
| 1412 | + SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, -ENODEV, 0x50700101, 0xffffffff, 0), | |
| 1413 | + SYSC_QUIRK("venc", 0x58003000, 0, -ENODEV, -ENODEV, 0x00000002, 0xffffffff, 0), | |
| 1414 | + SYSC_QUIRK("vfpe", 0, 0, 0x104, -ENODEV, 0x4d001200, 0xffffffff, 0), | |
| 1332 | 1415 | #endif |
| 1333 | 1416 | }; |
| 1334 | 1417 | |
| @@ -1350,16 +1433,13 @@ static void sysc_init_early_quirks(struct sysc *ddata) · 15 unmodified lines | ||
| 1350 | 1433 | if (q->base != ddata->module_pa) |
| 1351 | 1434 | continue; |
| 1352 | 1435 | |
| 1353 | − if (q->rev_offset >= 0 && | |
| 1354 | − q->rev_offset != ddata->offsets[SYSC_REVISION]) | |
| 1436 | + if (q->rev_offset != ddata->offsets[SYSC_REVISION]) | |
| 1355 | 1437 | continue; |
| 1356 | 1438 | |
| 1357 | − if (q->sysc_offset >= 0 && | |
| 1358 | − q->sysc_offset != ddata->offsets[SYSC_SYSCONFIG]) | |
| 1439 | + if (q->sysc_offset != ddata->offsets[SYSC_SYSCONFIG]) | |
| 1359 | 1440 | continue; |
| 1360 | 1441 | |
| 1361 | − if (q->syss_offset >= 0 && | |
| 1362 | − q->syss_offset != ddata->offsets[SYSC_SYSSTATUS]) | |
| 1442 | + if (q->syss_offset != ddata->offsets[SYSC_SYSSTATUS]) | |
| 1363 | 1443 | continue; |
| 1364 | 1444 | |
| 1365 | 1445 | ddata->name = q->name; |
| @@ -1379,16 +1459,13 @@ static void sysc_init_revision_quirks(struct sysc *ddata) · 13 unmodified lines | ||
| 1379 | 1459 | if (q->base && q->base != ddata->module_pa) |
| 1380 | 1460 | continue; |
| 1381 | 1461 | |
| 1382 | − if (q->rev_offset >= 0 && | |
| 1383 | − q->rev_offset != ddata->offsets[SYSC_REVISION]) | |
| 1462 | + if (q->rev_offset != ddata->offsets[SYSC_REVISION]) | |
| 1384 | 1463 | continue; |
| 1385 | 1464 | |
| 1386 | − if (q->sysc_offset >= 0 && | |
| 1387 | − q->sysc_offset != ddata->offsets[SYSC_SYSCONFIG]) | |
| 1465 | + if (q->sysc_offset != ddata->offsets[SYSC_SYSCONFIG]) | |
| 1388 | 1466 | continue; |
| 1389 | 1467 | |
| 1390 | − if (q->syss_offset >= 0 && | |
| 1391 | − q->syss_offset != ddata->offsets[SYSC_SYSSTATUS]) | |
| 1468 | + if (q->syss_offset != ddata->offsets[SYSC_SYSSTATUS]) | |
| 1392 | 1469 | continue; |
| 1393 | 1470 | |
| 1394 | 1471 | if (q->revision == ddata->revision || |
| @@ -1400,6 +1477,128 @@ static void sysc_init_revision_quirks(struct sysc *ddata) · 5 unmodified lines | ||
| 1400 | 1477 | } |
| 1401 | 1478 | } |
| 1402 | 1479 | |
| 1480 | +/* | |
| 1481 | + * DSS needs dispc outputs disabled to reset modules. Returns mask of | |
| 1482 | + * enabled DSS interrupts. Eventually we may be able to do this on | |
| 1483 | + * dispc init rather than top-level DSS init. | |
| 1484 | + */ | |
| 1485 | +static u32 sysc_quirk_dispc(struct sysc *ddata, int dispc_offset, | |
| 1486 | + bool disable) | |
| 1487 | +{ | |
| 1488 | + bool lcd_en, digit_en, lcd2_en = false, lcd3_en = false; | |
| 1489 | + const int lcd_en_mask = BIT(0), digit_en_mask = BIT(1); | |
| 1490 | + int manager_count; | |
| 1491 | + bool framedonetv_irq; | |
| 1492 | + u32 val, irq_mask = 0; | |
| 1493 | + | |
| 1494 | + switch (sysc_soc->soc) { | |
| 1495 | + case SOC_2420 ... SOC_3630: | |
| 1496 | + manager_count = 2; | |
| 1497 | + framedonetv_irq = false; | |
| 1498 | + break; | |
| 1499 | + case SOC_4430 ... SOC_4470: | |
| 1500 | + manager_count = 3; | |
| 1501 | + break; | |
| 1502 | + case SOC_5430: | |
| 1503 | + case SOC_DRA7: | |
| 1504 | + manager_count = 4; | |
| 1505 | + break; | |
| 1506 | + case SOC_AM4: | |
| 1507 | + manager_count = 1; | |
| 1508 | + break; | |
| 1509 | + case SOC_UNKNOWN: | |
| 1510 | + default: | |
| 1511 | + return 0; | |
| 1512 | + }; | |
| 1513 | + | |
| 1514 | + /* Remap the whole module range to be able to reset dispc outputs */ | |
| 1515 | + devm_iounmap(ddata->dev, ddata->module_va); | |
| 1516 | + ddata->module_va = devm_ioremap(ddata->dev, | |
| 1517 | + ddata->module_pa, | |
| 1518 | + ddata->module_size); | |
| 1519 | + if (!ddata->module_va) | |
| 1520 | + return -EIO; | |
| 1521 | + | |
| 1522 | + /* DISP_CONTROL */ | |
| 1523 | + val = sysc_read(ddata, dispc_offset + 0x40); | |
| 1524 | + lcd_en = val & lcd_en_mask; | |
| 1525 | + digit_en = val & digit_en_mask; | |
| 1526 | + if (lcd_en) | |
| 1527 | + irq_mask |= BIT(0); /* FRAMEDONE */ | |
| 1528 | + if (digit_en) { | |
| 1529 | + if (framedonetv_irq) | |
| 1530 | + irq_mask |= BIT(24); /* FRAMEDONETV */ | |
| 1531 | + else | |
| 1532 | + irq_mask |= BIT(2) | BIT(3); /* EVSYNC bits */ | |
| 1533 | + } | |
| 1534 | + if (disable & (lcd_en | digit_en)) | |
| 1535 | + sysc_write(ddata, dispc_offset + 0x40, | |
| 1536 | + val & ~(lcd_en_mask | digit_en_mask)); | |
| 1537 | + | |
| 1538 | + if (manager_count <= 2) | |
| 1539 | + return irq_mask; | |
| 1540 | + | |
| 1541 | + /* DISPC_CONTROL2 */ | |
| 1542 | + val = sysc_read(ddata, dispc_offset + 0x238); | |
| 1543 | + lcd2_en = val & lcd_en_mask; | |
| 1544 | + if (lcd2_en) | |
| 1545 | + irq_mask |= BIT(22); /* FRAMEDONE2 */ | |
| 1546 | + if (disable && lcd2_en) | |
| 1547 | + sysc_write(ddata, dispc_offset + 0x238, | |
| 1548 | + val & ~lcd_en_mask); | |
| 1549 | + | |
| 1550 | + if (manager_count <= 3) | |
| 1551 | + return irq_mask; | |
| 1552 | + | |
| 1553 | + /* DISPC_CONTROL3 */ | |
| 1554 | + val = sysc_read(ddata, dispc_offset + 0x848); | |
| 1555 | + lcd3_en = val & lcd_en_mask; | |
| 1556 | + if (lcd3_en) | |
| 1557 | + irq_mask |= BIT(30); /* FRAMEDONE3 */ | |
| 1558 | + if (disable && lcd3_en) | |
| 1559 | + sysc_write(ddata, dispc_offset + 0x848, | |
| 1560 | + val & ~lcd_en_mask); | |
| 1561 | + | |
| 1562 | + return irq_mask; | |
| 1563 | +} | |
| 1564 | + | |
| 1565 | +/* DSS needs child outputs disabled and SDI registers cleared for reset */ | |
| 1566 | +static void sysc_pre_reset_quirk_dss(struct sysc *ddata) | |
| 1567 | +{ | |
| 1568 | + const int dispc_offset = 0x1000; | |
| 1569 | + int error; | |
| 1570 | + u32 irq_mask, val; | |
| 1571 | + | |
| 1572 | + /* Get enabled outputs */ | |
| 1573 | + irq_mask = sysc_quirk_dispc(ddata, dispc_offset, false); | |
| 1574 | + if (!irq_mask) | |
| 1575 | + return; | |
| 1576 | + | |
| 1577 | + /* Clear IRQSTATUS */ | |
| 1578 | + sysc_write(ddata, dispc_offset + 0x18, irq_mask); | |
| 1579 | + | |
| 1580 | + /* Disable outputs */ | |
| 1581 | + val = sysc_quirk_dispc(ddata, dispc_offset, true); | |
| 1582 | + | |
| 1583 | + /* Poll IRQSTATUS */ | |
| 1584 | + error = readl_poll_timeout(ddata->module_va + dispc_offset + 0x18, | |
| 1585 | + val, val != irq_mask, 100, 50); | |
| 1586 | + if (error) | |
| 1587 | + dev_warn(ddata->dev, "%s: timed out %08x !+ %08x\n", | |
| 1588 | + __func__, val, irq_mask); | |
| 1589 | + | |
| 1590 | + if (sysc_soc->soc == SOC_3430) { | |
| 1591 | + /* Clear DSS_SDI_CONTROL */ | |
| 1592 | + sysc_write(ddata, 0x44, 0); | |
| 1593 | + | |
| 1594 | + /* Clear DSS_PLL_CONTROL */ | |
| 1595 | + sysc_write(ddata, 0x48, 0); | |
| 1596 | + } | |
| 1597 | + | |
| 1598 | + /* Clear DSS_CONTROL to switch DSS clock sources to PRCM if not */ | |
| 1599 | + sysc_write(ddata, 0x40, 0); | |
| 1600 | +} | |
| 1601 | + | |
| 1403 | 1602 | /* 1-wire needs module's internal clocks enabled for reset */ |
| 1404 | 1603 | static void sysc_pre_reset_quirk_hdq1w(struct sysc *ddata) |
| 1405 | 1604 | { |
| @@ -1419,7 +1618,7 @@ static void sysc_module_enable_quirk_aess(struct sysc *ddata) · 13 unmodified lines | ||
| 1419 | 1618 | sysc_write(ddata, offset, 1); |
| 1420 | 1619 | } |
| 1421 | 1620 | |
| 1422 | −/* I2C needs extra enable bit toggling for reset */ | |
| 1621 | +/* I2C needs to be disabled for reset */ | |
| 1423 | 1622 | static void sysc_clk_quirk_i2c(struct sysc *ddata, bool enable) |
| 1424 | 1623 | { |
| 1425 | 1624 | int offset; |
| @@ -1440,14 +1639,48 @@ static void sysc_clk_quirk_i2c(struct sysc *ddata, bool enable) · 14 unmodified lines | ||
| 1440 | 1639 | sysc_write(ddata, offset, val); |
| 1441 | 1640 | } |
| 1442 | 1641 | |
| 1443 | −static void sysc_clk_enable_quirk_i2c(struct sysc *ddata) | |
| 1642 | +static void sysc_pre_reset_quirk_i2c(struct sysc *ddata) | |
| 1643 | +{ | |
| 1644 | + sysc_clk_quirk_i2c(ddata, false); | |
| 1645 | +} | |
| 1646 | + | |
| 1647 | +static void sysc_post_reset_quirk_i2c(struct sysc *ddata) | |
| 1444 | 1648 | { |
| 1445 | 1649 | sysc_clk_quirk_i2c(ddata, true); |
| 1446 | 1650 | } |
| 1447 | 1651 | |
| 1448 | −static void sysc_clk_disable_quirk_i2c(struct sysc *ddata) | |
| 1652 | +/* RTC on am3 and 4 needs to be unlocked and locked for sysconfig */ | |
| 1653 | +static void sysc_quirk_rtc(struct sysc *ddata, bool lock) | |
| 1449 | 1654 | { |
| 1450 | − sysc_clk_quirk_i2c(ddata, false); | |
| 1655 | + u32 val, kick0_val = 0, kick1_val = 0; | |
| 1656 | + unsigned long flags; | |
| 1657 | + int error; | |
| 1658 | + | |
| 1659 | + if (!lock) { | |
| 1660 | + kick0_val = 0x83e70b13; | |
| 1661 | + kick1_val = 0x95a4f1e0; | |
| 1662 | + } | |
| 1663 | + | |
| 1664 | + local_irq_save(flags); | |
| 1665 | + /* RTC_STATUS BUSY bit may stay active for 1/32768 seconds (~30 usec) */ | |
| 1666 | + error = readl_poll_timeout(ddata->module_va + 0x44, val, | |
| 1667 | + !(val & BIT(0)), 100, 50); | |
| 1668 | + if (error) | |
| 1669 | + dev_warn(ddata->dev, "rtc busy timeout\n"); | |
| 1670 | + /* Now we have ~15 microseconds to read/write various registers */ | |
| 1671 | + sysc_write(ddata, 0x6c, kick0_val); | |
| 1672 | + sysc_write(ddata, 0x70, kick1_val); | |
| 1673 | + local_irq_restore(flags); | |
| 1674 | +} | |
| 1675 | + | |
| 1676 | +static void sysc_module_unlock_quirk_rtc(struct sysc *ddata) | |
| 1677 | +{ | |
| 1678 | + sysc_quirk_rtc(ddata, false); | |
| 1679 | +} | |
| 1680 | + | |
| 1681 | +static void sysc_module_lock_quirk_rtc(struct sysc *ddata) | |
| 1682 | +{ | |
| 1683 | + sysc_quirk_rtc(ddata, true); | |
| 1451 | 1684 | } |
| 1452 | 1685 | |
| 1453 | 1686 | /* 36xx SGX needs a quirk for to bypass OCP IPG interrupt logic */ |
| @@ -1483,20 +1716,30 @@ static void sysc_reset_done_quirk_wdt(struct sysc *ddata) · 29 unmodified lines | ||
| 1483 | 1716 | dev_warn(ddata->dev, "wdt disable step2 failed\n"); |
| 1484 | 1717 | } |
| 1485 | 1718 | |
| 1719 | +/* PRUSS needs to set MSTANDBY_INIT inorder to idle properly */ | |
| 1720 | +static void sysc_module_disable_quirk_pruss(struct sysc *ddata) | |
| 1721 | +{ | |
| 1722 | + u32 reg; | |
| 1723 | + | |
| 1724 | + reg = sysc_read(ddata, ddata->offsets[SYSC_SYSCONFIG]); | |
| 1725 | + reg |= SYSC_PRUSS_STANDBY_INIT; | |
| 1726 | + sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg); | |
| 1727 | +} | |
| 1728 | + | |
| 1486 | 1729 | static void sysc_init_module_quirks(struct sysc *ddata) |
| 1487 | 1730 | { |
| 1488 | 1731 | if (ddata->legacy_mode || !ddata->name) |
| 1489 | 1732 | return; |
| 1490 | 1733 | |
| 1491 | 1734 | if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_HDQ1W) { |
| 1492 | − ddata->clk_disable_quirk = sysc_pre_reset_quirk_hdq1w; | |
| 1735 | + ddata->pre_reset_quirk = sysc_pre_reset_quirk_hdq1w; | |
| 1493 | 1736 | |
| 1494 | 1737 | return; |
| 1495 | 1738 | } |
| 1496 | 1739 | |
| 1497 | 1740 | if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_I2C) { |
| 1498 | − ddata->clk_enable_quirk = sysc_clk_enable_quirk_i2c; | |
| 1499 | − ddata->clk_disable_quirk = sysc_clk_disable_quirk_i2c; | |
| 1741 | + ddata->pre_reset_quirk = sysc_pre_reset_quirk_i2c; | |
| 1742 | + ddata->post_reset_quirk = sysc_post_reset_quirk_i2c; | |
| 1500 | 1743 | |
| 1501 | 1744 | return; |
| 1502 | 1745 | } |
| @@ -1504,6 +1747,16 @@ static void sysc_init_module_quirks(struct sysc *ddata) · 1 unmodified line | ||
| 1504 | 1747 | if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_AESS) |
| 1505 | 1748 | ddata->module_enable_quirk = sysc_module_enable_quirk_aess; |
| 1506 | 1749 | |
| 1750 | + if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_DSS_RESET) | |
| 1751 | + ddata->pre_reset_quirk = sysc_pre_reset_quirk_dss; | |
| 1752 | + | |
| 1753 | + if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_RTC_UNLOCK) { | |
| 1754 | + ddata->module_unlock_quirk = sysc_module_unlock_quirk_rtc; | |
| 1755 | + ddata->module_lock_quirk = sysc_module_lock_quirk_rtc; | |
| 1756 | + | |
| 1757 | + return; | |
| 1758 | + } | |
| 1759 | + | |
| 1507 | 1760 | if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_SGX) |
| 1508 | 1761 | ddata->module_enable_quirk = sysc_module_enable_quirk_sgx; |
| 1509 | 1762 | |
| @@ -1511,6 +1764,9 @@ static void sysc_init_module_quirks(struct sysc *ddata) · 1 unmodified line | ||
| 1511 | 1764 | ddata->reset_done_quirk = sysc_reset_done_quirk_wdt; |
| 1512 | 1765 | ddata->module_disable_quirk = sysc_reset_done_quirk_wdt; |
| 1513 | 1766 | } |
| 1767 | + | |
| 1768 | + if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_PRUSS) | |
| 1769 | + ddata->module_disable_quirk = sysc_module_disable_quirk_pruss; | |
| 1514 | 1770 | } |
| 1515 | 1771 | |
| 1516 | 1772 | static int sysc_clockdomain_init(struct sysc *ddata) |
| @@ -1572,7 +1828,7 @@ static int sysc_reset(struct sysc *ddata) · 55 unmodified lines | ||
| 1572 | 1828 | sysc_offset = ddata->offsets[SYSC_SYSCONFIG]; |
| 1573 | 1829 | syss_offset = ddata->offsets[SYSC_SYSSTATUS]; |
| 1574 | 1830 | |
| 1575 | − if (ddata->legacy_mode || sysc_offset < 0 || | |
| 1831 | + if (ddata->legacy_mode || | |
| 1576 | 1832 | ddata->cap->regbits->srst_shift < 0 || |
| 1577 | 1833 | ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT) |
| 1578 | 1834 | return 0; |
| @@ -1584,19 +1840,21 @@ static int sysc_reset(struct sysc *ddata) · 5 unmodified lines | ||
| 1584 | 1840 | else |
| 1585 | 1841 | syss_done = ddata->cfg.syss_mask; |
| 1586 | 1842 | |
| 1587 | − if (ddata->clk_disable_quirk) | |
| 1588 | − ddata->clk_disable_quirk(ddata); | |
| 1843 | + if (ddata->pre_reset_quirk) | |
| 1844 | + ddata->pre_reset_quirk(ddata); | |
| 1589 | 1845 | |
| 1590 | − sysc_val = sysc_read_sysconfig(ddata); | |
| 1591 | − sysc_val |= sysc_mask; | |
| 1592 | − sysc_write(ddata, sysc_offset, sysc_val); | |
| 1846 | + if (sysc_offset >= 0) { | |
| 1847 | + sysc_val = sysc_read_sysconfig(ddata); | |
| 1848 | + sysc_val |= sysc_mask; | |
| 1849 | + sysc_write(ddata, sysc_offset, sysc_val); | |
| 1850 | + } | |
| 1593 | 1851 | |
| 1594 | 1852 | if (ddata->cfg.srst_udelay) |
| 1595 | 1853 | usleep_range(ddata->cfg.srst_udelay, |
| 1596 | 1854 | ddata->cfg.srst_udelay * 2); |
| 1597 | 1855 | |
| 1598 | − if (ddata->clk_enable_quirk) | |
| 1599 | − ddata->clk_enable_quirk(ddata); | |
| 1856 | + if (ddata->post_reset_quirk) | |
| 1857 | + ddata->post_reset_quirk(ddata); | |
| 1600 | 1858 | |
| 1601 | 1859 | /* Poll on reset status */ |
| 1602 | 1860 | if (syss_offset >= 0) { |
| @@ -2314,6 +2572,16 @@ static const struct sysc_capabilities sysc_dra7_mcan = { · 711 unmodified lines | ||
| 2314 | 2572 | .mod_quirks = SYSS_QUIRK_RESETDONE_INVERTED, |
| 2315 | 2573 | }; |
| 2316 | 2574 | |
| 2575 | +/* | |
| 2576 | + * PRUSS found on some AM33xx, AM437x and AM57xx SoCs | |
| 2577 | + */ | |
| 2578 | +static const struct sysc_capabilities sysc_pruss = { | |
| 2579 | + .type = TI_SYSC_PRUSS, | |
| 2580 | + .sysc_mask = SYSC_PRUSS_STANDBY_INIT | SYSC_PRUSS_SUB_MWAIT, | |
| 2581 | + .regbits = &sysc_regbits_omap4_simple, | |
| 2582 | + .mod_quirks = SYSC_MODULE_QUIRK_PRUSS, | |
| 2583 | +}; | |
| 2584 | + | |
| 2317 | 2585 | static int sysc_init_pdata(struct sysc *ddata) |
| 2318 | 2586 | { |
| 2319 | 2587 | struct ti_sysc_platform_data *pdata = dev_get_platdata(ddata->dev); |
| @@ -2387,6 +2655,154 @@ static void ti_sysc_idle(struct work_struct *work) · 67 unmodified lines | ||
| 2387 | 2655 | pm_runtime_put_sync(ddata->dev); |
| 2388 | 2656 | } |
| 2389 | 2657 | |
| 2658 | +/* | |
| 2659 | + * SoC model and features detection. Only needed for SoCs that need | |
| 2660 | + * special handling for quirks, no need to list others. | |
| 2661 | + */ | |
| 2662 | +static const struct soc_device_attribute sysc_soc_match[] = { | |
| 2663 | + SOC_FLAG("OMAP242*", SOC_2420), | |
| 2664 | + SOC_FLAG("OMAP243*", SOC_2430), | |
| 2665 | + SOC_FLAG("OMAP3[45]*", SOC_3430), | |
| 2666 | + SOC_FLAG("OMAP3[67]*", SOC_3630), | |
| 2667 | + SOC_FLAG("OMAP443*", SOC_4430), | |
| 2668 | + SOC_FLAG("OMAP446*", SOC_4460), | |
| 2669 | + SOC_FLAG("OMAP447*", SOC_4470), | |
| 2670 | + SOC_FLAG("OMAP54*", SOC_5430), | |
| 2671 | + SOC_FLAG("AM433", SOC_AM3), | |
| 2672 | + SOC_FLAG("AM43*", SOC_AM4), | |
| 2673 | + SOC_FLAG("DRA7*", SOC_DRA7), | |
| 2674 | + | |
| 2675 | + { /* sentinel */ }, | |
| 2676 | +}; | |
| 2677 | + | |
| 2678 | +/* | |
| 2679 | + * List of SoCs variants with disabled features. By default we assume all | |
| 2680 | + * devices in the device tree are available so no need to list those SoCs. | |
| 2681 | + */ | |
| 2682 | +static const struct soc_device_attribute sysc_soc_feat_match[] = { | |
| 2683 | + /* OMAP3430/3530 and AM3517 variants with some accelerators disabled */ | |
| 2684 | + SOC_FLAG("AM3505", DIS_SGX), | |
| 2685 | + SOC_FLAG("OMAP3525", DIS_SGX), | |
| 2686 | + SOC_FLAG("OMAP3515", DIS_IVA | DIS_SGX), | |
| 2687 | + SOC_FLAG("OMAP3503", DIS_ISP | DIS_IVA | DIS_SGX), | |
| 2688 | + | |
| 2689 | + /* OMAP3630/DM3730 variants with some accelerators disabled */ | |
| 2690 | + SOC_FLAG("AM3703", DIS_IVA | DIS_SGX), | |
| 2691 | + SOC_FLAG("DM3725", DIS_SGX), | |
| 2692 | + SOC_FLAG("OMAP3611", DIS_ISP | DIS_IVA | DIS_SGX), | |
| 2693 | + SOC_FLAG("OMAP3615/AM3715", DIS_IVA), | |
| 2694 | + SOC_FLAG("OMAP3621", DIS_ISP), | |
| 2695 | + | |
| 2696 | + { /* sentinel */ }, | |
| 2697 | +}; | |
| 2698 | + | |
| 2699 | +static int sysc_add_disabled(unsigned long base) | |
| 2700 | +{ | |
| 2701 | + struct sysc_address *disabled_module; | |
| 2702 | + | |
| 2703 | + disabled_module = kzalloc(sizeof(*disabled_module), GFP_KERNEL); | |
| 2704 | + if (!disabled_module) | |
| 2705 | + return -ENOMEM; | |
| 2706 | + | |
| 2707 | + disabled_module->base = base; | |
| 2708 | + | |
| 2709 | + mutex_lock(&sysc_soc->list_lock); | |
| 2710 | + list_add(&disabled_module->node, &sysc_soc->disabled_modules); | |
| 2711 | + mutex_unlock(&sysc_soc->list_lock); | |
| 2712 | + | |
| 2713 | + return 0; | |
| 2714 | +} | |
| 2715 | + | |
| 2716 | +/* | |
| 2717 | + * One time init to detect the booted SoC and disable unavailable features. | |
| 2718 | + * Note that we initialize static data shared across all ti-sysc instances | |
| 2719 | + * so ddata is only used for SoC type. This can be called from module_init | |
| 2720 | + * once we no longer need to rely on platform data. | |
| 2721 | + */ | |
| 2722 | +static int sysc_init_soc(struct sysc *ddata) | |
| 2723 | +{ | |
| 2724 | + const struct soc_device_attribute *match; | |
| 2725 | + struct ti_sysc_platform_data *pdata; | |
| 2726 | + unsigned long features = 0; | |
| 2727 | + | |
| 2728 | + if (sysc_soc) | |
| 2729 | + return 0; | |
| 2730 | + | |
| 2731 | + sysc_soc = kzalloc(sizeof(*sysc_soc), GFP_KERNEL); | |
| 2732 | + if (!sysc_soc) | |
| 2733 | + return -ENOMEM; | |
| 2734 | + | |
| 2735 | + mutex_init(&sysc_soc->list_lock); | |
| 2736 | + INIT_LIST_HEAD(&sysc_soc->disabled_modules); | |
| 2737 | + sysc_soc->general_purpose = true; | |
| 2738 | + | |
| 2739 | + pdata = dev_get_platdata(ddata->dev); | |
| 2740 | + if (pdata && pdata->soc_type_gp) | |
| 2741 | + sysc_soc->general_purpose = pdata->soc_type_gp(); | |
| 2742 | + | |
| 2743 | + match = soc_device_match(sysc_soc_match); | |
| 2744 | + if (match && match->data) | |
| 2745 | + sysc_soc->soc = (int)match->data; | |
| 2746 | + | |
| 2747 | + match = soc_device_match(sysc_soc_feat_match); | |
| 2748 | + if (!match) | |
| 2749 | + return 0; | |
| 2750 | + | |
| 2751 | + if (match->data) | |
| 2752 | + features = (unsigned long)match->data; | |
| 2753 | + | |
| 2754 | + /* | |
| 2755 | + * Add disabled devices to the list based on the module base. | |
| 2756 | + * Note that this must be done before we attempt to access the | |
| 2757 | + * device and have module revision checks working. | |
| 2758 | + */ | |
| 2759 | + if (features & DIS_ISP) | |
| 2760 | + sysc_add_disabled(0x480bd400); | |
| 2761 | + if (features & DIS_IVA) | |
| 2762 | + sysc_add_disabled(0x5d000000); | |
| 2763 | + if (features & DIS_SGX) | |
| 2764 | + sysc_add_disabled(0x50000000); | |
| 2765 | + | |
| 2766 | + return 0; | |
| 2767 | +} | |
| 2768 | + | |
| 2769 | +static void sysc_cleanup_soc(void) | |
| 2770 | +{ | |
| 2771 | + struct sysc_address *disabled_module; | |
| 2772 | + struct list_head *pos, *tmp; | |
| 2773 | + | |
| 2774 | + if (!sysc_soc) | |
| 2775 | + return; | |
| 2776 | + | |
| 2777 | + mutex_lock(&sysc_soc->list_lock); | |
| 2778 | + list_for_each_safe(pos, tmp, &sysc_soc->disabled_modules) { | |
| 2779 | + disabled_module = list_entry(pos, struct sysc_address, node); | |
| 2780 | + list_del(pos); | |
| 2781 | + kfree(disabled_module); | |
| 2782 | + } | |
| 2783 | + mutex_unlock(&sysc_soc->list_lock); | |
| 2784 | +} | |
| 2785 | + | |
| 2786 | +static int sysc_check_disabled_devices(struct sysc *ddata) | |
| 2787 | +{ | |
| 2788 | + struct sysc_address *disabled_module; | |
| 2789 | + struct list_head *pos; | |
| 2790 | + int error = 0; | |
| 2791 | + | |
| 2792 | + mutex_lock(&sysc_soc->list_lock); | |
| 2793 | + list_for_each(pos, &sysc_soc->disabled_modules) { | |
| 2794 | + disabled_module = list_entry(pos, struct sysc_address, node); | |
| 2795 | + if (ddata->module_pa == disabled_module->base) { | |
| 2796 | + dev_dbg(ddata->dev, "module disabled for this SoC\n"); | |
| 2797 | + error = -ENODEV; | |
| 2798 | + break; | |
| 2799 | + } | |
| 2800 | + } | |
| 2801 | + mutex_unlock(&sysc_soc->list_lock); | |
| 2802 | + | |
| 2803 | + return error; | |
| 2804 | +} | |
| 2805 | + | |
| 2390 | 2806 | static const struct of_device_id sysc_match_table[] = { |
| 2391 | 2807 | { .compatible = "simple-bus", }, |
| 2392 | 2808 | { /* sentinel */ }, |
| @@ -2405,6 +2821,10 @@ static int sysc_probe(struct platform_device *pdev) · 12 unmodified lines | ||
| 2405 | 2821 | ddata->dev = &pdev->dev; |
| 2406 | 2822 | platform_set_drvdata(pdev, ddata); |
| 2407 | 2823 | |
| 2824 | + error = sysc_init_soc(ddata); | |
| 2825 | + if (error) | |
| 2826 | + return error; | |
| 2827 | + | |
| 2408 | 2828 | error = sysc_init_match(ddata); |
| 2409 | 2829 | if (error) |
| 2410 | 2830 | return error; |
| @@ -2435,6 +2855,10 @@ static int sysc_probe(struct platform_device *pdev) · 24 unmodified lines | ||
| 2435 | 2855 | |
| 2436 | 2856 | sysc_init_early_quirks(ddata); |
| 2437 | 2857 | |
| 2858 | + error = sysc_check_disabled_devices(ddata); | |
| 2859 | + if (error) | |
| 2860 | + return error; | |
| 2861 | + | |
| 2438 | 2862 | error = sysc_get_clocks(ddata); |
| 2439 | 2863 | if (error) |
| 2440 | 2864 | return error; |
| @@ -2539,6 +2963,7 @@ static const struct of_device_id sysc_match[] = { · 98 unmodified lines | ||
| 2539 | 2963 | { .compatible = "ti,sysc-usb-host-fs", |
| 2540 | 2964 | .data = &sysc_omap4_usb_host_fs, }, |
| 2541 | 2965 | { .compatible = "ti,sysc-dra7-mcan", .data = &sysc_dra7_mcan, }, |
| 2966 | + { .compatible = "ti,sysc-pruss", .data = &sysc_pruss, }, | |
| 2542 | 2967 | { }, |
| 2543 | 2968 | }; |
| 2544 | 2969 | MODULE_DEVICE_TABLE(of, sysc_match); |
| @@ -2565,6 +2990,7 @@ static void __exit sysc_exit(void) · 20 unmodified lines | ||
| 2565 | 2990 | { |
| 2566 | 2991 | bus_unregister_notifier(&platform_bus_type, &sysc_nb); |
| 2567 | 2992 | platform_driver_unregister(&sysc_driver); |
| 2993 | + sysc_cleanup_soc(); | |
| 2568 | 2994 | } |
| 2569 | 2995 | module_exit(sysc_exit); |
| 2570 | 2996 | |
| @@ -1,6 +1,7 @@ | ||
| 1 | 1 | # SPDX-License-Identifier: GPL-2.0-only |
| 2 | −obj-y = scmi-bus.o scmi-driver.o scmi-protocols.o | |
| 2 | +obj-y = scmi-bus.o scmi-driver.o scmi-protocols.o scmi-transport.o | |
| 3 | 3 | scmi-bus-y = bus.o |
| 4 | 4 | scmi-driver-y = driver.o |
| 5 | +scmi-transport-y = mailbox.o shmem.o | |
| 5 | 6 | scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o |
| 6 | 7 | obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o |
| @@ -33,8 +33,8 @@ enum scmi_common_cmd { · 32 unmodified lines | ||
| 33 | 33 | /** |
| 34 | 34 | * struct scmi_msg_resp_prot_version - Response for a message |
| 35 | 35 | * |
| 36 | − * @major_version: Major version of the ABI that firmware supports | |
| 37 | 36 | * @minor_version: Minor version of the ABI that firmware supports |
| 37 | + * @major_version: Major version of the ABI that firmware supports | |
| 38 | 38 | * |
| 39 | 39 | * In general, ABI version changes follow the rule that minor version increments |
| 40 | 40 | * are backward compatible. Major revision changes in ABI may not be |
| @@ -47,6 +47,19 @@ struct scmi_msg_resp_prot_version { · 6 unmodified lines | ||
| 47 | 47 | __le16 major_version; |
| 48 | 48 | }; |
| 49 | 49 | |
| 50 | +#define MSG_ID_MASK GENMASK(7, 0) | |
| 51 | +#define MSG_XTRACT_ID(hdr) FIELD_GET(MSG_ID_MASK, (hdr)) | |
| 52 | +#define MSG_TYPE_MASK GENMASK(9, 8) | |
| 53 | +#define MSG_XTRACT_TYPE(hdr) FIELD_GET(MSG_TYPE_MASK, (hdr)) | |
| 54 | +#define MSG_TYPE_COMMAND 0 | |
| 55 | +#define MSG_TYPE_DELAYED_RESP 2 | |
| 56 | +#define MSG_TYPE_NOTIFICATION 3 | |
| 57 | +#define MSG_PROTOCOL_ID_MASK GENMASK(17, 10) | |
| 58 | +#define MSG_XTRACT_PROT_ID(hdr) FIELD_GET(MSG_PROTOCOL_ID_MASK, (hdr)) | |
| 59 | +#define MSG_TOKEN_ID_MASK GENMASK(27, 18) | |
| 60 | +#define MSG_XTRACT_TOKEN(hdr) FIELD_GET(MSG_TOKEN_ID_MASK, (hdr)) | |
| 61 | +#define MSG_TOKEN_MAX (MSG_XTRACT_TOKEN(MSG_TOKEN_ID_MASK) + 1) | |
| 62 | + | |
| 50 | 63 | /** |
| 51 | 64 | * struct scmi_msg_hdr - Message(Tx/Rx) header |
| 52 | 65 | * |
| @@ -67,6 +80,33 @@ struct scmi_msg_hdr { · 14 unmodified lines | ||
| 67 | 80 | bool poll_completion; |
| 68 | 81 | }; |
| 69 | 82 | |
| 83 | +/** | |
| 84 | + * pack_scmi_header() - packs and returns 32-bit header | |
| 85 | + * | |
| 86 | + * @hdr: pointer to header containing all the information on message id, | |
| 87 | + * protocol id and sequence id. | |
| 88 | + * | |
| 89 | + * Return: 32-bit packed message header to be sent to the platform. | |
| 90 | + */ | |
| 91 | +static inline u32 pack_scmi_header(struct scmi_msg_hdr *hdr) | |
| 92 | +{ | |
| 93 | + return FIELD_PREP(MSG_ID_MASK, hdr->id) | | |
| 94 | + FIELD_PREP(MSG_TOKEN_ID_MASK, hdr->seq) | | |
| 95 | + FIELD_PREP(MSG_PROTOCOL_ID_MASK, hdr->protocol_id); | |
| 96 | +} | |
| 97 | + | |
| 98 | +/** | |
| 99 | + * unpack_scmi_header() - unpacks and records message and protocol id | |
| 100 | + * | |
| 101 | + * @msg_hdr: 32-bit packed message header sent from the platform | |
| 102 | + * @hdr: pointer to header to fetch message and protocol id. | |
| 103 | + */ | |
| 104 | +static inline void unpack_scmi_header(u32 msg_hdr, struct scmi_msg_hdr *hdr) | |
| 105 | +{ | |
| 106 | + hdr->id = MSG_XTRACT_ID(msg_hdr); | |
| 107 | + hdr->protocol_id = MSG_XTRACT_PROT_ID(msg_hdr); | |
| 108 | +} | |
| 109 | + | |
| 70 | 110 | /** |
| 71 | 111 | * struct scmi_msg - Message(Tx/Rx) structure |
| 72 | 112 | * |
| @@ -88,7 +128,7 @@ struct scmi_msg { · 15 unmodified lines | ||
| 88 | 128 | * message. If request-ACK protocol is used, we can reuse the same |
| 89 | 129 | * buffer for the rx path as we use for the tx path. |
| 90 | 130 | * @done: command message transmit completion event |
| 91 | − * @async: pointer to delayed response message received event completion | |
| 131 | + * @async_done: pointer to delayed response message received event completion | |
| 92 | 132 | */ |
| 93 | 133 | struct scmi_xfer { |
| 94 | 134 | int transfer_id; |
| @@ -113,3 +153,74 @@ void scmi_setup_protocol_implemented(const struct scmi_handle *handle, · 18 unmodified lines | ||
| 113 | 153 | u8 *prot_imp); |
| 114 | 154 | |
| 115 | 155 | int scmi_base_protocol_init(struct scmi_handle *h); |
| 156 | + | |
| 157 | +/* SCMI Transport */ | |
| 158 | +/** | |
| 159 | + * struct scmi_chan_info - Structure representing a SCMI channel information | |
| 160 | + * | |
| 161 | + * @dev: Reference to device in the SCMI hierarchy corresponding to this | |
| 162 | + * channel | |
| 163 | + * @handle: Pointer to SCMI entity handle | |
| 164 | + * @transport_info: Transport layer related information | |
| 165 | + */ | |
| 166 | +struct scmi_chan_info { | |
| 167 | + struct device *dev; | |
| 168 | + struct scmi_handle *handle; | |
| 169 | + void *transport_info; | |
| 170 | +}; | |
| 171 | + | |
| 172 | +/** | |
| 173 | + * struct scmi_transport_ops - Structure representing a SCMI transport ops | |
| 174 | + * | |
| 175 | + * @chan_available: Callback to check if channel is available or not | |
| 176 | + * @chan_setup: Callback to allocate and setup a channel | |
| 177 | + * @chan_free: Callback to free a channel | |
| 178 | + * @send_message: Callback to send a message | |
| 179 | + * @mark_txdone: Callback to mark tx as done | |
| 180 | + * @fetch_response: Callback to fetch response | |
| 181 | + * @poll_done: Callback to poll transfer status | |
| 182 | + */ | |
| 183 | +struct scmi_transport_ops { | |
| 184 | + bool (*chan_available)(struct device *dev, int idx); | |
| 185 | + int (*chan_setup)(struct scmi_chan_info *cinfo, struct device *dev, | |
| 186 | + bool tx); | |
| 187 | + int (*chan_free)(int id, void *p, void *data); | |
| 188 | + int (*send_message)(struct scmi_chan_info *cinfo, | |
| 189 | + struct scmi_xfer *xfer); | |
| 190 | + void (*mark_txdone)(struct scmi_chan_info *cinfo, int ret); | |
| 191 | + void (*fetch_response)(struct scmi_chan_info *cinfo, | |
| 192 | + struct scmi_xfer *xfer); | |
| 193 | + bool (*poll_done)(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer); | |
| 194 | +}; | |
| 195 | + | |
| 196 | +/** | |
| 197 | + * struct scmi_desc - Description of SoC integration | |
| 198 | + * | |
| 199 | + * @ops: Pointer to the transport specific ops structure | |
| 200 | + * @max_rx_timeout_ms: Timeout for communication with SoC (in Milliseconds) | |
| 201 | + * @max_msg: Maximum number of messages that can be pending | |
| 202 | + * simultaneously in the system | |
| 203 | + * @max_msg_size: Maximum size of data per message that can be handled. | |
| 204 | + */ | |
| 205 | +struct scmi_desc { | |
| 206 | + struct scmi_transport_ops *ops; | |
| 207 | + int max_rx_timeout_ms; | |
| 208 | + int max_msg; | |
| 209 | + int max_msg_size; | |
| 210 | +}; | |
| 211 | + | |
| 212 | +extern const struct scmi_desc scmi_mailbox_desc; | |
| 213 | + | |
| 214 | +void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr); | |
| 215 | +void scmi_free_channel(struct scmi_chan_info *cinfo, struct idr *idr, int id); | |
| 216 | + | |
| 217 | +/* shmem related declarations */ | |
| 218 | +struct scmi_shared_mem; | |
| 219 | + | |
| 220 | +void shmem_tx_prepare(struct scmi_shared_mem __iomem *shmem, | |
| 221 | + struct scmi_xfer *xfer); | |
| 222 | +u32 shmem_read_header(struct scmi_shared_mem __iomem *shmem); | |
| 223 | +void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem, | |
| 224 | + struct scmi_xfer *xfer); | |
| 225 | +bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem, | |
| 226 | + struct scmi_xfer *xfer); | |
| @@ -19,12 +19,10 @@ · 18 unmodified lines | ||
| 19 | 19 | #include <linux/io.h> |
| 20 | 20 | #include <linux/kernel.h> |
| 21 | 21 | #include <linux/ktime.h> |
| 22 | −#include <linux/mailbox_client.h> | |
| 23 | 22 | #include <linux/module.h> |
| 24 | 23 | #include <linux/of_address.h> |
| 25 | 24 | #include <linux/of_device.h> |
| 26 | 25 | #include <linux/processor.h> |
| 27 | −#include <linux/semaphore.h> | |
| 28 | 26 | #include <linux/slab.h> |
| 29 | 27 | |
| 30 | 28 | #include "common.h" |
| @@ -32,19 +30,6 @@ · 1 unmodified line | ||
| 32 | 30 | #define CREATE_TRACE_POINTS |
| 33 | 31 | #include <trace/events/scmi.h> |
| 34 | 32 | |
| 35 | −#define MSG_ID_MASK GENMASK(7, 0) | |
| 36 | −#define MSG_XTRACT_ID(hdr) FIELD_GET(MSG_ID_MASK, (hdr)) | |
| 37 | −#define MSG_TYPE_MASK GENMASK(9, 8) | |
| 38 | −#define MSG_XTRACT_TYPE(hdr) FIELD_GET(MSG_TYPE_MASK, (hdr)) | |
| 39 | −#define MSG_TYPE_COMMAND 0 | |
| 40 | −#define MSG_TYPE_DELAYED_RESP 2 | |
| 41 | −#define MSG_TYPE_NOTIFICATION 3 | |
| 42 | −#define MSG_PROTOCOL_ID_MASK GENMASK(17, 10) | |
| 43 | −#define MSG_XTRACT_PROT_ID(hdr) FIELD_GET(MSG_PROTOCOL_ID_MASK, (hdr)) | |
| 44 | −#define MSG_TOKEN_ID_MASK GENMASK(27, 18) | |
| 45 | −#define MSG_XTRACT_TOKEN(hdr) FIELD_GET(MSG_TOKEN_ID_MASK, (hdr)) | |
| 46 | −#define MSG_TOKEN_MAX (MSG_XTRACT_TOKEN(MSG_TOKEN_ID_MASK) + 1) | |
| 47 | − | |
| 48 | 33 | enum scmi_error_codes { |
| 49 | 34 | SCMI_SUCCESS = 0, /* Success */ |
| 50 | 35 | SCMI_ERR_SUPPORT = -1, /* Not supported */ |
| @@ -82,46 +67,14 @@ struct scmi_xfers_info { · 31 unmodified lines | ||
| 82 | 67 | spinlock_t xfer_lock; |
| 83 | 68 | }; |
| 84 | 69 | |
| 85 | −/** | |
| 86 | − * struct scmi_desc - Description of SoC integration | |
| 87 | − * | |
| 88 | − * @max_rx_timeout_ms: Timeout for communication with SoC (in Milliseconds) | |
| 89 | − * @max_msg: Maximum number of messages that can be pending | |
| 90 | − * simultaneously in the system | |
| 91 | − * @max_msg_size: Maximum size of data per message that can be handled. | |
| 92 | − */ | |
| 93 | −struct scmi_desc { | |
| 94 | − int max_rx_timeout_ms; | |
| 95 | − int max_msg; | |
| 96 | − int max_msg_size; | |
| 97 | −}; | |
| 98 | − | |
| 99 | −/** | |
| 100 | − * struct scmi_chan_info - Structure representing a SCMI channel information | |
| 101 | − * | |
| 102 | − * @cl: Mailbox Client | |
| 103 | − * @chan: Transmit/Receive mailbox channel | |
| 104 | − * @payload: Transmit/Receive mailbox channel payload area | |
| 105 | − * @dev: Reference to device in the SCMI hierarchy corresponding to this | |
| 106 | − * channel | |
| 107 | − * @handle: Pointer to SCMI entity handle | |
| 108 | − */ | |
| 109 | −struct scmi_chan_info { | |
| 110 | − struct mbox_client cl; | |
| 111 | − struct mbox_chan *chan; | |
| 112 | − void __iomem *payload; | |
| 113 | − struct device *dev; | |
| 114 | − struct scmi_handle *handle; | |
| 115 | −}; | |
| 116 | − | |
| 117 | 70 | /** |
| 118 | 71 | * struct scmi_info - Structure representing a SCMI instance |
| 119 | 72 | * |
| 120 | 73 | * @dev: Device pointer |
| 121 | 74 | * @desc: SoC description for this instance |
| 122 | − * @handle: Instance of SCMI handle to send to clients | |
| 123 | 75 | * @version: SCMI revision information containing protocol version, |
| 124 | 76 | * implementation version and (sub-)vendor identification. |
| 77 | + * @handle: Instance of SCMI handle to send to clients | |
| 125 | 78 | * @tx_minfo: Universal Transmit Message management info |
| 126 | 79 | * @tx_idr: IDR object to map protocol id to Tx channel info pointer |
| 127 | 80 | * @rx_idr: IDR object to map protocol id to Rx channel info pointer |
| @@ -143,27 +96,8 @@ struct scmi_info { · 15 unmodified lines | ||
| 143 | 96 | int users; |
| 144 | 97 | }; |
| 145 | 98 | |
| 146 | −#define client_to_scmi_chan_info(c) container_of(c, struct scmi_chan_info, cl) | |
| 147 | 99 | #define handle_to_scmi_info(h) container_of(h, struct scmi_info, handle) |
| 148 | 100 | |
| 149 | −/* | |
| 150 | − * SCMI specification requires all parameters, message headers, return | |
| 151 | − * arguments or any protocol data to be expressed in little endian | |
| 152 | − * format only. | |
| 153 | − */ | |
| 154 | −struct scmi_shared_mem { | |
| 155 | − __le32 reserved; | |
| 156 | − __le32 channel_status; | |
| 157 | −#define SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR BIT(1) | |
| 158 | −#define SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE BIT(0) | |
| 159 | − __le32 reserved1[2]; | |
| 160 | − __le32 flags; | |
| 161 | −#define SCMI_SHMEM_FLAG_INTR_ENABLED BIT(0) | |
| 162 | − __le32 length; | |
| 163 | − __le32 msg_header; | |
| 164 | − u8 msg_payload[0]; | |
| 165 | −}; | |
| 166 | − | |
| 167 | 101 | static const int scmi_linux_errmap[] = { |
| 168 | 102 | /* better than switch case as long as return value is continuous */ |
| 169 | 103 | 0, /* SCMI_SUCCESS */ |
| @@ -199,77 +133,6 @@ static inline void scmi_dump_header_dbg(struct device *dev, · 29 unmodified lines | ||
| 199 | 133 | hdr->id, hdr->seq, hdr->protocol_id); |
| 200 | 134 | } |
| 201 | 135 | |
| 202 | −static void scmi_fetch_response(struct scmi_xfer *xfer, | |
| 203 | − struct scmi_shared_mem __iomem *mem) | |
| 204 | −{ | |
| 205 | − xfer->hdr.status = ioread32(mem->msg_payload); | |
| 206 | − /* Skip the length of header and status in payload area i.e 8 bytes */ | |
| 207 | − xfer->rx.len = min_t(size_t, xfer->rx.len, ioread32(&mem->length) - 8); | |
| 208 | − | |
| 209 | − /* Take a copy to the rx buffer.. */ | |
| 210 | − memcpy_fromio(xfer->rx.buf, mem->msg_payload + 4, xfer->rx.len); | |
| 211 | −} | |
| 212 | − | |
| 213 | −/** | |
| 214 | − * pack_scmi_header() - packs and returns 32-bit header | |
| 215 | − * | |
| 216 | − * @hdr: pointer to header containing all the information on message id, | |
| 217 | − * protocol id and sequence id. | |
| 218 | − * | |
| 219 | − * Return: 32-bit packed message header to be sent to the platform. | |
| 220 | − */ | |
| 221 | −static inline u32 pack_scmi_header(struct scmi_msg_hdr *hdr) | |
| 222 | −{ | |
| 223 | − return FIELD_PREP(MSG_ID_MASK, hdr->id) | | |
| 224 | − FIELD_PREP(MSG_TOKEN_ID_MASK, hdr->seq) | | |
| 225 | − FIELD_PREP(MSG_PROTOCOL_ID_MASK, hdr->protocol_id); | |
| 226 | −} | |
| 227 | − | |
| 228 | −/** | |
| 229 | − * unpack_scmi_header() - unpacks and records message and protocol id | |
| 230 | − * | |
| 231 | − * @msg_hdr: 32-bit packed message header sent from the platform | |
| 232 | − * @hdr: pointer to header to fetch message and protocol id. | |
| 233 | − */ | |
| 234 | −static inline void unpack_scmi_header(u32 msg_hdr, struct scmi_msg_hdr *hdr) | |
| 235 | −{ | |
| 236 | − hdr->id = MSG_XTRACT_ID(msg_hdr); | |
| 237 | − hdr->protocol_id = MSG_XTRACT_PROT_ID(msg_hdr); | |
| 238 | −} | |
| 239 | − | |
| 240 | −/** | |
| 241 | − * scmi_tx_prepare() - mailbox client callback to prepare for the transfer | |
| 242 | − * | |
| 243 | − * @cl: client pointer | |
| 244 | − * @m: mailbox message | |
| 245 | − * | |
| 246 | − * This function prepares the shared memory which contains the header and the | |
| 247 | − * payload. | |
| 248 | − */ | |
| 249 | −static void scmi_tx_prepare(struct mbox_client *cl, void *m) | |
| 250 | −{ | |
| 251 | − struct scmi_xfer *t = m; | |
| 252 | − struct scmi_chan_info *cinfo = client_to_scmi_chan_info(cl); | |
| 253 | − struct scmi_shared_mem __iomem *mem = cinfo->payload; | |
| 254 | − | |
| 255 | − /* | |
| 256 | − * Ideally channel must be free by now unless OS timeout last | |
| 257 | − * request and platform continued to process the same, wait | |
| 258 | − * until it releases the shared memory, otherwise we may endup | |
| 259 | − * overwriting its response with new message payload or vice-versa | |
| 260 | − */ | |
| 261 | − spin_until_cond(ioread32(&mem->channel_status) & | |
| 262 | − SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE); | |
| 263 | − /* Mark channel busy + clear error */ | |
| 264 | − iowrite32(0x0, &mem->channel_status); | |
| 265 | − iowrite32(t->hdr.poll_completion ? 0 : SCMI_SHMEM_FLAG_INTR_ENABLED, | |
| 266 | − &mem->flags); | |
| 267 | − iowrite32(sizeof(mem->msg_header) + t->tx.len, &mem->length); | |
| 268 | − iowrite32(pack_scmi_header(&t->hdr), &mem->msg_header); | |
| 269 | − if (t->tx.buf) | |
| 270 | − memcpy_toio(mem->msg_payload, t->tx.buf, t->tx.len); | |
| 271 | −} | |
| 272 | − | |
| 273 | 136 | /** |
| 274 | 137 | * scmi_xfer_get() - Allocate one message |
| 275 | 138 | * |
| @@ -338,10 +201,10 @@ __scmi_xfer_put(struct scmi_xfers_info *minfo, struct scmi_xfer *xfer) · 62 unmodified lines | ||
| 338 | 201 | } |
| 339 | 202 | |
| 340 | 203 | /** |
| 341 | − * scmi_rx_callback() - mailbox client callback for receive messages | |
| 204 | + * scmi_rx_callback() - callback for receiving messages | |
| 342 | 205 | * |
| 343 | − * @cl: client pointer | |
| 344 | − * @m: mailbox message | |
| 206 | + * @cinfo: SCMI channel info | |
| 207 | + * @msg_hdr: Message header | |
| 345 | 208 | * |
| 346 | 209 | * Processes one received message to appropriate transfer information and |
| 347 | 210 | * signals completion of the transfer. |
| @@ -349,21 +212,14 @@ __scmi_xfer_put(struct scmi_xfers_info *minfo, struct scmi_xfer *xfer) · 1 unmodified line | ||
| 349 | 212 | * NOTE: This function will be invoked in IRQ context, hence should be |
| 350 | 213 | * as optimal as possible. |
| 351 | 214 | */ |
| 352 | −static void scmi_rx_callback(struct mbox_client *cl, void *m) | |
| 215 | +void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr) | |
| 353 | 216 | { |
| 354 | − u8 msg_type; | |
| 355 | − u32 msg_hdr; | |
| 356 | − u16 xfer_id; | |
| 357 | − struct scmi_xfer *xfer; | |
| 358 | − struct scmi_chan_info *cinfo = client_to_scmi_chan_info(cl); | |
| 359 | − struct device *dev = cinfo->dev; | |
| 360 | 217 | struct scmi_info *info = handle_to_scmi_info(cinfo->handle); |
| 361 | 218 | struct scmi_xfers_info *minfo = &info->tx_minfo; |
| 362 | − struct scmi_shared_mem __iomem *mem = cinfo->payload; | |
| 363 | − | |
| 364 | − msg_hdr = ioread32(&mem->msg_header); | |
| 365 | − msg_type = MSG_XTRACT_TYPE(msg_hdr); | |
| 366 | − xfer_id = MSG_XTRACT_TOKEN(msg_hdr); | |
| 219 | + u16 xfer_id = MSG_XTRACT_TOKEN(msg_hdr); | |
| 220 | + u8 msg_type = MSG_XTRACT_TYPE(msg_hdr); | |
| 221 | + struct device *dev = cinfo->dev; | |
| 222 | + struct scmi_xfer *xfer; | |
| 367 | 223 | |
| 368 | 224 | if (msg_type == MSG_TYPE_NOTIFICATION) |
| 369 | 225 | return; /* Notifications not yet supported */ |
| @@ -378,7 +234,7 @@ static void scmi_rx_callback(struct mbox_client *cl, void *m) · 8 unmodified lines | ||
| 378 | 234 | |
| 379 | 235 | scmi_dump_header_dbg(dev, &xfer->hdr); |
| 380 | 236 | |
| 381 | − scmi_fetch_response(xfer, mem); | |
| 237 | + info->desc->ops->fetch_response(cinfo, xfer); | |
| 382 | 238 | |
| 383 | 239 | trace_scmi_rx_done(xfer->transfer_id, xfer->hdr.id, |
| 384 | 240 | xfer->hdr.protocol_id, xfer->hdr.seq, |
| @@ -403,28 +259,15 @@ void scmi_xfer_put(const struct scmi_handle *handle, struct scmi_xfer *xfer) · 18 unmodified lines | ||
| 403 | 259 | __scmi_xfer_put(&info->tx_minfo, xfer); |
| 404 | 260 | } |
| 405 | 261 | |
| 406 | −static bool | |
| 407 | −scmi_xfer_poll_done(const struct scmi_chan_info *cinfo, struct scmi_xfer *xfer) | |
| 408 | −{ | |
| 409 | − struct scmi_shared_mem __iomem *mem = cinfo->payload; | |
| 410 | − u16 xfer_id = MSG_XTRACT_TOKEN(ioread32(&mem->msg_header)); | |
| 411 | − | |
| 412 | − if (xfer->hdr.seq != xfer_id) | |
| 413 | − return false; | |
| 414 | − | |
| 415 | − return ioread32(&mem->channel_status) & | |
| 416 | − (SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR | | |
| 417 | − SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE); | |
| 418 | −} | |
| 419 | − | |
| 420 | 262 | #define SCMI_MAX_POLL_TO_NS (100 * NSEC_PER_USEC) |
| 421 | 263 | |
| 422 | −static bool scmi_xfer_done_no_timeout(const struct scmi_chan_info *cinfo, | |
| 264 | +static bool scmi_xfer_done_no_timeout(struct scmi_chan_info *cinfo, | |
| 423 | 265 | struct scmi_xfer *xfer, ktime_t stop) |
| 424 | 266 | { |
| 425 | − ktime_t __cur = ktime_get(); | |
| 267 | + struct scmi_info *info = handle_to_scmi_info(cinfo->handle); | |
| 426 | 268 | |
| 427 | − return scmi_xfer_poll_done(cinfo, xfer) || ktime_after(__cur, stop); | |
| 269 | + return info->desc->ops->poll_done(cinfo, xfer) || | |
| 270 | + ktime_after(ktime_get(), stop); | |
| 428 | 271 | } |
| 429 | 272 | |
| 430 | 273 | /** |
| @@ -453,29 +296,26 @@ int scmi_do_xfer(const struct scmi_handle *handle, struct scmi_xfer *xfer) · 22 unmodified lines | ||
| 453 | 296 | xfer->hdr.protocol_id, xfer->hdr.seq, |
| 454 | 297 | xfer->hdr.poll_completion); |
| 455 | 298 | |
| 456 | − ret = mbox_send_message(cinfo->chan, xfer); | |
| 299 | + ret = info->desc->ops->send_message(cinfo, xfer); | |
| 457 | 300 | if (ret < 0) { |
| 458 | − dev_dbg(dev, "mbox send fail %d\n", ret); | |
| 301 | + dev_dbg(dev, "Failed to send message %d\n", ret); | |
| 459 | 302 | return ret; |
| 460 | 303 | } |
| 461 | 304 | |
| 462 | − /* mbox_send_message returns non-negative value on success, so reset */ | |
| 463 | − ret = 0; | |
| 464 | − | |
| 465 | 305 | if (xfer->hdr.poll_completion) { |
| 466 | 306 | ktime_t stop = ktime_add_ns(ktime_get(), SCMI_MAX_POLL_TO_NS); |
| 467 | 307 | |
| 468 | 308 | spin_until_cond(scmi_xfer_done_no_timeout(cinfo, xfer, stop)); |
| 469 | 309 | |
| 470 | 310 | if (ktime_before(ktime_get(), stop)) |
| 471 | − scmi_fetch_response(xfer, cinfo->payload); | |
| 311 | + info->desc->ops->fetch_response(cinfo, xfer); | |
| 472 | 312 | else |
| 473 | 313 | ret = -ETIMEDOUT; |
| 474 | 314 | } else { |
| 475 | 315 | /* And we wait for the response. */ |
| 476 | 316 | timeout = msecs_to_jiffies(info->desc->max_rx_timeout_ms); |
| 477 | 317 | if (!wait_for_completion_timeout(&xfer->done, timeout)) { |
| 478 | − dev_err(dev, "mbox timed out in resp(caller: %pS)\n", | |
| 318 | + dev_err(dev, "timed out in resp(caller: %pS)\n", | |
| 479 | 319 | (void *)_RET_IP_); |
| 480 | 320 | ret = -ETIMEDOUT; |
| 481 | 321 | } |
| @@ -484,13 +324,8 @@ int scmi_do_xfer(const struct scmi_handle *handle, struct scmi_xfer *xfer) · 2 unmodified lines | ||
| 484 | 324 | if (!ret && xfer->hdr.status) |
| 485 | 325 | ret = scmi_to_linux_errno(xfer->hdr.status); |
| 486 | 326 | |
| 487 | − /* | |
| 488 | − * NOTE: we might prefer not to need the mailbox ticker to manage the | |
| 489 | − * transfer queueing since the protocol layer queues things by itself. | |
| 490 | − * Unfortunately, we have to kick the mailbox framework after we have | |
| 491 | − * received our message. | |
| 492 | − */ | |
| 493 | − mbox_client_txdone(cinfo->chan, ret); | |
| 327 | + if (info->desc->ops->mark_txdone) | |
| 328 | + info->desc->ops->mark_txdone(cinfo, ret); | |
| 494 | 329 | |
| 495 | 330 | trace_scmi_xfer_end(xfer->transfer_id, xfer->hdr.id, |
| 496 | 331 | xfer->hdr.protocol_id, xfer->hdr.seq, |
| @@ -731,23 +566,12 @@ static int scmi_xfer_info_init(struct scmi_info *sinfo) · 234 unmodified lines | ||
| 731 | 566 | return 0; |
| 732 | 567 | } |
| 733 | 568 | |
| 734 | −static int scmi_mailbox_check(struct device_node *np, int idx) | |
| 735 | −{ | |
| 736 | − return of_parse_phandle_with_args(np, "mboxes", "#mbox-cells", | |
| 737 | − idx, NULL); | |
| 738 | −} | |
| 739 | − | |
| 740 | −static int scmi_mbox_chan_setup(struct scmi_info *info, struct device *dev, | |
| 741 | − int prot_id, bool tx) | |
| 569 | +static int scmi_chan_setup(struct scmi_info *info, struct device *dev, | |
| 570 | + int prot_id, bool tx) | |
| 742 | 571 | { |
| 743 | 572 | int ret, idx; |
| 744 | − struct resource res; | |
| 745 | − resource_size_t size; | |
| 746 | − struct device_node *shmem, *np = dev->of_node; | |
| 747 | 573 | struct scmi_chan_info *cinfo; |
| 748 | − struct mbox_client *cl; | |
| 749 | 574 | struct idr *idr; |
| 750 | − const char *desc = tx ? "Tx" : "Rx"; | |
| 751 | 575 | |
| 752 | 576 | /* Transmit channel is first entry i.e. index 0 */ |
| 753 | 577 | idx = tx ? 0 : 1; |
| @@ -758,7 +582,7 @@ static int scmi_mbox_chan_setup(struct scmi_info *info, struct device *dev, · 4 unmodified lines | ||
| 758 | 582 | if (cinfo) |
| 759 | 583 | return 0; |
| 760 | 584 | |
| 761 | − if (scmi_mailbox_check(np, idx)) { | |
| 585 | + if (!info->desc->ops->chan_available(dev, idx)) { | |
| 762 | 586 | cinfo = idr_find(idr, SCMI_PROTOCOL_BASE); |
| 763 | 587 | if (unlikely(!cinfo)) /* Possible only if platform has no Rx */ |
| 764 | 588 | return -EINVAL; |
| @@ -771,36 +595,9 @@ static int scmi_mbox_chan_setup(struct scmi_info *info, struct device *dev, · 6 unmodified lines | ||
| 771 | 595 | |
| 772 | 596 | cinfo->dev = dev; |
| 773 | 597 | |
| 774 | − cl = &cinfo->cl; | |
| 775 | − cl->dev = dev; | |
| 776 | − cl->rx_callback = scmi_rx_callback; | |
| 777 | − cl->tx_prepare = tx ? scmi_tx_prepare : NULL; | |
| 778 | − cl->tx_block = false; | |
| 779 | − cl->knows_txdone = tx; | |
| 780 | − | |
| 781 | − shmem = of_parse_phandle(np, "shmem", idx); | |
| 782 | − ret = of_address_to_resource(shmem, 0, &res); | |
| 783 | − of_node_put(shmem); | |
| 784 | − if (ret) { | |
| 785 | − dev_err(dev, "failed to get SCMI %s payload memory\n", desc); | |
| 786 | − return ret; | |
| 787 | − } | |
| 788 | − | |
| 789 | − size = resource_size(&res); | |
| 790 | − cinfo->payload = devm_ioremap(info->dev, res.start, size); | |
| 791 | − if (!cinfo->payload) { | |
| 792 | − dev_err(dev, "failed to ioremap SCMI %s payload\n", desc); | |
| 793 | − return -EADDRNOTAVAIL; | |
| 794 | − } | |
| 795 | − | |
| 796 | − cinfo->chan = mbox_request_channel(cl, idx); | |
| 797 | − if (IS_ERR(cinfo->chan)) { | |
| 798 | − ret = PTR_ERR(cinfo->chan); | |
| 799 | − if (ret != -EPROBE_DEFER) | |
| 800 | − dev_err(dev, "failed to request SCMI %s mailbox\n", | |
| 801 | − desc); | |
| 598 | + ret = info->desc->ops->chan_setup(cinfo, info->dev, tx); | |
| 599 | + if (ret) | |
| 802 | 600 | return ret; |
| 803 | − } | |
| 804 | 601 | |
| 805 | 602 | idr_alloc: |
| 806 | 603 | ret = idr_alloc(idr, cinfo, prot_id, prot_id + 1, GFP_KERNEL); |
| @@ -814,12 +611,12 @@ idr_alloc: · 7 unmodified lines | ||
| 814 | 611 | } |
| 815 | 612 | |
| 816 | 613 | static inline int |
| 817 | −scmi_mbox_txrx_setup(struct scmi_info *info, struct device *dev, int prot_id) | |
| 614 | +scmi_txrx_setup(struct scmi_info *info, struct device *dev, int prot_id) | |
| 818 | 615 | { |
| 819 | − int ret = scmi_mbox_chan_setup(info, dev, prot_id, true); | |
| 616 | + int ret = scmi_chan_setup(info, dev, prot_id, true); | |
| 820 | 617 | |
| 821 | 618 | if (!ret) /* Rx is optional, hence no error check */ |
| 822 | − scmi_mbox_chan_setup(info, dev, prot_id, false); | |
| 619 | + scmi_chan_setup(info, dev, prot_id, false); | |
| 823 | 620 | |
| 824 | 621 | return ret; |
| 825 | 622 | } |
| @@ -837,7 +634,7 @@ scmi_create_protocol_device(struct device_node *np, struct scmi_info *info, · 11 unmodified lines | ||
| 837 | 634 | return; |
| 838 | 635 | } |
| 839 | 636 | |
| 840 | − if (scmi_mbox_txrx_setup(info, &sdev->dev, prot_id)) { | |
| 637 | + if (scmi_txrx_setup(info, &sdev->dev, prot_id)) { | |
| 841 | 638 | dev_err(&sdev->dev, "failed to setup transport\n"); |
| 842 | 639 | scmi_device_destroy(sdev); |
| 843 | 640 | return; |
| @@ -890,12 +687,6 @@ static int scmi_probe(struct platform_device *pdev) · 46 unmodified lines | ||
| 890 | 687 | struct device *dev = &pdev->dev; |
| 891 | 688 | struct device_node *child, *np = dev->of_node; |
| 892 | 689 | |
| 893 | − /* Only mailbox method supported, check for the presence of one */ | |
| 894 | − if (scmi_mailbox_check(np, 0)) { | |
| 895 | − dev_err(dev, "no mailbox found in %pOF\n", np); | |
| 896 | − return -EINVAL; | |
| 897 | − } | |
| 898 | − | |
| 899 | 690 | desc = of_device_get_match_data(dev); |
| 900 | 691 | if (!desc) |
| 901 | 692 | return -EINVAL; |
| @@ -920,7 +711,7 @@ static int scmi_probe(struct platform_device *pdev) · 18 unmodified lines | ||
| 920 | 711 | handle->dev = info->dev; |
| 921 | 712 | handle->version = &info->version; |
| 922 | 713 | |
| 923 | − ret = scmi_mbox_txrx_setup(info, dev, SCMI_PROTOCOL_BASE); | |
| 714 | + ret = scmi_txrx_setup(info, dev, SCMI_PROTOCOL_BASE); | |
| 924 | 715 | if (ret) |
| 925 | 716 | return ret; |
| 926 | 717 | |
| @@ -955,19 +746,9 @@ static int scmi_probe(struct platform_device *pdev) · 28 unmodified lines | ||
| 955 | 746 | return 0; |
| 956 | 747 | } |
| 957 | 748 | |
| 958 | −static int scmi_mbox_free_channel(int id, void *p, void *data) | |
| 749 | +void scmi_free_channel(struct scmi_chan_info *cinfo, struct idr *idr, int id) | |
| 959 | 750 | { |
| 960 | − struct scmi_chan_info *cinfo = p; | |
| 961 | − struct idr *idr = data; | |
| 962 | − | |
| 963 | − if (!IS_ERR_OR_NULL(cinfo->chan)) { | |
| 964 | − mbox_free_channel(cinfo->chan); | |
| 965 | − cinfo->chan = NULL; | |
| 966 | − } | |
| 967 | − | |
| 968 | 751 | idr_remove(idr, id); |
| 969 | − | |
| 970 | − return 0; | |
| 971 | 752 | } |
| 972 | 753 | |
| 973 | 754 | static int scmi_remove(struct platform_device *pdev) |
| @@ -987,11 +768,11 @@ static int scmi_remove(struct platform_device *pdev) · 13 unmodified lines | ||
| 987 | 768 | return ret; |
| 988 | 769 | |
| 989 | 770 | /* Safe to free channels since no more users */ |
| 990 | − ret = idr_for_each(idr, scmi_mbox_free_channel, idr); | |
| 771 | + ret = idr_for_each(idr, info->desc->ops->chan_free, idr); | |
| 991 | 772 | idr_destroy(&info->tx_idr); |
| 992 | 773 | |
| 993 | 774 | idr = &info->rx_idr; |
| 994 | − ret = idr_for_each(idr, scmi_mbox_free_channel, idr); | |
| 775 | + ret = idr_for_each(idr, info->desc->ops->chan_free, idr); | |
| 995 | 776 | idr_destroy(&info->rx_idr); |
| 996 | 777 | |
| 997 | 778 | return ret; |
| @@ -1043,15 +824,9 @@ static struct attribute *versions_attrs[] = { · 45 unmodified lines | ||
| 1043 | 824 | }; |
| 1044 | 825 | ATTRIBUTE_GROUPS(versions); |
| 1045 | 826 | |
| 1046 | −static const struct scmi_desc scmi_generic_desc = { | |
| 1047 | − .max_rx_timeout_ms = 30, /* We may increase this if required */ | |
| 1048 | − .max_msg = 20, /* Limited by MBOX_TX_QUEUE_LEN */ | |
| 1049 | − .max_msg_size = 128, | |
| 1050 | −}; | |
| 1051 | − | |
| 1052 | 827 | /* Each compatible listed below must have descriptor associated with it */ |
| 1053 | 828 | static const struct of_device_id scmi_of_match[] = { |
| 1054 | − { .compatible = "arm,scmi", .data = &scmi_generic_desc }, | |
| 829 | + { .compatible = "arm,scmi", .data = &scmi_mailbox_desc }, | |
| 1055 | 830 | { /* Sentinel */ }, |
| 1056 | 831 | }; |
| 1057 | 832 | |
| @@ -0,0 +1,184 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* | |
| 3 | + * System Control and Management Interface (SCMI) Message Mailbox Transport | |
| 4 | + * driver. | |
| 5 | + * | |
| 6 | + * Copyright (C) 2019 ARM Ltd. | |
| 7 | + */ | |
| 8 | + | |
| 9 | +#include <linux/err.h> | |
| 10 | +#include <linux/device.h> | |
| 11 | +#include <linux/mailbox_client.h> | |
| 12 | +#include <linux/of.h> | |
| 13 | +#include <linux/of_address.h> | |
| 14 | +#include <linux/slab.h> | |
| 15 | + | |
| 16 | +#include "common.h" | |
| 17 | + | |
| 18 | +/** | |
| 19 | + * struct scmi_mailbox - Structure representing a SCMI mailbox transport | |
| 20 | + * | |
| 21 | + * @cl: Mailbox Client | |
| 22 | + * @chan: Transmit/Receive mailbox channel | |
| 23 | + * @cinfo: SCMI channel info | |
| 24 | + * @shmem: Transmit/Receive shared memory area | |
| 25 | + */ | |
| 26 | +struct scmi_mailbox { | |
| 27 | + struct mbox_client cl; | |
| 28 | + struct mbox_chan *chan; | |
| 29 | + struct scmi_chan_info *cinfo; | |
| 30 | + struct scmi_shared_mem __iomem *shmem; | |
| 31 | +}; | |
| 32 | + | |
| 33 | +#define client_to_scmi_mailbox(c) container_of(c, struct scmi_mailbox, cl) | |
| 34 | + | |
| 35 | +static void tx_prepare(struct mbox_client *cl, void *m) | |
| 36 | +{ | |
| 37 | + struct scmi_mailbox *smbox = client_to_scmi_mailbox(cl); | |
| 38 | + | |
| 39 | + shmem_tx_prepare(smbox->shmem, m); | |
| 40 | +} | |
| 41 | + | |
| 42 | +static void rx_callback(struct mbox_client *cl, void *m) | |
| 43 | +{ | |
| 44 | + struct scmi_mailbox *smbox = client_to_scmi_mailbox(cl); | |
| 45 | + | |
| 46 | + scmi_rx_callback(smbox->cinfo, shmem_read_header(smbox->shmem)); | |
| 47 | +} | |
| 48 | + | |
| 49 | +static bool mailbox_chan_available(struct device *dev, int idx) | |
| 50 | +{ | |
| 51 | + return !of_parse_phandle_with_args(dev->of_node, "mboxes", | |
| 52 | + "#mbox-cells", idx, NULL); | |
| 53 | +} | |
| 54 | + | |
| 55 | +static int mailbox_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, | |
| 56 | + bool tx) | |
| 57 | +{ | |
| 58 | + const char *desc = tx ? "Tx" : "Rx"; | |
| 59 | + struct device *cdev = cinfo->dev; | |
| 60 | + struct scmi_mailbox *smbox; | |
| 61 | + struct device_node *shmem; | |
| 62 | + int ret, idx = tx ? 0 : 1; | |
| 63 | + struct mbox_client *cl; | |
| 64 | + resource_size_t size; | |
| 65 | + struct resource res; | |
| 66 | + | |
| 67 | + smbox = devm_kzalloc(dev, sizeof(*smbox), GFP_KERNEL); | |
| 68 | + if (!smbox) | |
| 69 | + return -ENOMEM; | |
| 70 | + | |
| 71 | + shmem = of_parse_phandle(cdev->of_node, "shmem", idx); | |
| 72 | + ret = of_address_to_resource(shmem, 0, &res); | |
| 73 | + of_node_put(shmem); | |
| 74 | + if (ret) { | |
| 75 | + dev_err(cdev, "failed to get SCMI %s shared memory\n", desc); | |
| 76 | + return ret; | |
| 77 | + } | |
| 78 | + | |
| 79 | + size = resource_size(&res); | |
| 80 | + smbox->shmem = devm_ioremap(dev, res.start, size); | |
| 81 | + if (!smbox->shmem) { | |
| 82 | + dev_err(dev, "failed to ioremap SCMI %s shared memory\n", desc); | |
| 83 | + return -EADDRNOTAVAIL; | |
| 84 | + } | |
| 85 | + | |
| 86 | + cl = &smbox->cl; | |
| 87 | + cl->dev = cdev; | |
| 88 | + cl->tx_prepare = tx ? tx_prepare : NULL; | |
| 89 | + cl->rx_callback = rx_callback; | |
| 90 | + cl->tx_block = false; | |
| 91 | + cl->knows_txdone = tx; | |
| 92 | + | |
| 93 | + smbox->chan = mbox_request_channel(cl, tx ? 0 : 1); | |
| 94 | + if (IS_ERR(smbox->chan)) { | |
| 95 | + ret = PTR_ERR(smbox->chan); | |
| 96 | + if (ret != -EPROBE_DEFER) | |
| 97 | + dev_err(cdev, "failed to request SCMI %s mailbox\n", | |
| 98 | + tx ? "Tx" : "Rx"); | |
| 99 | + return ret; | |
| 100 | + } | |
| 101 | + | |
| 102 | + cinfo->transport_info = smbox; | |
| 103 | + smbox->cinfo = cinfo; | |
| 104 | + | |
| 105 | + return 0; | |
| 106 | +} | |
| 107 | + | |
| 108 | +static int mailbox_chan_free(int id, void *p, void *data) | |
| 109 | +{ | |
| 110 | + struct scmi_chan_info *cinfo = p; | |
| 111 | + struct scmi_mailbox *smbox = cinfo->transport_info; | |
| 112 | + | |
| 113 | + if (!IS_ERR(smbox->chan)) { | |
| 114 | + mbox_free_channel(smbox->chan); | |
| 115 | + cinfo->transport_info = NULL; | |
| 116 | + smbox->chan = NULL; | |
| 117 | + smbox->cinfo = NULL; | |
| 118 | + } | |
| 119 | + | |
| 120 | + scmi_free_channel(cinfo, data, id); | |
| 121 | + | |
| 122 | + return 0; | |
| 123 | +} | |
| 124 | + | |
| 125 | +static int mailbox_send_message(struct scmi_chan_info *cinfo, | |
| 126 | + struct scmi_xfer *xfer) | |
| 127 | +{ | |
| 128 | + struct scmi_mailbox *smbox = cinfo->transport_info; | |
| 129 | + int ret; | |
| 130 | + | |
| 131 | + ret = mbox_send_message(smbox->chan, xfer); | |
| 132 | + | |
| 133 | + /* mbox_send_message returns non-negative value on success, so reset */ | |
| 134 | + if (ret > 0) | |
| 135 | + ret = 0; | |
| 136 | + | |
| 137 | + return ret; | |
| 138 | +} | |
| 139 | + | |
| 140 | +static void mailbox_mark_txdone(struct scmi_chan_info *cinfo, int ret) | |
| 141 | +{ | |
| 142 | + struct scmi_mailbox *smbox = cinfo->transport_info; | |
| 143 | + | |
| 144 | + /* | |
| 145 | + * NOTE: we might prefer not to need the mailbox ticker to manage the | |
| 146 | + * transfer queueing since the protocol layer queues things by itself. | |
| 147 | + * Unfortunately, we have to kick the mailbox framework after we have | |
| 148 | + * received our message. | |
| 149 | + */ | |
| 150 | + mbox_client_txdone(smbox->chan, ret); | |
| 151 | +} | |
| 152 | + | |
| 153 | +static void mailbox_fetch_response(struct scmi_chan_info *cinfo, | |
| 154 | + struct scmi_xfer *xfer) | |
| 155 | +{ | |
| 156 | + struct scmi_mailbox *smbox = cinfo->transport_info; | |
| 157 | + | |
| 158 | + shmem_fetch_response(smbox->shmem, xfer); | |
| 159 | +} | |
| 160 | + | |
| 161 | +static bool | |
| 162 | +mailbox_poll_done(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer) | |
| 163 | +{ | |
| 164 | + struct scmi_mailbox *smbox = cinfo->transport_info; | |
| 165 | + | |
| 166 | + return shmem_poll_done(smbox->shmem, xfer); | |
| 167 | +} | |
| 168 | + | |
| 169 | +static struct scmi_transport_ops scmi_mailbox_ops = { | |
| 170 | + .chan_available = mailbox_chan_available, | |
| 171 | + .chan_setup = mailbox_chan_setup, | |
| 172 | + .chan_free = mailbox_chan_free, | |
| 173 | + .send_message = mailbox_send_message, | |
| 174 | + .mark_txdone = mailbox_mark_txdone, | |
| 175 | + .fetch_response = mailbox_fetch_response, | |
| 176 | + .poll_done = mailbox_poll_done, | |
| 177 | +}; | |
| 178 | + | |
| 179 | +const struct scmi_desc scmi_mailbox_desc = { | |
| 180 | + .ops = &scmi_mailbox_ops, | |
| 181 | + .max_rx_timeout_ms = 30, /* We may increase this if required */ | |
| 182 | + .max_msg = 20, /* Limited by MBOX_TX_QUEUE_LEN */ | |
| 183 | + .max_msg_size = 128, | |
| 184 | +}; | |
| @@ -89,7 +89,7 @@ struct scmi_msg_resp_perf_describe_levels { · 88 unmodified lines | ||
| 89 | 89 | __le32 power; |
| 90 | 90 | __le16 transition_latency_us; |
| 91 | 91 | __le16 reserved; |
| 92 | − } opp[0]; | |
| 92 | + } opp[]; | |
| 93 | 93 | }; |
| 94 | 94 | |
| 95 | 95 | struct scmi_perf_get_fc_info { |
| @@ -0,0 +1,83 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* | |
| 3 | + * For transport using shared mem structure. | |
| 4 | + * | |
| 5 | + * Copyright (C) 2019 ARM Ltd. | |
| 6 | + */ | |
| 7 | + | |
| 8 | +#include <linux/io.h> | |
| 9 | +#include <linux/processor.h> | |
| 10 | +#include <linux/types.h> | |
| 11 | + | |
| 12 | +#include "common.h" | |
| 13 | + | |
| 14 | +/* | |
| 15 | + * SCMI specification requires all parameters, message headers, return | |
| 16 | + * arguments or any protocol data to be expressed in little endian | |
| 17 | + * format only. | |
| 18 | + */ | |
| 19 | +struct scmi_shared_mem { | |
| 20 | + __le32 reserved; | |
| 21 | + __le32 channel_status; | |
| 22 | +#define SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR BIT(1) | |
| 23 | +#define SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE BIT(0) | |
| 24 | + __le32 reserved1[2]; | |
| 25 | + __le32 flags; | |
| 26 | +#define SCMI_SHMEM_FLAG_INTR_ENABLED BIT(0) | |
| 27 | + __le32 length; | |
| 28 | + __le32 msg_header; | |
| 29 | + u8 msg_payload[]; | |
| 30 | +}; | |
| 31 | + | |
| 32 | +void shmem_tx_prepare(struct scmi_shared_mem __iomem *shmem, | |
| 33 | + struct scmi_xfer *xfer) | |
| 34 | +{ | |
| 35 | + /* | |
| 36 | + * Ideally channel must be free by now unless OS timeout last | |
| 37 | + * request and platform continued to process the same, wait | |
| 38 | + * until it releases the shared memory, otherwise we may endup | |
| 39 | + * overwriting its response with new message payload or vice-versa | |
| 40 | + */ | |
| 41 | + spin_until_cond(ioread32(&shmem->channel_status) & | |
| 42 | + SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE); | |
| 43 | + /* Mark channel busy + clear error */ | |
| 44 | + iowrite32(0x0, &shmem->channel_status); | |
| 45 | + iowrite32(xfer->hdr.poll_completion ? 0 : SCMI_SHMEM_FLAG_INTR_ENABLED, | |
| 46 | + &shmem->flags); | |
| 47 | + iowrite32(sizeof(shmem->msg_header) + xfer->tx.len, &shmem->length); | |
| 48 | + iowrite32(pack_scmi_header(&xfer->hdr), &shmem->msg_header); | |
| 49 | + if (xfer->tx.buf) | |
| 50 | + memcpy_toio(shmem->msg_payload, xfer->tx.buf, xfer->tx.len); | |
| 51 | +} | |
| 52 | + | |
| 53 | +u32 shmem_read_header(struct scmi_shared_mem __iomem *shmem) | |
| 54 | +{ | |
| 55 | + return ioread32(&shmem->msg_header); | |
| 56 | +} | |
| 57 | + | |
| 58 | +void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem, | |
| 59 | + struct scmi_xfer *xfer) | |
| 60 | +{ | |
| 61 | + xfer->hdr.status = ioread32(shmem->msg_payload); | |
| 62 | + /* Skip the length of header and status in shmem area i.e 8 bytes */ | |
| 63 | + xfer->rx.len = min_t(size_t, xfer->rx.len, | |
| 64 | + ioread32(&shmem->length) - 8); | |
| 65 | + | |
| 66 | + /* Take a copy to the rx buffer.. */ | |
| 67 | + memcpy_fromio(xfer->rx.buf, shmem->msg_payload + 4, xfer->rx.len); | |
| 68 | +} | |
| 69 | + | |
| 70 | +bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem, | |
| 71 | + struct scmi_xfer *xfer) | |
| 72 | +{ | |
| 73 | + u16 xfer_id; | |
| 74 | + | |
| 75 | + xfer_id = MSG_XTRACT_TOKEN(ioread32(&shmem->msg_header)); | |
| 76 | + | |
| 77 | + if (xfer->hdr.seq != xfer_id) | |
| 78 | + return false; | |
| 79 | + | |
| 80 | + return ioread32(&shmem->channel_status) & | |
| 81 | + (SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR | | |
| 82 | + SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE); | |
| 83 | +} | |
| @@ -262,12 +262,12 @@ struct scpi_drvinfo { · 261 unmodified lines | ||
| 262 | 262 | struct scpi_shared_mem { |
| 263 | 263 | __le32 command; |
| 264 | 264 | __le32 status; |
| 265 | − u8 payload[0]; | |
| 265 | + u8 payload[]; | |
| 266 | 266 | } __packed; |
| 267 | 267 | |
| 268 | 268 | struct legacy_scpi_shared_mem { |
| 269 | 269 | __le32 status; |
| 270 | − u8 payload[0]; | |
| 270 | + u8 payload[]; | |
| 271 | 271 | } __packed; |
| 272 | 272 | |
| 273 | 273 | struct scp_capabilities { |
| @@ -93,7 +93,7 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { · 92 unmodified lines | ||
| 93 | 93 | { "kpp", IMX_SC_R_KPP, 1, false, 0 }, |
| 94 | 94 | { "fspi", IMX_SC_R_FSPI_0, 2, true, 0 }, |
| 95 | 95 | { "mu_a", IMX_SC_R_MU_0A, 14, true, 0 }, |
| 96 | − { "mu_b", IMX_SC_R_MU_13B, 1, true, 13 }, | |
| 96 | + { "mu_b", IMX_SC_R_MU_5B, 9, true, 5 }, | |
| 97 | 97 | |
| 98 | 98 | /* CONN SS */ |
| 99 | 99 | { "usb", IMX_SC_R_USB_0, 2, true, 0 }, |
| @@ -109,6 +109,7 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { · 9 unmodified lines | ||
| 109 | 109 | { "audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, false, 0 }, |
| 110 | 110 | { "audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, false, 0 }, |
| 111 | 111 | { "audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, false, 0 }, |
| 112 | + { "audio-clk-1", IMX_SC_R_AUDIO_CLK_1, 1, false, 0 }, | |
| 112 | 113 | { "dma0-ch", IMX_SC_R_DMA_0_CH0, 16, true, 0 }, |
| 113 | 114 | { "dma1-ch", IMX_SC_R_DMA_1_CH0, 16, true, 0 }, |
| 114 | 115 | { "dma2-ch", IMX_SC_R_DMA_2_CH0, 5, true, 0 }, |
| @@ -116,7 +117,13 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { · 1 unmodified line | ||
| 116 | 117 | { "asrc1", IMX_SC_R_ASRC_1, 1, false, 0 }, |
| 117 | 118 | { "esai0", IMX_SC_R_ESAI_0, 1, false, 0 }, |
| 118 | 119 | { "spdif0", IMX_SC_R_SPDIF_0, 1, false, 0 }, |
| 120 | + { "spdif1", IMX_SC_R_SPDIF_1, 1, false, 0 }, | |
| 119 | 121 | { "sai", IMX_SC_R_SAI_0, 3, true, 0 }, |
| 122 | + { "sai3", IMX_SC_R_SAI_3, 1, false, 0 }, | |
| 123 | + { "sai4", IMX_SC_R_SAI_4, 1, false, 0 }, | |
| 124 | + { "sai5", IMX_SC_R_SAI_5, 1, false, 0 }, | |
| 125 | + { "sai6", IMX_SC_R_SAI_6, 1, false, 0 }, | |
| 126 | + { "sai7", IMX_SC_R_SAI_7, 1, false, 0 }, | |
| 120 | 127 | { "amix", IMX_SC_R_AMIX, 1, false, 0 }, |
| 121 | 128 | { "mqs0", IMX_SC_R_MQS_0, 1, false, 0 }, |
| 122 | 129 | { "dsp", IMX_SC_R_DSP, 1, false, 0 }, |
| @@ -158,6 +165,10 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { · 35 unmodified lines | ||
| 158 | 165 | /* DC SS */ |
| 159 | 166 | { "dc0", IMX_SC_R_DC_0, 1, false, 0 }, |
| 160 | 167 | { "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, true, 0 }, |
| 168 | + | |
| 169 | + /* CM40 SS */ | |
| 170 | + { "cm40_i2c", IMX_SC_R_M4_0_I2C, 1, 0 }, | |
| 171 | + { "cm40_intmux", IMX_SC_R_M4_0_INTMUX, 1, 0 }, | |
| 161 | 172 | }; |
| 162 | 173 | |
| 163 | 174 | static const struct imx_sc_pd_soc imx8qxp_scu_pd = { |
| @@ -44,6 +44,8 @@ static const struct meson_sm_chip gxbb_chip = { · 43 unmodified lines | ||
| 44 | 44 | CMD(SM_EFUSE_WRITE, 0x82000031), |
| 45 | 45 | CMD(SM_EFUSE_USER_MAX, 0x82000033), |
| 46 | 46 | CMD(SM_GET_CHIP_ID, 0x82000044), |
| 47 | + CMD(SM_A1_PWRC_SET, 0x82000093), | |
| 48 | + CMD(SM_A1_PWRC_GET, 0x82000095), | |
| 47 | 49 | { /* sentinel */ }, |
| 48 | 50 | }, |
| 49 | 51 | }; |
| @@ -7,7 +7,7 @@ config TEGRA_IVC · 6 unmodified lines | ||
| 7 | 7 | help |
| 8 | 8 | IVC (Inter-VM Communication) protocol is part of the IPC |
| 9 | 9 | (Inter Processor Communication) framework on Tegra. It maintains the |
| 10 | − data and the different commuication channels in SysRAM or RAM and | |
| 10 | + data and the different communication channels in SysRAM or RAM and | |
| 11 | 11 | keeps the content is synchronization between host CPU and remote |
| 12 | 12 | processors. |
| 13 | 13 | |
| @@ -1348,9 +1348,15 @@ static int dss_component_compare(struct device *dev, void *data) · 1347 unmodified lines | ||
| 1348 | 1348 | return dev == child; |
| 1349 | 1349 | } |
| 1350 | 1350 | |
| 1351 | +struct dss_component_match_data { | |
| 1352 | + struct device *dev; | |
| 1353 | + struct component_match **match; | |
| 1354 | +}; | |
| 1355 | + | |
| 1351 | 1356 | static int dss_add_child_component(struct device *dev, void *data) |
| 1352 | 1357 | { |
| 1353 | − struct component_match **match = data; | |
| 1358 | + struct dss_component_match_data *cmatch = data; | |
| 1359 | + struct component_match **match = cmatch->match; | |
| 1354 | 1360 | |
| 1355 | 1361 | /* |
| 1356 | 1362 | * HACK |
| @@ -1361,7 +1367,17 @@ static int dss_add_child_component(struct device *dev, void *data) · 4 unmodified lines | ||
| 1361 | 1367 | if (strstr(dev_name(dev), "rfbi")) |
| 1362 | 1368 | return 0; |
| 1363 | 1369 | |
| 1364 | − component_match_add(dev->parent, match, dss_component_compare, dev); | |
| 1370 | + /* | |
| 1371 | + * Handle possible interconnect target modules defined within the DSS. | |
| 1372 | + * The DSS components can be children of an interconnect target module | |
| 1373 | + * after the device tree has been updated for the module data. | |
| 1374 | + * See also omapdss_boot_init() for compatible fixup. | |
| 1375 | + */ | |
| 1376 | + if (strstr(dev_name(dev), "target-module")) | |
| 1377 | + return device_for_each_child(dev, cmatch, | |
| 1378 | + dss_add_child_component); | |
| 1379 | + | |
| 1380 | + component_match_add(cmatch->dev, match, dss_component_compare, dev); | |
| 1365 | 1381 | |
| 1366 | 1382 | return 0; |
| 1367 | 1383 | } |
| @@ -1404,6 +1420,7 @@ static int dss_probe_hardware(struct dss_device *dss) · 36 unmodified lines | ||
| 1404 | 1420 | static int dss_probe(struct platform_device *pdev) |
| 1405 | 1421 | { |
| 1406 | 1422 | const struct soc_device_attribute *soc; |
| 1423 | + struct dss_component_match_data cmatch; | |
| 1407 | 1424 | struct component_match *match = NULL; |
| 1408 | 1425 | struct resource *dss_mem; |
| 1409 | 1426 | struct dss_device *dss; |
| @@ -1481,7 +1498,9 @@ static int dss_probe(struct platform_device *pdev) · 71 unmodified lines | ||
| 1481 | 1498 | |
| 1482 | 1499 | omapdss_gather_components(&pdev->dev); |
| 1483 | 1500 | |
| 1484 | − device_for_each_child(&pdev->dev, &match, dss_add_child_component); | |
| 1501 | + cmatch.dev = &pdev->dev; | |
| 1502 | + cmatch.match = &match; | |
| 1503 | + device_for_each_child(&pdev->dev, &cmatch, dss_add_child_component); | |
| 1485 | 1504 | |
| 1486 | 1505 | r = component_master_add_with_match(&pdev->dev, &dss_component_ops, match); |
| 1487 | 1506 | if (r) |
| @@ -178,9 +178,24 @@ static const struct of_device_id omapdss_of_fixups_whitelist[] __initconst = { · 177 unmodified lines | ||
| 178 | 178 | {}, |
| 179 | 179 | }; |
| 180 | 180 | |
| 181 | +static void __init omapdss_find_children(struct device_node *np) | |
| 182 | +{ | |
| 183 | + struct device_node *child; | |
| 184 | + | |
| 185 | + for_each_available_child_of_node(np, child) { | |
| 186 | + if (!of_find_property(child, "compatible", NULL)) | |
| 187 | + continue; | |
| 188 | + | |
| 189 | + omapdss_walk_device(child, true); | |
| 190 | + | |
| 191 | + if (of_device_is_compatible(child, "ti,sysc")) | |
| 192 | + omapdss_find_children(child); | |
| 193 | + } | |
| 194 | +} | |
| 195 | + | |
| 181 | 196 | static int __init omapdss_boot_init(void) |
| 182 | 197 | { |
| 183 | − struct device_node *dss, *child; | |
| 198 | + struct device_node *dss; | |
| 184 | 199 | |
| 185 | 200 | INIT_LIST_HEAD(&dss_conv_list); |
| 186 | 201 | |
| @@ -190,13 +205,7 @@ static int __init omapdss_boot_init(void) · 3 unmodified lines | ||
| 190 | 205 | goto put_node; |
| 191 | 206 | |
| 192 | 207 | omapdss_walk_device(dss, true); |
| 193 | − | |
| 194 | − for_each_available_child_of_node(dss, child) { | |
| 195 | − if (!of_find_property(child, "compatible", NULL)) | |
| 196 | − continue; | |
| 197 | − | |
| 198 | − omapdss_walk_device(child, true); | |
| 199 | − } | |
| 208 | + omapdss_find_children(dss); | |
| 200 | 209 | |
| 201 | 210 | while (!list_empty(&dss_conv_list)) { |
| 202 | 211 | struct dss_conv_node *n; |
| @@ -1158,6 +1158,11 @@ static void emc_debugfs_init(struct device *dev, struct tegra_emc *emc) · 1157 unmodified lines | ||
| 1158 | 1158 | emc->debugfs.max_rate = emc->timings[i].rate; |
| 1159 | 1159 | } |
| 1160 | 1160 | |
| 1161 | + if (!emc->num_timings) { | |
| 1162 | + emc->debugfs.min_rate = clk_get_rate(emc->clk); | |
| 1163 | + emc->debugfs.max_rate = emc->debugfs.min_rate; | |
| 1164 | + } | |
| 1165 | + | |
| 1161 | 1166 | err = clk_set_rate_range(emc->clk, emc->debugfs.min_rate, |
| 1162 | 1167 | emc->debugfs.max_rate); |
| 1163 | 1168 | if (err < 0) { |
| @@ -628,6 +628,11 @@ static void tegra_emc_debugfs_init(struct tegra_emc *emc) · 627 unmodified lines | ||
| 628 | 628 | emc->debugfs.max_rate = emc->timings[i].rate; |
| 629 | 629 | } |
| 630 | 630 | |
| 631 | + if (!emc->num_timings) { | |
| 632 | + emc->debugfs.min_rate = clk_get_rate(emc->clk); | |
| 633 | + emc->debugfs.max_rate = emc->debugfs.min_rate; | |
| 634 | + } | |
| 635 | + | |
| 631 | 636 | err = clk_set_rate_range(emc->clk, emc->debugfs.min_rate, |
| 632 | 637 | emc->debugfs.max_rate); |
| 633 | 638 | if (err < 0) { |
| @@ -1256,6 +1256,11 @@ static void tegra_emc_debugfs_init(struct tegra_emc *emc) · 1255 unmodified lines | ||
| 1256 | 1256 | emc->debugfs.max_rate = emc->timings[i].rate; |
| 1257 | 1257 | } |
| 1258 | 1258 | |
| 1259 | + if (!emc->num_timings) { | |
| 1260 | + emc->debugfs.min_rate = clk_get_rate(emc->clk); | |
| 1261 | + emc->debugfs.max_rate = emc->debugfs.min_rate; | |
| 1262 | + } | |
| 1263 | + | |
| 1259 | 1264 | err = clk_set_rate_range(emc->clk, emc->debugfs.min_rate, |
| 1260 | 1265 | emc->debugfs.max_rate); |
| 1261 | 1266 | if (err < 0) { |
| @@ -11,7 +11,7 @@ obj-$(CONFIG_ARCH_DOVE) += dove/ · 10 unmodified lines | ||
| 11 | 11 | obj-$(CONFIG_MACH_DOVE) += dove/ |
| 12 | 12 | obj-y += fsl/ |
| 13 | 13 | obj-$(CONFIG_ARCH_GEMINI) += gemini/ |
| 14 | −obj-$(CONFIG_ARCH_MXC) += imx/ | |
| 14 | +obj-y += imx/ | |
| 15 | 15 | obj-$(CONFIG_ARCH_IXP4XX) += ixp4xx/ |
| 16 | 16 | obj-$(CONFIG_SOC_XWAY) += lantiq/ |
| 17 | 17 | obj-y += mediatek/ |
| @@ -48,6 +48,19 @@ config MESON_EE_PM_DOMAINS · 47 unmodified lines | ||
| 48 | 48 | Say yes to expose Amlogic Meson Everything-Else Power Domains as |
| 49 | 49 | Generic Power Domains. |
| 50 | 50 | |
| 51 | +config MESON_SECURE_PM_DOMAINS | |
| 52 | + bool "Amlogic Meson Secure Power Domains driver" | |
| 53 | + depends on (ARCH_MESON || COMPILE_TEST) && MESON_SM | |
| 54 | + depends on PM && OF | |
| 55 | + depends on HAVE_ARM_SMCCC | |
| 56 | + default ARCH_MESON | |
| 57 | + select PM_GENERIC_DOMAINS | |
| 58 | + select PM_GENERIC_DOMAINS_OF | |
| 59 | + help | |
| 60 | + Support for the power controller on Amlogic A1/C1 series. | |
| 61 | + Say yes to expose Amlogic Meson Secure Power Domains as Generic | |
| 62 | + Power Domains. | |
| 63 | + | |
| 51 | 64 | config MESON_MX_SOCINFO |
| 52 | 65 | bool "Amlogic Meson MX SoC Information driver" |
| 53 | 66 | depends on ARCH_MESON || COMPILE_TEST |
| @@ -5,3 +5,4 @@ obj-$(CONFIG_MESON_GX_SOCINFO) += meson-gx-socinfo.o · 4 unmodified lines | ||
| 5 | 5 | obj-$(CONFIG_MESON_GX_PM_DOMAINS) += meson-gx-pwrc-vpu.o |
| 6 | 6 | obj-$(CONFIG_MESON_MX_SOCINFO) += meson-mx-socinfo.o |
| 7 | 7 | obj-$(CONFIG_MESON_EE_PM_DOMAINS) += meson-ee-pwrc.o |
| 8 | +obj-$(CONFIG_MESON_SECURE_PM_DOMAINS) += meson-secure-pwrc.o | |
| @@ -0,0 +1,204 @@ | ||
| 1 | +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) | |
| 2 | +/* | |
| 3 | + * Copyright (c) 2019 Amlogic, Inc. | |
| 4 | + * Author: Jianxin Pan <jianxin.pan@amlogic.com> | |
| 5 | + */ | |
| 6 | + | |
| 7 | +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
| 8 | + | |
| 9 | +#include <linux/io.h> | |
| 10 | +#include <linux/of_device.h> | |
| 11 | +#include <linux/platform_device.h> | |
| 12 | +#include <linux/pm_domain.h> | |
| 13 | +#include <dt-bindings/power/meson-a1-power.h> | |
| 14 | +#include <linux/arm-smccc.h> | |
| 15 | +#include <linux/firmware/meson/meson_sm.h> | |
| 16 | + | |
| 17 | +#define PWRC_ON 1 | |
| 18 | +#define PWRC_OFF 0 | |
| 19 | + | |
| 20 | +struct meson_secure_pwrc_domain { | |
| 21 | + struct generic_pm_domain base; | |
| 22 | + unsigned int index; | |
| 23 | + struct meson_secure_pwrc *pwrc; | |
| 24 | +}; | |
| 25 | + | |
| 26 | +struct meson_secure_pwrc { | |
| 27 | + struct meson_secure_pwrc_domain *domains; | |
| 28 | + struct genpd_onecell_data xlate; | |
| 29 | + struct meson_sm_firmware *fw; | |
| 30 | +}; | |
| 31 | + | |
| 32 | +struct meson_secure_pwrc_domain_desc { | |
| 33 | + unsigned int index; | |
| 34 | + unsigned int flags; | |
| 35 | + char *name; | |
| 36 | + bool (*is_off)(struct meson_secure_pwrc_domain *pwrc_domain); | |
| 37 | +}; | |
| 38 | + | |
| 39 | +struct meson_secure_pwrc_domain_data { | |
| 40 | + unsigned int count; | |
| 41 | + struct meson_secure_pwrc_domain_desc *domains; | |
| 42 | +}; | |
| 43 | + | |
| 44 | +static bool pwrc_secure_is_off(struct meson_secure_pwrc_domain *pwrc_domain) | |
| 45 | +{ | |
| 46 | + int is_off = 1; | |
| 47 | + | |
| 48 | + if (meson_sm_call(pwrc_domain->pwrc->fw, SM_A1_PWRC_GET, &is_off, | |
| 49 | + pwrc_domain->index, 0, 0, 0, 0) < 0) | |
| 50 | + pr_err("failed to get power domain status\n"); | |
| 51 | + | |
| 52 | + return is_off; | |
| 53 | +} | |
| 54 | + | |
| 55 | +static int meson_secure_pwrc_off(struct generic_pm_domain *domain) | |
| 56 | +{ | |
| 57 | + int ret = 0; | |
| 58 | + struct meson_secure_pwrc_domain *pwrc_domain = | |
| 59 | + container_of(domain, struct meson_secure_pwrc_domain, base); | |
| 60 | + | |
| 61 | + if (meson_sm_call(pwrc_domain->pwrc->fw, SM_A1_PWRC_SET, NULL, | |
| 62 | + pwrc_domain->index, PWRC_OFF, 0, 0, 0) < 0) { | |
| 63 | + pr_err("failed to set power domain off\n"); | |
| 64 | + ret = -EINVAL; | |
| 65 | + } | |
| 66 | + | |
| 67 | + return ret; | |
| 68 | +} | |
| 69 | + | |
| 70 | +static int meson_secure_pwrc_on(struct generic_pm_domain *domain) | |
| 71 | +{ | |
| 72 | + int ret = 0; | |
| 73 | + struct meson_secure_pwrc_domain *pwrc_domain = | |
| 74 | + container_of(domain, struct meson_secure_pwrc_domain, base); | |
| 75 | + | |
| 76 | + if (meson_sm_call(pwrc_domain->pwrc->fw, SM_A1_PWRC_SET, NULL, | |
| 77 | + pwrc_domain->index, PWRC_ON, 0, 0, 0) < 0) { | |
| 78 | + pr_err("failed to set power domain on\n"); | |
| 79 | + ret = -EINVAL; | |
| 80 | + } | |
| 81 | + | |
| 82 | + return ret; | |
| 83 | +} | |
| 84 | + | |
| 85 | +#define SEC_PD(__name, __flag) \ | |
| 86 | +[PWRC_##__name##_ID] = \ | |
| 87 | +{ \ | |
| 88 | + .name = #__name, \ | |
| 89 | + .index = PWRC_##__name##_ID, \ | |
| 90 | + .is_off = pwrc_secure_is_off, \ | |
| 91 | + .flags = __flag, \ | |
| 92 | +} | |
| 93 | + | |
| 94 | +static struct meson_secure_pwrc_domain_desc a1_pwrc_domains[] = { | |
| 95 | + SEC_PD(DSPA, 0), | |
| 96 | + SEC_PD(DSPB, 0), | |
| 97 | + /* UART should keep working in ATF after suspend and before resume */ | |
| 98 | + SEC_PD(UART, GENPD_FLAG_ALWAYS_ON), | |
| 99 | + /* DMC is for DDR PHY ana/dig and DMC, and should be always on */ | |
| 100 | + SEC_PD(DMC, GENPD_FLAG_ALWAYS_ON), | |
| 101 | + SEC_PD(I2C, 0), | |
| 102 | + SEC_PD(PSRAM, 0), | |
| 103 | + SEC_PD(ACODEC, 0), | |
| 104 | + SEC_PD(AUDIO, 0), | |
| 105 | + SEC_PD(OTP, 0), | |
| 106 | + SEC_PD(DMA, 0), | |
| 107 | + SEC_PD(SD_EMMC, 0), | |
| 108 | + SEC_PD(RAMA, 0), | |
| 109 | + /* SRAMB is used as ATF runtime memory, and should be always on */ | |
| 110 | + SEC_PD(RAMB, GENPD_FLAG_ALWAYS_ON), | |
| 111 | + SEC_PD(IR, 0), | |
| 112 | + SEC_PD(SPICC, 0), | |
| 113 | + SEC_PD(SPIFC, 0), | |
| 114 | + SEC_PD(USB, 0), | |
| 115 | + /* NIC is for the Arm NIC-400 interconnect, and should be always on */ | |
| 116 | + SEC_PD(NIC, GENPD_FLAG_ALWAYS_ON), | |
| 117 | + SEC_PD(PDMIN, 0), | |
| 118 | + SEC_PD(RSA, 0), | |
| 119 | +}; | |
| 120 | + | |
| 121 | +static int meson_secure_pwrc_probe(struct platform_device *pdev) | |
| 122 | +{ | |
| 123 | + int i; | |
| 124 | + struct device_node *sm_np; | |
| 125 | + struct meson_secure_pwrc *pwrc; | |
| 126 | + const struct meson_secure_pwrc_domain_data *match; | |
| 127 | + | |
| 128 | + match = of_device_get_match_data(&pdev->dev); | |
| 129 | + if (!match) { | |
| 130 | + dev_err(&pdev->dev, "failed to get match data\n"); | |
| 131 | + return -ENODEV; | |
| 132 | + } | |
| 133 | + | |
| 134 | + sm_np = of_find_compatible_node(NULL, NULL, "amlogic,meson-gxbb-sm"); | |
| 135 | + if (!sm_np) { | |
| 136 | + dev_err(&pdev->dev, "no secure-monitor node\n"); | |
| 137 | + return -ENODEV; | |
| 138 | + } | |
| 139 | + | |
| 140 | + pwrc = devm_kzalloc(&pdev->dev, sizeof(*pwrc), GFP_KERNEL); | |
| 141 | + if (!pwrc) | |
| 142 | + return -ENOMEM; | |
| 143 | + | |
| 144 | + pwrc->fw = meson_sm_get(sm_np); | |
| 145 | + of_node_put(sm_np); | |
| 146 | + if (!pwrc->fw) | |
| 147 | + return -EPROBE_DEFER; | |
| 148 | + | |
| 149 | + pwrc->xlate.domains = devm_kcalloc(&pdev->dev, match->count, | |
| 150 | + sizeof(*pwrc->xlate.domains), | |
| 151 | + GFP_KERNEL); | |
| 152 | + if (!pwrc->xlate.domains) | |
| 153 | + return -ENOMEM; | |
| 154 | + | |
| 155 | + pwrc->domains = devm_kcalloc(&pdev->dev, match->count, | |
| 156 | + sizeof(*pwrc->domains), GFP_KERNEL); | |
| 157 | + if (!pwrc->domains) | |
| 158 | + return -ENOMEM; | |
| 159 | + | |
| 160 | + pwrc->xlate.num_domains = match->count; | |
| 161 | + platform_set_drvdata(pdev, pwrc); | |
| 162 | + | |
| 163 | + for (i = 0 ; i < match->count ; ++i) { | |
| 164 | + struct meson_secure_pwrc_domain *dom = &pwrc->domains[i]; | |
| 165 | + | |
| 166 | + if (!match->domains[i].index) | |
| 167 | + continue; | |
| 168 | + | |
| 169 | + dom->pwrc = pwrc; | |
| 170 | + dom->index = match->domains[i].index; | |
| 171 | + dom->base.name = match->domains[i].name; | |
| 172 | + dom->base.flags = match->domains[i].flags; | |
| 173 | + dom->base.power_on = meson_secure_pwrc_on; | |
| 174 | + dom->base.power_off = meson_secure_pwrc_off; | |
| 175 | + | |
| 176 | + pm_genpd_init(&dom->base, NULL, match->domains[i].is_off(dom)); | |
| 177 | + | |
| 178 | + pwrc->xlate.domains[i] = &dom->base; | |
| 179 | + } | |
| 180 | + | |
| 181 | + return of_genpd_add_provider_onecell(pdev->dev.of_node, &pwrc->xlate); | |
| 182 | +} | |
| 183 | + | |
| 184 | +static struct meson_secure_pwrc_domain_data meson_secure_a1_pwrc_data = { | |
| 185 | + .domains = a1_pwrc_domains, | |
| 186 | + .count = ARRAY_SIZE(a1_pwrc_domains), | |
| 187 | +}; | |
| 188 | + | |
| 189 | +static const struct of_device_id meson_secure_pwrc_match_table[] = { | |
| 190 | + { | |
| 191 | + .compatible = "amlogic,meson-a1-pwrc", | |
| 192 | + .data = &meson_secure_a1_pwrc_data, | |
| 193 | + }, | |
| 194 | + { /* sentinel */ } | |
| 195 | +}; | |
| 196 | + | |
| 197 | +static struct platform_driver meson_secure_pwrc_driver = { | |
| 198 | + .probe = meson_secure_pwrc_probe, | |
| 199 | + .driver = { | |
| 200 | + .name = "meson_secure_pwrc", | |
| 201 | + .of_match_table = meson_secure_pwrc_match_table, | |
| 202 | + }, | |
| 203 | +}; | |
| 204 | +builtin_platform_driver(meson_secure_pwrc_driver); | |
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) |
| 2 | 2 | /* |
| 3 | 3 | * Copyright 2014-2016 Freescale Semiconductor Inc. |
| 4 | − * Copyright 2016 NXP | |
| 4 | + * Copyright 2016-2019 NXP | |
| 5 | 5 | * |
| 6 | 6 | */ |
| 7 | 7 | #include <linux/types.h> |
| @@ -432,6 +432,69 @@ int dpaa2_io_service_enqueue_fq(struct dpaa2_io *d, · 424 unmodified lines | ||
| 432 | 432 | } |
| 433 | 433 | EXPORT_SYMBOL(dpaa2_io_service_enqueue_fq); |
| 434 | 434 | |
| 435 | +/** | |
| 436 | + * dpaa2_io_service_enqueue_multiple_fq() - Enqueue multiple frames | |
| 437 | + * to a frame queue using one fqid. | |
| 438 | + * @d: the given DPIO service. | |
| 439 | + * @fqid: the given frame queue id. | |
| 440 | + * @fd: the frame descriptor which is enqueued. | |
| 441 | + * @nb: number of frames to be enqueud | |
| 442 | + * | |
| 443 | + * Return 0 for successful enqueue, -EBUSY if the enqueue ring is not ready, | |
| 444 | + * or -ENODEV if there is no dpio service. | |
| 445 | + */ | |
| 446 | +int dpaa2_io_service_enqueue_multiple_fq(struct dpaa2_io *d, | |
| 447 | + u32 fqid, | |
| 448 | + const struct dpaa2_fd *fd, | |
| 449 | + int nb) | |
| 450 | +{ | |
| 451 | + struct qbman_eq_desc ed; | |
| 452 | + | |
| 453 | + d = service_select(d); | |
| 454 | + if (!d) | |
| 455 | + return -ENODEV; | |
| 456 | + | |
| 457 | + qbman_eq_desc_clear(&ed); | |
| 458 | + qbman_eq_desc_set_no_orp(&ed, 0); | |
| 459 | + qbman_eq_desc_set_fq(&ed, fqid); | |
| 460 | + | |
| 461 | + return qbman_swp_enqueue_multiple(d->swp, &ed, fd, 0, nb); | |
| 462 | +} | |
| 463 | +EXPORT_SYMBOL(dpaa2_io_service_enqueue_multiple_fq); | |
| 464 | + | |
| 465 | +/** | |
| 466 | + * dpaa2_io_service_enqueue_multiple_desc_fq() - Enqueue multiple frames | |
| 467 | + * to different frame queue using a list of fqids. | |
| 468 | + * @d: the given DPIO service. | |
| 469 | + * @fqid: the given list of frame queue ids. | |
| 470 | + * @fd: the frame descriptor which is enqueued. | |
| 471 | + * @nb: number of frames to be enqueud | |
| 472 | + * | |
| 473 | + * Return 0 for successful enqueue, -EBUSY if the enqueue ring is not ready, | |
| 474 | + * or -ENODEV if there is no dpio service. | |
| 475 | + */ | |
| 476 | +int dpaa2_io_service_enqueue_multiple_desc_fq(struct dpaa2_io *d, | |
| 477 | + u32 *fqid, | |
| 478 | + const struct dpaa2_fd *fd, | |
| 479 | + int nb) | |
| 480 | +{ | |
| 481 | + int i; | |
| 482 | + struct qbman_eq_desc ed[32]; | |
| 483 | + | |
| 484 | + d = service_select(d); | |
| 485 | + if (!d) | |
| 486 | + return -ENODEV; | |
| 487 | + | |
| 488 | + for (i = 0; i < nb; i++) { | |
| 489 | + qbman_eq_desc_clear(&ed[i]); | |
| 490 | + qbman_eq_desc_set_no_orp(&ed[i], 0); | |
| 491 | + qbman_eq_desc_set_fq(&ed[i], fqid[i]); | |
| 492 | + } | |
| 493 | + | |
| 494 | + return qbman_swp_enqueue_multiple_desc(d->swp, &ed[0], fd, nb); | |
| 495 | +} | |
| 496 | +EXPORT_SYMBOL(dpaa2_io_service_enqueue_multiple_desc_fq); | |
| 497 | + | |
| 435 | 498 | /** |
| 436 | 499 | * dpaa2_io_service_enqueue_qd() - Enqueue a frame to a QD. |
| 437 | 500 | * @d: the given DPIO service. |
| @@ -526,7 +589,7 @@ EXPORT_SYMBOL_GPL(dpaa2_io_service_acquire); · 88 unmodified lines | ||
| 526 | 589 | |
| 527 | 590 | /** |
| 528 | 591 | * dpaa2_io_store_create() - Create the dma memory storage for dequeue result. |
| 529 | − * @max_frames: the maximum number of dequeued result for frames, must be <= 16. | |
| 592 | + * @max_frames: the maximum number of dequeued result for frames, must be <= 32. | |
| 530 | 593 | * @dev: the device to allow mapping/unmapping the DMAable region. |
| 531 | 594 | * |
| 532 | 595 | * The size of the storage is "max_frames*sizeof(struct dpaa2_dq)". |
| @@ -541,7 +604,7 @@ struct dpaa2_io_store *dpaa2_io_store_create(unsigned int max_frames, · 8 unmodified lines | ||
| 541 | 604 | struct dpaa2_io_store *ret; |
| 542 | 605 | size_t size; |
| 543 | 606 | |
| 544 | − if (!max_frames || (max_frames > 16)) | |
| 607 | + if (!max_frames || (max_frames > 32)) | |
| 545 | 608 | return NULL; |
| 546 | 609 | |
| 547 | 610 | ret = kmalloc(sizeof(*ret), GFP_KERNEL); |
| @@ -1,24 +1,18 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) |
| 2 | 2 | /* |
| 3 | 3 | * Copyright (C) 2014-2016 Freescale Semiconductor, Inc. |
| 4 | − * Copyright 2016 NXP | |
| 4 | + * Copyright 2016-2019 NXP | |
| 5 | 5 | * |
| 6 | 6 | */ |
| 7 | 7 | |
| 8 | 8 | #include <asm/cacheflush.h> |
| 9 | 9 | #include <linux/io.h> |
| 10 | 10 | #include <linux/slab.h> |
| 11 | +#include <linux/spinlock.h> | |
| 11 | 12 | #include <soc/fsl/dpaa2-global.h> |
| 12 | 13 | |
| 13 | 14 | #include "qbman-portal.h" |
| 14 | 15 | |
| 15 | −#define QMAN_REV_4000 0x04000000 | |
| 16 | −#define QMAN_REV_4100 0x04010000 | |
| 17 | −#define QMAN_REV_4101 0x04010001 | |
| 18 | −#define QMAN_REV_5000 0x05000000 | |
| 19 | − | |
| 20 | −#define QMAN_REV_MASK 0xffff0000 | |
| 21 | − | |
| 22 | 16 | /* All QBMan command and result structures use this "valid bit" encoding */ |
| 23 | 17 | #define QB_VALID_BIT ((u32)0x80) |
| 24 | 18 | |
| @@ -28,6 +22,7 @@ · 3 unmodified lines | ||
| 28 | 22 | |
| 29 | 23 | /* CINH register offsets */ |
| 30 | 24 | #define QBMAN_CINH_SWP_EQCR_PI 0x800 |
| 25 | +#define QBMAN_CINH_SWP_EQCR_CI 0x840 | |
| 31 | 26 | #define QBMAN_CINH_SWP_EQAR 0x8c0 |
| 32 | 27 | #define QBMAN_CINH_SWP_CR_RT 0x900 |
| 33 | 28 | #define QBMAN_CINH_SWP_VDQCR_RT 0x940 |
| @@ -51,6 +46,8 @@ · 17 unmodified lines | ||
| 51 | 46 | #define QBMAN_CENA_SWP_CR 0x600 |
| 52 | 47 | #define QBMAN_CENA_SWP_RR(vb) (0x700 + ((u32)(vb) >> 1)) |
| 53 | 48 | #define QBMAN_CENA_SWP_VDQCR 0x780 |
| 49 | +#define QBMAN_CENA_SWP_EQCR_CI 0x840 | |
| 50 | +#define QBMAN_CENA_SWP_EQCR_CI_MEMBACK 0x1840 | |
| 54 | 51 | |
| 55 | 52 | /* CENA register offsets in memory-backed mode */ |
| 56 | 53 | #define QBMAN_CENA_SWP_DQRR_MEM(n) (0x800 + ((u32)(n) << 6)) |
| @@ -78,6 +75,12 @@ · 21 unmodified lines | ||
| 78 | 75 | /* opaque token for static dequeues */ |
| 79 | 76 | #define QMAN_SDQCR_TOKEN 0xbb |
| 80 | 77 | |
| 78 | +#define QBMAN_EQCR_DCA_IDXMASK 0x0f | |
| 79 | +#define QBMAN_ENQUEUE_FLAG_DCA (1ULL << 31) | |
| 80 | + | |
| 81 | +#define EQ_DESC_SIZE_WITHOUT_FD 29 | |
| 82 | +#define EQ_DESC_SIZE_FD_START 32 | |
| 83 | + | |
| 81 | 84 | enum qbman_sdqcr_dct { |
| 82 | 85 | qbman_sdqcr_dct_null = 0, |
| 83 | 86 | qbman_sdqcr_dct_prio_ics, |
| @@ -90,6 +93,82 @@ enum qbman_sdqcr_fc { · 6 unmodified lines | ||
| 90 | 93 | qbman_sdqcr_fc_up_to_3 = 1 |
| 91 | 94 | }; |
| 92 | 95 | |
| 96 | +/* Internal Function declaration */ | |
| 97 | +static int qbman_swp_enqueue_direct(struct qbman_swp *s, | |
| 98 | + const struct qbman_eq_desc *d, | |
| 99 | + const struct dpaa2_fd *fd); | |
| 100 | +static int qbman_swp_enqueue_mem_back(struct qbman_swp *s, | |
| 101 | + const struct qbman_eq_desc *d, | |
| 102 | + const struct dpaa2_fd *fd); | |
| 103 | +static int qbman_swp_enqueue_multiple_direct(struct qbman_swp *s, | |
| 104 | + const struct qbman_eq_desc *d, | |
| 105 | + const struct dpaa2_fd *fd, | |
| 106 | + uint32_t *flags, | |
| 107 | + int num_frames); | |
| 108 | +static int qbman_swp_enqueue_multiple_mem_back(struct qbman_swp *s, | |
| 109 | + const struct qbman_eq_desc *d, | |
| 110 | + const struct dpaa2_fd *fd, | |
| 111 | + uint32_t *flags, | |
| 112 | + int num_frames); | |
| 113 | +static int | |
| 114 | +qbman_swp_enqueue_multiple_desc_direct(struct qbman_swp *s, | |
| 115 | + const struct qbman_eq_desc *d, | |
| 116 | + const struct dpaa2_fd *fd, | |
| 117 | + int num_frames); | |
| 118 | +static | |
| 119 | +int qbman_swp_enqueue_multiple_desc_mem_back(struct qbman_swp *s, | |
| 120 | + const struct qbman_eq_desc *d, | |
| 121 | + const struct dpaa2_fd *fd, | |
| 122 | + int num_frames); | |
| 123 | +static int qbman_swp_pull_direct(struct qbman_swp *s, | |
| 124 | + struct qbman_pull_desc *d); | |
| 125 | +static int qbman_swp_pull_mem_back(struct qbman_swp *s, | |
| 126 | + struct qbman_pull_desc *d); | |
| 127 | + | |
| 128 | +const struct dpaa2_dq *qbman_swp_dqrr_next_direct(struct qbman_swp *s); | |
| 129 | +const struct dpaa2_dq *qbman_swp_dqrr_next_mem_back(struct qbman_swp *s); | |
| 130 | + | |
| 131 | +static int qbman_swp_release_direct(struct qbman_swp *s, | |
| 132 | + const struct qbman_release_desc *d, | |
| 133 | + const u64 *buffers, | |
| 134 | + unsigned int num_buffers); | |
| 135 | +static int qbman_swp_release_mem_back(struct qbman_swp *s, | |
| 136 | + const struct qbman_release_desc *d, | |
| 137 | + const u64 *buffers, | |
| 138 | + unsigned int num_buffers); | |
| 139 | + | |
| 140 | +/* Function pointers */ | |
| 141 | +int (*qbman_swp_enqueue_ptr)(struct qbman_swp *s, | |
| 142 | + const struct qbman_eq_desc *d, | |
| 143 | + const struct dpaa2_fd *fd) | |
| 144 | + = qbman_swp_enqueue_direct; | |
| 145 | + | |
| 146 | +int (*qbman_swp_enqueue_multiple_ptr)(struct qbman_swp *s, | |
| 147 | + const struct qbman_eq_desc *d, | |
| 148 | + const struct dpaa2_fd *fd, | |
| 149 | + uint32_t *flags, | |
| 150 | + int num_frames) | |
| 151 | + = qbman_swp_enqueue_multiple_direct; | |
| 152 | + | |
| 153 | +int | |
| 154 | +(*qbman_swp_enqueue_multiple_desc_ptr)(struct qbman_swp *s, | |
| 155 | + const struct qbman_eq_desc *d, | |
| 156 | + const struct dpaa2_fd *fd, | |
| 157 | + int num_frames) | |
| 158 | + = qbman_swp_enqueue_multiple_desc_direct; | |
| 159 | + | |
| 160 | +int (*qbman_swp_pull_ptr)(struct qbman_swp *s, struct qbman_pull_desc *d) | |
| 161 | + = qbman_swp_pull_direct; | |
| 162 | + | |
| 163 | +const struct dpaa2_dq *(*qbman_swp_dqrr_next_ptr)(struct qbman_swp *s) | |
| 164 | + = qbman_swp_dqrr_next_direct; | |
| 165 | + | |
| 166 | +int (*qbman_swp_release_ptr)(struct qbman_swp *s, | |
| 167 | + const struct qbman_release_desc *d, | |
| 168 | + const u64 *buffers, | |
| 169 | + unsigned int num_buffers) | |
| 170 | + = qbman_swp_release_direct; | |
| 171 | + | |
| 93 | 172 | /* Portal Access */ |
| 94 | 173 | |
| 95 | 174 | static inline u32 qbman_read_register(struct qbman_swp *p, u32 offset) |
| @@ -146,6 +225,15 @@ static inline u32 qbman_set_swp_cfg(u8 max_fill, u8 wn, u8 est, u8 rpm, u8 dcm, · 50 unmodified lines | ||
| 146 | 225 | |
| 147 | 226 | #define QMAN_RT_MODE 0x00000100 |
| 148 | 227 | |
| 228 | +static inline u8 qm_cyc_diff(u8 ringsize, u8 first, u8 last) | |
| 229 | +{ | |
| 230 | + /* 'first' is included, 'last' is excluded */ | |
| 231 | + if (first <= last) | |
| 232 | + return last - first; | |
| 233 | + else | |
| 234 | + return (2 * ringsize) - (first - last); | |
| 235 | +} | |
| 236 | + | |
| 149 | 237 | /** |
| 150 | 238 | * qbman_swp_init() - Create a functional object representing the given |
| 151 | 239 | * QBMan portal descriptor. |
| @@ -156,11 +244,16 @@ static inline u32 qbman_set_swp_cfg(u8 max_fill, u8 wn, u8 est, u8 rpm, u8 dcm, · 4 unmodified lines | ||
| 156 | 244 | */ |
| 157 | 245 | struct qbman_swp *qbman_swp_init(const struct qbman_swp_desc *d) |
| 158 | 246 | { |
| 159 | − struct qbman_swp *p = kmalloc(sizeof(*p), GFP_KERNEL); | |
| 247 | + struct qbman_swp *p = kzalloc(sizeof(*p), GFP_KERNEL); | |
| 160 | 248 | u32 reg; |
| 249 | + u32 mask_size; | |
| 250 | + u32 eqcr_pi; | |
| 161 | 251 | |
| 162 | 252 | if (!p) |
| 163 | 253 | return NULL; |
| 254 | + | |
| 255 | + spin_lock_init(&p->access_spinlock); | |
| 256 | + | |
| 164 | 257 | p->desc = d; |
| 165 | 258 | p->mc.valid_bit = QB_VALID_BIT; |
| 166 | 259 | p->sdq = 0; |
| @@ -186,25 +279,38 @@ struct qbman_swp *qbman_swp_init(const struct qbman_swp_desc *d) · 19 unmodified lines | ||
| 186 | 279 | p->addr_cena = d->cena_bar; |
| 187 | 280 | p->addr_cinh = d->cinh_bar; |
| 188 | 281 | |
| 189 | − if ((p->desc->qman_version & QMAN_REV_MASK) >= QMAN_REV_5000) | |
| 190 | − memset(p->addr_cena, 0, 64 * 1024); | |
| 282 | + if ((p->desc->qman_version & QMAN_REV_MASK) < QMAN_REV_5000) { | |
| 191 | 283 | |
| 192 | − reg = qbman_set_swp_cfg(p->dqrr.dqrr_size, | |
| 193 | − 1, /* Writes Non-cacheable */ | |
| 194 | − 0, /* EQCR_CI stashing threshold */ | |
| 195 | − 3, /* RPM: Valid bit mode, RCR in array mode */ | |
| 196 | − 2, /* DCM: Discrete consumption ack mode */ | |
| 197 | − 3, /* EPM: Valid bit mode, EQCR in array mode */ | |
| 198 | − 1, /* mem stashing drop enable == TRUE */ | |
| 199 | − 1, /* mem stashing priority == TRUE */ | |
| 200 | − 1, /* mem stashing enable == TRUE */ | |
| 201 | − 1, /* dequeue stashing priority == TRUE */ | |
| 202 | − 0, /* dequeue stashing enable == FALSE */ | |
| 203 | − 0); /* EQCR_CI stashing priority == FALSE */ | |
| 204 | − if ((p->desc->qman_version & QMAN_REV_MASK) >= QMAN_REV_5000) | |
| 284 | + reg = qbman_set_swp_cfg(p->dqrr.dqrr_size, | |
| 285 | + 1, /* Writes Non-cacheable */ | |
| 286 | + 0, /* EQCR_CI stashing threshold */ | |
| 287 | + 3, /* RPM: RCR in array mode */ | |
| 288 | + 2, /* DCM: Discrete consumption ack */ | |
| 289 | + 2, /* EPM: EQCR in ring mode */ | |
| 290 | + 1, /* mem stashing drop enable enable */ | |
| 291 | + 1, /* mem stashing priority enable */ | |
| 292 | + 1, /* mem stashing enable */ | |
| 293 | + 1, /* dequeue stashing priority enable */ | |
| 294 | + 0, /* dequeue stashing enable enable */ | |
| 295 | + 0); /* EQCR_CI stashing priority enable */ | |
| 296 | + } else { | |
| 297 | + memset(p->addr_cena, 0, 64 * 1024); | |
| 298 | + reg = qbman_set_swp_cfg(p->dqrr.dqrr_size, | |
| 299 | + 1, /* Writes Non-cacheable */ | |
| 300 | + 1, /* EQCR_CI stashing threshold */ | |
| 301 | + 3, /* RPM: RCR in array mode */ | |
| 302 | + 2, /* DCM: Discrete consumption ack */ | |
| 303 | + 0, /* EPM: EQCR in ring mode */ | |
| 304 | + 1, /* mem stashing drop enable */ | |
| 305 | + 1, /* mem stashing priority enable */ | |
| 306 | + 1, /* mem stashing enable */ | |
| 307 | + 1, /* dequeue stashing priority enable */ | |
| 308 | + 0, /* dequeue stashing enable */ | |
| 309 | + 0); /* EQCR_CI stashing priority enable */ | |
| 205 | 310 | reg |= 1 << SWP_CFG_CPBS_SHIFT | /* memory-backed mode */ |
| 206 | 311 | 1 << SWP_CFG_VPM_SHIFT | /* VDQCR read triggered mode */ |
| 207 | 312 | 1 << SWP_CFG_CPM_SHIFT; /* CR read triggered mode */ |
| 313 | + } | |
| 208 | 314 | |
| 209 | 315 | qbman_write_register(p, QBMAN_CINH_SWP_CFG, reg); |
| 210 | 316 | reg = qbman_read_register(p, QBMAN_CINH_SWP_CFG); |
| @@ -225,6 +331,30 @@ struct qbman_swp *qbman_swp_init(const struct qbman_swp_desc *d) · 14 unmodified lines | ||
| 225 | 331 | * applied when dequeues from a specific channel are enabled. |
| 226 | 332 | */ |
| 227 | 333 | qbman_write_register(p, QBMAN_CINH_SWP_SDQCR, 0); |
| 334 | + | |
| 335 | + p->eqcr.pi_ring_size = 8; | |
| 336 | + if ((p->desc->qman_version & QMAN_REV_MASK) >= QMAN_REV_5000) { | |
| 337 | + p->eqcr.pi_ring_size = 32; | |
| 338 | + qbman_swp_enqueue_ptr = | |
| 339 | + qbman_swp_enqueue_mem_back; | |
| 340 | + qbman_swp_enqueue_multiple_ptr = | |
| 341 | + qbman_swp_enqueue_multiple_mem_back; | |
| 342 | + qbman_swp_enqueue_multiple_desc_ptr = | |
| 343 | + qbman_swp_enqueue_multiple_desc_mem_back; | |
| 344 | + qbman_swp_pull_ptr = qbman_swp_pull_mem_back; | |
| 345 | + qbman_swp_dqrr_next_ptr = qbman_swp_dqrr_next_mem_back; | |
| 346 | + qbman_swp_release_ptr = qbman_swp_release_mem_back; | |
| 347 | + } | |
| 348 | + | |
| 349 | + for (mask_size = p->eqcr.pi_ring_size; mask_size > 0; mask_size >>= 1) | |
| 350 | + p->eqcr.pi_ci_mask = (p->eqcr.pi_ci_mask << 1) + 1; | |
| 351 | + eqcr_pi = qbman_read_register(p, QBMAN_CINH_SWP_EQCR_PI); | |
| 352 | + p->eqcr.pi = eqcr_pi & p->eqcr.pi_ci_mask; | |
| 353 | + p->eqcr.pi_vb = eqcr_pi & QB_VALID_BIT; | |
| 354 | + p->eqcr.ci = qbman_read_register(p, QBMAN_CINH_SWP_EQCR_CI) | |
| 355 | + & p->eqcr.pi_ci_mask; | |
| 356 | + p->eqcr.available = p->eqcr.pi_ring_size; | |
| 357 | + | |
| 228 | 358 | return p; |
| 229 | 359 | } |
| 230 | 360 | |
| @@ -378,6 +508,7 @@ enum qb_enqueue_commands { · 147 unmodified lines | ||
| 378 | 508 | #define QB_ENQUEUE_CMD_ORP_ENABLE_SHIFT 2 |
| 379 | 509 | #define QB_ENQUEUE_CMD_IRQ_ON_DISPATCH_SHIFT 3 |
| 380 | 510 | #define QB_ENQUEUE_CMD_TARGET_TYPE_SHIFT 4 |
| 511 | +#define QB_ENQUEUE_CMD_DCA_EN_SHIFT 7 | |
| 381 | 512 | |
| 382 | 513 | /** |
| 383 | 514 | * qbman_eq_desc_clear() - Clear the contents of a descriptor to |
| @@ -453,8 +584,9 @@ static inline void qbman_write_eqcr_am_rt_register(struct qbman_swp *p, · 69 unmodified lines | ||
| 453 | 584 | QMAN_RT_MODE); |
| 454 | 585 | } |
| 455 | 586 | |
| 587 | +#define QB_RT_BIT ((u32)0x100) | |
| 456 | 588 | /** |
| 457 | − * qbman_swp_enqueue() - Issue an enqueue command | |
| 589 | + * qbman_swp_enqueue_direct() - Issue an enqueue command | |
| 458 | 590 | * @s: the software portal used for enqueue |
| 459 | 591 | * @d: the enqueue descriptor |
| 460 | 592 | * @fd: the frame descriptor to be enqueued |
| @@ -464,30 +596,351 @@ static inline void qbman_write_eqcr_am_rt_register(struct qbman_swp *p, · 3 unmodified lines | ||
| 464 | 596 | * |
| 465 | 597 | * Return 0 for successful enqueue, -EBUSY if the EQCR is not ready. |
| 466 | 598 | */ |
| 467 | −int qbman_swp_enqueue(struct qbman_swp *s, const struct qbman_eq_desc *d, | |
| 468 | − const struct dpaa2_fd *fd) | |
| 599 | +static | |
| 600 | +int qbman_swp_enqueue_direct(struct qbman_swp *s, | |
| 601 | + const struct qbman_eq_desc *d, | |
| 602 | + const struct dpaa2_fd *fd) | |
| 469 | 603 | { |
| 470 | − struct qbman_eq_desc *p; | |
| 471 | − u32 eqar = qbman_read_register(s, QBMAN_CINH_SWP_EQAR); | |
| 604 | + int flags = 0; | |
| 605 | + int ret = qbman_swp_enqueue_multiple_direct(s, d, fd, &flags, 1); | |
| 472 | 606 | |
| 473 | − if (!EQAR_SUCCESS(eqar)) | |
| 474 | − return -EBUSY; | |
| 607 | + if (ret >= 0) | |
| 608 | + ret = 0; | |
| 609 | + else | |
| 610 | + ret = -EBUSY; | |
| 611 | + return ret; | |
| 612 | +} | |
| 613 | + | |
| 614 | +/** | |
| 615 | + * qbman_swp_enqueue_mem_back() - Issue an enqueue command | |
| 616 | + * @s: the software portal used for enqueue | |
| 617 | + * @d: the enqueue descriptor | |
| 618 | + * @fd: the frame descriptor to be enqueued | |
| 619 | + * | |
| 620 | + * Please note that 'fd' should only be NULL if the "action" of the | |
| 621 | + * descriptor is "orp_hole" or "orp_nesn". | |
| 622 | + * | |
| 623 | + * Return 0 for successful enqueue, -EBUSY if the EQCR is not ready. | |
| 624 | + */ | |
| 625 | +static | |
| 626 | +int qbman_swp_enqueue_mem_back(struct qbman_swp *s, | |
| 627 | + const struct qbman_eq_desc *d, | |
| 628 | + const struct dpaa2_fd *fd) | |
| 629 | +{ | |
| 630 | + int flags = 0; | |
| 631 | + int ret = qbman_swp_enqueue_multiple_mem_back(s, d, fd, &flags, 1); | |
| 475 | 632 | |
| 476 | − p = qbman_get_cmd(s, QBMAN_CENA_SWP_EQCR(EQAR_IDX(eqar))); | |
| 477 | − memcpy(&p->dca, &d->dca, 31); | |
| 478 | − memcpy(&p->fd, fd, sizeof(*fd)); | |
| 633 | + if (ret >= 0) | |
| 634 | + ret = 0; | |
| 635 | + else | |
| 636 | + ret = -EBUSY; | |
| 637 | + return ret; | |
| 638 | +} | |
| 479 | 639 | |
| 480 | − if ((s->desc->qman_version & QMAN_REV_MASK) < QMAN_REV_5000) { | |
| 481 | − /* Set the verb byte, have to substitute in the valid-bit */ | |
| 482 | − dma_wmb(); | |
| 483 | − p->verb = d->verb | EQAR_VB(eqar); | |
| 484 | − } else { | |
| 485 | − p->verb = d->verb | EQAR_VB(eqar); | |
| 486 | − dma_wmb(); | |
| 487 | − qbman_write_eqcr_am_rt_register(s, EQAR_IDX(eqar)); | |
| 640 | +/** | |
| 641 | + * qbman_swp_enqueue_multiple_direct() - Issue a multi enqueue command | |
| 642 | + * using one enqueue descriptor | |
| 643 | + * @s: the software portal used for enqueue | |
| 644 | + * @d: the enqueue descriptor | |
| 645 | + * @fd: table pointer of frame descriptor table to be enqueued | |
| 646 | + * @flags: table pointer of QBMAN_ENQUEUE_FLAG_DCA flags, not used if NULL | |
| 647 | + * @num_frames: number of fd to be enqueued | |
| 648 | + * | |
| 649 | + * Return the number of fd enqueued, or a negative error number. | |
| 650 | + */ | |
| 651 | +static | |
| 652 | +int qbman_swp_enqueue_multiple_direct(struct qbman_swp *s, | |
| 653 | + const struct qbman_eq_desc *d, | |
| 654 | + const struct dpaa2_fd *fd, | |
| 655 | + uint32_t *flags, | |
| 656 | + int num_frames) | |
| 657 | +{ | |
| 658 | + uint32_t *p = NULL; | |
| 659 | + const uint32_t *cl = (uint32_t *)d; | |
| 660 | + uint32_t eqcr_ci, eqcr_pi, half_mask, full_mask; | |
| 661 | + int i, num_enqueued = 0; | |
| 662 | + uint64_t addr_cena; | |
| 663 | + | |
| 664 | + spin_lock(&s->access_spinlock); | |
| 665 | + half_mask = (s->eqcr.pi_ci_mask>>1); | |
| 666 | + full_mask = s->eqcr.pi_ci_mask; | |
| 667 | + | |
| 668 | + if (!s->eqcr.available) { | |
| 669 | + eqcr_ci = s->eqcr.ci; | |
| 670 | + p = s->addr_cena + QBMAN_CENA_SWP_EQCR_CI; | |
| 671 | + s->eqcr.ci = qbman_read_register(s, QBMAN_CINH_SWP_EQCR_CI); | |
| 672 | + | |
| 673 | + s->eqcr.available = qm_cyc_diff(s->eqcr.pi_ring_size, | |
| 674 | + eqcr_ci, s->eqcr.ci); | |
| 675 | + if (!s->eqcr.available) { | |
| 676 | + spin_unlock(&s->access_spinlock); | |
| 677 | + return 0; | |
| 678 | + } | |
| 488 | 679 | } |
| 489 | 680 | |
| 490 | − return 0; | |
| 681 | + eqcr_pi = s->eqcr.pi; | |
| 682 | + num_enqueued = (s->eqcr.available < num_frames) ? | |
| 683 | + s->eqcr.available : num_frames; | |
| 684 | + s->eqcr.available -= num_enqueued; | |
| 685 | + /* Fill in the EQCR ring */ | |
| 686 | + for (i = 0; i < num_enqueued; i++) { | |
| 687 | + p = (s->addr_cena + QBMAN_CENA_SWP_EQCR(eqcr_pi & half_mask)); | |
| 688 | + /* Skip copying the verb */ | |
| 689 | + memcpy(&p[1], &cl[1], EQ_DESC_SIZE_WITHOUT_FD - 1); | |
| 690 | + memcpy(&p[EQ_DESC_SIZE_FD_START/sizeof(uint32_t)], | |
| 691 | + &fd[i], sizeof(*fd)); | |
| 692 | + eqcr_pi++; | |
| 693 | + } | |
| 694 | + | |
| 695 | + dma_wmb(); | |
| 696 | + | |
| 697 | + /* Set the verb byte, have to substitute in the valid-bit */ | |
| 698 | + eqcr_pi = s->eqcr.pi; | |
| 699 | + for (i = 0; i < num_enqueued; i++) { | |
| 700 | + p = (s->addr_cena + QBMAN_CENA_SWP_EQCR(eqcr_pi & half_mask)); | |
| 701 | + p[0] = cl[0] | s->eqcr.pi_vb; | |
| 702 | + if (flags && (flags[i] & QBMAN_ENQUEUE_FLAG_DCA)) { | |
| 703 | + struct qbman_eq_desc *d = (struct qbman_eq_desc *)p; | |
| 704 | + | |
| 705 | + d->dca = (1 << QB_ENQUEUE_CMD_DCA_EN_SHIFT) | | |
| 706 | + ((flags[i]) & QBMAN_EQCR_DCA_IDXMASK); | |
| 707 | + } | |
| 708 | + eqcr_pi++; | |
| 709 | + if (!(eqcr_pi & half_mask)) | |
| 710 | + s->eqcr.pi_vb ^= QB_VALID_BIT; | |
| 711 | + } | |
| 712 | + | |
| 713 | + /* Flush all the cacheline without load/store in between */ | |
| 714 | + eqcr_pi = s->eqcr.pi; | |
| 715 | + addr_cena = (size_t)s->addr_cena; | |
| 716 | + for (i = 0; i < num_enqueued; i++) | |
| 717 | + eqcr_pi++; | |
| 718 | + s->eqcr.pi = eqcr_pi & full_mask; | |
| 719 | + spin_unlock(&s->access_spinlock); | |
| 720 | + | |
| 721 | + return num_enqueued; | |
| 722 | +} | |
| 723 | + | |
| 724 | +/** | |
| 725 | + * qbman_swp_enqueue_multiple_mem_back() - Issue a multi enqueue command | |
| 726 | + * using one enqueue descriptor | |
| 727 | + * @s: the software portal used for enqueue | |
| 728 | + * @d: the enqueue descriptor | |
| 729 | + * @fd: table pointer of frame descriptor table to be enqueued | |
| 730 | + * @flags: table pointer of QBMAN_ENQUEUE_FLAG_DCA flags, not used if NULL | |
| 731 | + * @num_frames: number of fd to be enqueued | |
| 732 | + * | |
| 733 | + * Return the number of fd enqueued, or a negative error number. | |
| 734 | + */ | |
| 735 | +static | |
| 736 | +int qbman_swp_enqueue_multiple_mem_back(struct qbman_swp *s, | |
| 737 | + const struct qbman_eq_desc *d, | |
| 738 | + const struct dpaa2_fd *fd, | |
| 739 | + uint32_t *flags, | |
| 740 | + int num_frames) | |
| 741 | +{ | |
| 742 | + uint32_t *p = NULL; | |
| 743 | + const uint32_t *cl = (uint32_t *)(d); | |
| 744 | + uint32_t eqcr_ci, eqcr_pi, half_mask, full_mask; | |
| 745 | + int i, num_enqueued = 0; | |
| 746 | + unsigned long irq_flags; | |
| 747 | + | |
| 748 | + spin_lock(&s->access_spinlock); | |
| 749 | + local_irq_save(irq_flags); | |
| 750 | + | |
| 751 | + half_mask = (s->eqcr.pi_ci_mask>>1); | |
| 752 | + full_mask = s->eqcr.pi_ci_mask; | |
| 753 | + if (!s->eqcr.available) { | |
| 754 | + eqcr_ci = s->eqcr.ci; | |
| 755 | + p = s->addr_cena + QBMAN_CENA_SWP_EQCR_CI_MEMBACK; | |
| 756 | + s->eqcr.ci = __raw_readl(p) & full_mask; | |
| 757 | + s->eqcr.available = qm_cyc_diff(s->eqcr.pi_ring_size, | |
| 758 | + eqcr_ci, s->eqcr.ci); | |
| 759 | + if (!s->eqcr.available) { | |
| 760 | + local_irq_restore(irq_flags); | |
| 761 | + spin_unlock(&s->access_spinlock); | |
| 762 | + return 0; | |
| 763 | + } | |
| 764 | + } | |
| 765 | + | |
| 766 | + eqcr_pi = s->eqcr.pi; | |
| 767 | + num_enqueued = (s->eqcr.available < num_frames) ? | |
| 768 | + s->eqcr.available : num_frames; | |
| 769 | + s->eqcr.available -= num_enqueued; | |
| 770 | + /* Fill in the EQCR ring */ | |
| 771 | + for (i = 0; i < num_enqueued; i++) { | |
| 772 | + p = (s->addr_cena + QBMAN_CENA_SWP_EQCR(eqcr_pi & half_mask)); | |
| 773 | + /* Skip copying the verb */ | |
| 774 | + memcpy(&p[1], &cl[1], EQ_DESC_SIZE_WITHOUT_FD - 1); | |
| 775 | + memcpy(&p[EQ_DESC_SIZE_FD_START/sizeof(uint32_t)], | |
| 776 | + &fd[i], sizeof(*fd)); | |
| 777 | + eqcr_pi++; | |
| 778 | + } | |
| 779 | + | |
| 780 | + /* Set the verb byte, have to substitute in the valid-bit */ | |
| 781 | + eqcr_pi = s->eqcr.pi; | |
| 782 | + for (i = 0; i < num_enqueued; i++) { | |
| 783 | + p = (s->addr_cena + QBMAN_CENA_SWP_EQCR(eqcr_pi & half_mask)); | |
| 784 | + p[0] = cl[0] | s->eqcr.pi_vb; | |
| 785 | + if (flags && (flags[i] & QBMAN_ENQUEUE_FLAG_DCA)) { | |
| 786 | + struct qbman_eq_desc *d = (struct qbman_eq_desc *)p; | |
| 787 | + | |
| 788 | + d->dca = (1 << QB_ENQUEUE_CMD_DCA_EN_SHIFT) | | |
| 789 | + ((flags[i]) & QBMAN_EQCR_DCA_IDXMASK); | |
| 790 | + } | |
| 791 | + eqcr_pi++; | |
| 792 | + if (!(eqcr_pi & half_mask)) | |
| 793 | + s->eqcr.pi_vb ^= QB_VALID_BIT; | |
| 794 | + } | |
| 795 | + s->eqcr.pi = eqcr_pi & full_mask; | |
| 796 | + | |
| 797 | + dma_wmb(); | |
| 798 | + qbman_write_register(s, QBMAN_CINH_SWP_EQCR_PI, | |
| 799 | + (QB_RT_BIT)|(s->eqcr.pi)|s->eqcr.pi_vb); | |
| 800 | + local_irq_restore(irq_flags); | |
| 801 | + spin_unlock(&s->access_spinlock); | |
| 802 | + | |
| 803 | + return num_enqueued; | |
| 804 | +} | |
| 805 | + | |
| 806 | +/** | |
| 807 | + * qbman_swp_enqueue_multiple_desc_direct() - Issue a multi enqueue command | |
| 808 | + * using multiple enqueue descriptor | |
| 809 | + * @s: the software portal used for enqueue | |
| 810 | + * @d: table of minimal enqueue descriptor | |
| 811 | + * @fd: table pointer of frame descriptor table to be enqueued | |
| 812 | + * @num_frames: number of fd to be enqueued | |
| 813 | + * | |
| 814 | + * Return the number of fd enqueued, or a negative error number. | |
| 815 | + */ | |
| 816 | +static | |
| 817 | +int qbman_swp_enqueue_multiple_desc_direct(struct qbman_swp *s, | |
| 818 | + const struct qbman_eq_desc *d, | |
| 819 | + const struct dpaa2_fd *fd, | |
| 820 | + int num_frames) | |
| 821 | +{ | |
| 822 | + uint32_t *p; | |
| 823 | + const uint32_t *cl; | |
| 824 | + uint32_t eqcr_ci, eqcr_pi, half_mask, full_mask; | |
| 825 | + int i, num_enqueued = 0; | |
| 826 | + uint64_t addr_cena; | |
| 827 | + | |
| 828 | + half_mask = (s->eqcr.pi_ci_mask>>1); | |
| 829 | + full_mask = s->eqcr.pi_ci_mask; | |
| 830 | + if (!s->eqcr.available) { | |
| 831 | + eqcr_ci = s->eqcr.ci; | |
| 832 | + p = s->addr_cena + QBMAN_CENA_SWP_EQCR_CI; | |
| 833 | + s->eqcr.ci = qbman_read_register(s, QBMAN_CINH_SWP_EQCR_CI); | |
| 834 | + s->eqcr.available = qm_cyc_diff(s->eqcr.pi_ring_size, | |
| 835 | + eqcr_ci, s->eqcr.ci); | |
| 836 | + if (!s->eqcr.available) | |
| 837 | + return 0; | |
| 838 | + } | |
| 839 | + | |
| 840 | + eqcr_pi = s->eqcr.pi; | |
| 841 | + num_enqueued = (s->eqcr.available < num_frames) ? | |
| 842 | + s->eqcr.available : num_frames; | |
| 843 | + s->eqcr.available -= num_enqueued; | |
| 844 | + /* Fill in the EQCR ring */ | |
| 845 | + for (i = 0; i < num_enqueued; i++) { | |
| 846 | + p = (s->addr_cena + QBMAN_CENA_SWP_EQCR(eqcr_pi & half_mask)); | |
| 847 | + cl = (uint32_t *)(&d[i]); | |
| 848 | + /* Skip copying the verb */ | |
| 849 | + memcpy(&p[1], &cl[1], EQ_DESC_SIZE_WITHOUT_FD - 1); | |
| 850 | + memcpy(&p[EQ_DESC_SIZE_FD_START/sizeof(uint32_t)], | |
| 851 | + &fd[i], sizeof(*fd)); | |
| 852 | + eqcr_pi++; | |
| 853 | + } | |
| 854 | + | |
| 855 | + dma_wmb(); | |
| 856 | + | |
| 857 | + /* Set the verb byte, have to substitute in the valid-bit */ | |
| 858 | + eqcr_pi = s->eqcr.pi; | |
| 859 | + for (i = 0; i < num_enqueued; i++) { | |
| 860 | + p = (s->addr_cena + QBMAN_CENA_SWP_EQCR(eqcr_pi & half_mask)); | |
| 861 | + cl = (uint32_t *)(&d[i]); | |
| 862 | + p[0] = cl[0] | s->eqcr.pi_vb; | |
| 863 | + eqcr_pi++; | |
| 864 | + if (!(eqcr_pi & half_mask)) | |
| 865 | + s->eqcr.pi_vb ^= QB_VALID_BIT; | |
| 866 | + } | |
| 867 | + | |
| 868 | + /* Flush all the cacheline without load/store in between */ | |
| 869 | + eqcr_pi = s->eqcr.pi; | |
| 870 | + addr_cena = (uint64_t)s->addr_cena; | |
| 871 | + for (i = 0; i < num_enqueued; i++) | |
| 872 | + eqcr_pi++; | |
| 873 | + s->eqcr.pi = eqcr_pi & full_mask; | |
| 874 | + | |
| 875 | + return num_enqueued; | |
| 876 | +} | |
| 877 | + | |
| 878 | +/** | |
| 879 | + * qbman_swp_enqueue_multiple_desc_mem_back() - Issue a multi enqueue command | |
| 880 | + * using multiple enqueue descriptor | |
| 881 | + * @s: the software portal used for enqueue | |
| 882 | + * @d: table of minimal enqueue descriptor | |
| 883 | + * @fd: table pointer of frame descriptor table to be enqueued | |
| 884 | + * @num_frames: number of fd to be enqueued | |
| 885 | + * | |
| 886 | + * Return the number of fd enqueued, or a negative error number. | |
| 887 | + */ | |
| 888 | +static | |
| 889 | +int qbman_swp_enqueue_multiple_desc_mem_back(struct qbman_swp *s, | |
| 890 | + const struct qbman_eq_desc *d, | |
| 891 | + const struct dpaa2_fd *fd, | |
| 892 | + int num_frames) | |
| 893 | +{ | |
| 894 | + uint32_t *p; | |
| 895 | + const uint32_t *cl; | |
| 896 | + uint32_t eqcr_ci, eqcr_pi, half_mask, full_mask; | |
| 897 | + int i, num_enqueued = 0; | |
| 898 | + | |
| 899 | + half_mask = (s->eqcr.pi_ci_mask>>1); | |
| 900 | + full_mask = s->eqcr.pi_ci_mask; | |
| 901 | + if (!s->eqcr.available) { | |
| 902 | + eqcr_ci = s->eqcr.ci; | |
| 903 | + p = s->addr_cena + QBMAN_CENA_SWP_EQCR_CI_MEMBACK; | |
| 904 | + s->eqcr.ci = __raw_readl(p) & full_mask; | |
| 905 | + s->eqcr.available = qm_cyc_diff(s->eqcr.pi_ring_size, | |
| 906 | + eqcr_ci, s->eqcr.ci); | |
| 907 | + if (!s->eqcr.available) | |
| 908 | + return 0; | |
| 909 | + } | |
| 910 | + | |
| 911 | + eqcr_pi = s->eqcr.pi; | |
| 912 | + num_enqueued = (s->eqcr.available < num_frames) ? | |
| 913 | + s->eqcr.available : num_frames; | |
| 914 | + s->eqcr.available -= num_enqueued; | |
| 915 | + /* Fill in the EQCR ring */ | |
| 916 | + for (i = 0; i < num_enqueued; i++) { | |
| 917 | + p = (s->addr_cena + QBMAN_CENA_SWP_EQCR(eqcr_pi & half_mask)); | |
| 918 | + cl = (uint32_t *)(&d[i]); | |
| 919 | + /* Skip copying the verb */ | |
| 920 | + memcpy(&p[1], &cl[1], EQ_DESC_SIZE_WITHOUT_FD - 1); | |
| 921 | + memcpy(&p[EQ_DESC_SIZE_FD_START/sizeof(uint32_t)], | |
| 922 | + &fd[i], sizeof(*fd)); | |
| 923 | + eqcr_pi++; | |
| 924 | + } | |
| 925 | + | |
| 926 | + /* Set the verb byte, have to substitute in the valid-bit */ | |
| 927 | + eqcr_pi = s->eqcr.pi; | |
| 928 | + for (i = 0; i < num_enqueued; i++) { | |
| 929 | + p = (s->addr_cena + QBMAN_CENA_SWP_EQCR(eqcr_pi & half_mask)); | |
| 930 | + cl = (uint32_t *)(&d[i]); | |
| 931 | + p[0] = cl[0] | s->eqcr.pi_vb; | |
| 932 | + eqcr_pi++; | |
| 933 | + if (!(eqcr_pi & half_mask)) | |
| 934 | + s->eqcr.pi_vb ^= QB_VALID_BIT; | |
| 935 | + } | |
| 936 | + | |
| 937 | + s->eqcr.pi = eqcr_pi & full_mask; | |
| 938 | + | |
| 939 | + dma_wmb(); | |
| 940 | + qbman_write_register(s, QBMAN_CINH_SWP_EQCR_PI, | |
| 941 | + (QB_RT_BIT)|(s->eqcr.pi)|s->eqcr.pi_vb); | |
| 942 | + | |
| 943 | + return num_enqueued; | |
| 491 | 944 | } |
| 492 | 945 | |
| 493 | 946 | /* Static (push) dequeue */ |
| @@ -645,7 +1098,7 @@ void qbman_pull_desc_set_channel(struct qbman_pull_desc *d, u32 chid, · 151 unmodified lines | ||
| 645 | 1098 | } |
| 646 | 1099 | |
| 647 | 1100 | /** |
| 648 | − * qbman_swp_pull() - Issue the pull dequeue command | |
| 1101 | + * qbman_swp_pull_direct() - Issue the pull dequeue command | |
| 649 | 1102 | * @s: the software portal object |
| 650 | 1103 | * @d: the software portal descriptor which has been configured with |
| 651 | 1104 | * the set of qbman_pull_desc_set_*() calls |
| @@ -653,7 +1106,8 @@ void qbman_pull_desc_set_channel(struct qbman_pull_desc *d, u32 chid, · 1 unmodified line | ||
| 653 | 1106 | * Return 0 for success, and -EBUSY if the software portal is not ready |
| 654 | 1107 | * to do pull dequeue. |
| 655 | 1108 | */ |
| 656 | −int qbman_swp_pull(struct qbman_swp *s, struct qbman_pull_desc *d) | |
| 1109 | +static | |
| 1110 | +int qbman_swp_pull_direct(struct qbman_swp *s, struct qbman_pull_desc *d) | |
| 657 | 1111 | { |
| 658 | 1112 | struct qbman_pull_desc *p; |
| 659 | 1113 | |
| @@ -671,18 +1125,48 @@ int qbman_swp_pull(struct qbman_swp *s, struct qbman_pull_desc *d) · 11 unmodified lines | ||
| 671 | 1125 | p->dq_src = d->dq_src; |
| 672 | 1126 | p->rsp_addr = d->rsp_addr; |
| 673 | 1127 | p->rsp_addr_virt = d->rsp_addr_virt; |
| 1128 | + dma_wmb(); | |
| 1129 | + /* Set the verb byte, have to substitute in the valid-bit */ | |
| 1130 | + p->verb = d->verb | s->vdq.valid_bit; | |
| 1131 | + s->vdq.valid_bit ^= QB_VALID_BIT; | |
| 674 | 1132 | |
| 675 | − if ((s->desc->qman_version & QMAN_REV_MASK) < QMAN_REV_5000) { | |
| 676 | − dma_wmb(); | |
| 677 | − /* Set the verb byte, have to substitute in the valid-bit */ | |
| 678 | − p->verb = d->verb | s->vdq.valid_bit; | |
| 679 | − s->vdq.valid_bit ^= QB_VALID_BIT; | |
| 680 | − } else { | |
| 681 | − p->verb = d->verb | s->vdq.valid_bit; | |
| 682 | − s->vdq.valid_bit ^= QB_VALID_BIT; | |
| 683 | − dma_wmb(); | |
| 684 | − qbman_write_register(s, QBMAN_CINH_SWP_VDQCR_RT, QMAN_RT_MODE); | |
| 1133 | + return 0; | |
| 1134 | +} | |
| 1135 | + | |
| 1136 | +/** | |
| 1137 | + * qbman_swp_pull_mem_back() - Issue the pull dequeue command | |
| 1138 | + * @s: the software portal object | |
| 1139 | + * @d: the software portal descriptor which has been configured with | |
| 1140 | + * the set of qbman_pull_desc_set_*() calls | |
| 1141 | + * | |
| 1142 | + * Return 0 for success, and -EBUSY if the software portal is not ready | |
| 1143 | + * to do pull dequeue. | |
| 1144 | + */ | |
| 1145 | +static | |
| 1146 | +int qbman_swp_pull_mem_back(struct qbman_swp *s, struct qbman_pull_desc *d) | |
| 1147 | +{ | |
| 1148 | + struct qbman_pull_desc *p; | |
| 1149 | + | |
| 1150 | + if (!atomic_dec_and_test(&s->vdq.available)) { | |
| 1151 | + atomic_inc(&s->vdq.available); | |
| 1152 | + return -EBUSY; | |
| 685 | 1153 | } |
| 1154 | + s->vdq.storage = (void *)(uintptr_t)d->rsp_addr_virt; | |
| 1155 | + if ((s->desc->qman_version & QMAN_REV_MASK) < QMAN_REV_5000) | |
| 1156 | + p = qbman_get_cmd(s, QBMAN_CENA_SWP_VDQCR); | |
| 1157 | + else | |
| 1158 | + p = qbman_get_cmd(s, QBMAN_CENA_SWP_VDQCR_MEM); | |
| 1159 | + p->numf = d->numf; | |
| 1160 | + p->tok = QMAN_DQ_TOKEN_VALID; | |
| 1161 | + p->dq_src = d->dq_src; | |
| 1162 | + p->rsp_addr = d->rsp_addr; | |
| 1163 | + p->rsp_addr_virt = d->rsp_addr_virt; | |
| 1164 | + | |
| 1165 | + /* Set the verb byte, have to substitute in the valid-bit */ | |
| 1166 | + p->verb = d->verb | s->vdq.valid_bit; | |
| 1167 | + s->vdq.valid_bit ^= QB_VALID_BIT; | |
| 1168 | + dma_wmb(); | |
| 1169 | + qbman_write_register(s, QBMAN_CINH_SWP_VDQCR_RT, QMAN_RT_MODE); | |
| 686 | 1170 | |
| 687 | 1171 | return 0; |
| 688 | 1172 | } |
| @@ -690,14 +1174,14 @@ int qbman_swp_pull(struct qbman_swp *s, struct qbman_pull_desc *d) · 1 unmodified line | ||
| 690 | 1174 | #define QMAN_DQRR_PI_MASK 0xf |
| 691 | 1175 | |
| 692 | 1176 | /** |
| 693 | − * qbman_swp_dqrr_next() - Get an valid DQRR entry | |
| 1177 | + * qbman_swp_dqrr_next_direct() - Get an valid DQRR entry | |
| 694 | 1178 | * @s: the software portal object |
| 695 | 1179 | * |
| 696 | 1180 | * Return NULL if there are no unconsumed DQRR entries. Return a DQRR entry |
| 697 | 1181 | * only once, so repeated calls can return a sequence of DQRR entries, without |
| 698 | 1182 | * requiring they be consumed immediately or in any particular order. |
| 699 | 1183 | */ |
| 700 | −const struct dpaa2_dq *qbman_swp_dqrr_next(struct qbman_swp *s) | |
| 1184 | +const struct dpaa2_dq *qbman_swp_dqrr_next_direct(struct qbman_swp *s) | |
| 701 | 1185 | { |
| 702 | 1186 | u32 verb; |
| 703 | 1187 | u32 response_verb; |
| @@ -740,10 +1224,99 @@ const struct dpaa2_dq *qbman_swp_dqrr_next(struct qbman_swp *s) · 36 unmodified lines | ||
| 740 | 1224 | QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx))); |
| 741 | 1225 | } |
| 742 | 1226 | |
| 743 | − if ((s->desc->qman_version & QMAN_REV_MASK) < QMAN_REV_5000) | |
| 744 | − p = qbman_get_cmd(s, QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx)); | |
| 745 | − else | |
| 746 | − p = qbman_get_cmd(s, QBMAN_CENA_SWP_DQRR_MEM(s->dqrr.next_idx)); | |
| 1227 | + p = qbman_get_cmd(s, QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx)); | |
| 1228 | + verb = p->dq.verb; | |
| 1229 | + | |
| 1230 | + /* | |
| 1231 | + * If the valid-bit isn't of the expected polarity, nothing there. Note, | |
| 1232 | + * in the DQRR reset bug workaround, we shouldn't need to skip these | |
| 1233 | + * check, because we've already determined that a new entry is available | |
| 1234 | + * and we've invalidated the cacheline before reading it, so the | |
| 1235 | + * valid-bit behaviour is repaired and should tell us what we already | |
| 1236 | + * knew from reading PI. | |
| 1237 | + */ | |
| 1238 | + if ((verb & QB_VALID_BIT) != s->dqrr.valid_bit) { | |
| 1239 | + prefetch(qbman_get_cmd(s, | |
| 1240 | + QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx))); | |
| 1241 | + return NULL; | |
| 1242 | + } | |
| 1243 | + /* | |
| 1244 | + * There's something there. Move "next_idx" attention to the next ring | |
| 1245 | + * entry (and prefetch it) before returning what we found. | |
| 1246 | + */ | |
| 1247 | + s->dqrr.next_idx++; | |
| 1248 | + s->dqrr.next_idx &= s->dqrr.dqrr_size - 1; /* Wrap around */ | |
| 1249 | + if (!s->dqrr.next_idx) | |
| 1250 | + s->dqrr.valid_bit ^= QB_VALID_BIT; | |
| 1251 | + | |
| 1252 | + /* | |
| 1253 | + * If this is the final response to a volatile dequeue command | |
| 1254 | + * indicate that the vdq is available | |
| 1255 | + */ | |
| 1256 | + flags = p->dq.stat; | |
| 1257 | + response_verb = verb & QBMAN_RESULT_MASK; | |
| 1258 | + if ((response_verb == QBMAN_RESULT_DQ) && | |
| 1259 | + (flags & DPAA2_DQ_STAT_VOLATILE) && | |
| 1260 | + (flags & DPAA2_DQ_STAT_EXPIRED)) | |
| 1261 | + atomic_inc(&s->vdq.available); | |
| 1262 | + | |
| 1263 | + prefetch(qbman_get_cmd(s, QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx))); | |
| 1264 | + | |
| 1265 | + return p; | |
| 1266 | +} | |
| 1267 | + | |
| 1268 | +/** | |
| 1269 | + * qbman_swp_dqrr_next_mem_back() - Get an valid DQRR entry | |
| 1270 | + * @s: the software portal object | |
| 1271 | + * | |
| 1272 | + * Return NULL if there are no unconsumed DQRR entries. Return a DQRR entry | |
| 1273 | + * only once, so repeated calls can return a sequence of DQRR entries, without | |
| 1274 | + * requiring they be consumed immediately or in any particular order. | |
| 1275 | + */ | |
| 1276 | +const struct dpaa2_dq *qbman_swp_dqrr_next_mem_back(struct qbman_swp *s) | |
| 1277 | +{ | |
| 1278 | + u32 verb; | |
| 1279 | + u32 response_verb; | |
| 1280 | + u32 flags; | |
| 1281 | + struct dpaa2_dq *p; | |
| 1282 | + | |
| 1283 | + /* Before using valid-bit to detect if something is there, we have to | |
| 1284 | + * handle the case of the DQRR reset bug... | |
| 1285 | + */ | |
| 1286 | + if (unlikely(s->dqrr.reset_bug)) { | |
| 1287 | + /* | |
| 1288 | + * We pick up new entries by cache-inhibited producer index, | |
| 1289 | + * which means that a non-coherent mapping would require us to | |
| 1290 | + * invalidate and read *only* once that PI has indicated that | |
| 1291 | + * there's an entry here. The first trip around the DQRR ring | |
| 1292 | + * will be much less efficient than all subsequent trips around | |
| 1293 | + * it... | |
| 1294 | + */ | |
| 1295 | + u8 pi = qbman_read_register(s, QBMAN_CINH_SWP_DQPI) & | |
| 1296 | + QMAN_DQRR_PI_MASK; | |
| 1297 | + | |
| 1298 | + /* there are new entries if pi != next_idx */ | |
| 1299 | + if (pi == s->dqrr.next_idx) | |
| 1300 | + return NULL; | |
| 1301 | + | |
| 1302 | + /* | |
| 1303 | + * if next_idx is/was the last ring index, and 'pi' is | |
| 1304 | + * different, we can disable the workaround as all the ring | |
| 1305 | + * entries have now been DMA'd to so valid-bit checking is | |
| 1306 | + * repaired. Note: this logic needs to be based on next_idx | |
| 1307 | + * (which increments one at a time), rather than on pi (which | |
| 1308 | + * can burst and wrap-around between our snapshots of it). | |
| 1309 | + */ | |
| 1310 | + if (s->dqrr.next_idx == (s->dqrr.dqrr_size - 1)) { | |
| 1311 | + pr_debug("next_idx=%d, pi=%d, clear reset bug\n", | |
| 1312 | + s->dqrr.next_idx, pi); | |
| 1313 | + s->dqrr.reset_bug = 0; | |
| 1314 | + } | |
| 1315 | + prefetch(qbman_get_cmd(s, | |
| 1316 | + QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx))); | |
| 1317 | + } | |
| 1318 | + | |
| 1319 | + p = qbman_get_cmd(s, QBMAN_CENA_SWP_DQRR_MEM(s->dqrr.next_idx)); | |
| 747 | 1320 | verb = p->dq.verb; |
| 748 | 1321 | |
| 749 | 1322 | /* |
| @@ -872,7 +1445,7 @@ void qbman_release_desc_set_rcdi(struct qbman_release_desc *d, int enable) · 122 unmodified lines | ||
| 872 | 1445 | #define RAR_SUCCESS(rar) ((rar) & 0x100) |
| 873 | 1446 | |
| 874 | 1447 | /** |
| 875 | − * qbman_swp_release() - Issue a buffer release command | |
| 1448 | + * qbman_swp_release_direct() - Issue a buffer release command | |
| 876 | 1449 | * @s: the software portal object |
| 877 | 1450 | * @d: the release descriptor |
| 878 | 1451 | * @buffers: a pointer pointing to the buffer address to be released |
| @@ -880,8 +1453,9 @@ void qbman_release_desc_set_rcdi(struct qbman_release_desc *d, int enable) · 1 unmodified line | ||
| 880 | 1453 | * |
| 881 | 1454 | * Return 0 for success, -EBUSY if the release command ring is not ready. |
| 882 | 1455 | */ |
| 883 | −int qbman_swp_release(struct qbman_swp *s, const struct qbman_release_desc *d, | |
| 884 | − const u64 *buffers, unsigned int num_buffers) | |
| 1456 | +int qbman_swp_release_direct(struct qbman_swp *s, | |
| 1457 | + const struct qbman_release_desc *d, | |
| 1458 | + const u64 *buffers, unsigned int num_buffers) | |
| 885 | 1459 | { |
| 886 | 1460 | int i; |
| 887 | 1461 | struct qbman_release_desc *p; |
| @@ -895,28 +1469,59 @@ int qbman_swp_release(struct qbman_swp *s, const struct qbman_release_desc *d, · 7 unmodified lines | ||
| 895 | 1469 | return -EBUSY; |
| 896 | 1470 | |
| 897 | 1471 | /* Start the release command */ |
| 898 | − if ((s->desc->qman_version & QMAN_REV_MASK) < QMAN_REV_5000) | |
| 899 | − p = qbman_get_cmd(s, QBMAN_CENA_SWP_RCR(RAR_IDX(rar))); | |
| 900 | − else | |
| 901 | − p = qbman_get_cmd(s, QBMAN_CENA_SWP_RCR_MEM(RAR_IDX(rar))); | |
| 1472 | + p = qbman_get_cmd(s, QBMAN_CENA_SWP_RCR(RAR_IDX(rar))); | |
| 1473 | + | |
| 902 | 1474 | /* Copy the caller's buffer pointers to the command */ |
| 903 | 1475 | for (i = 0; i < num_buffers; i++) |
| 904 | 1476 | p->buf[i] = cpu_to_le64(buffers[i]); |
| 905 | 1477 | p->bpid = d->bpid; |
| 906 | 1478 | |
| 907 | − if ((s->desc->qman_version & QMAN_REV_MASK) < QMAN_REV_5000) { | |
| 908 | − /* | |
| 909 | − * Set the verb byte, have to substitute in the valid-bit | |
| 910 | − * and the number of buffers. | |
| 911 | − */ | |
| 912 | − dma_wmb(); | |
| 913 | − p->verb = d->verb | RAR_VB(rar) | num_buffers; | |
| 914 | − } else { | |
| 915 | − p->verb = d->verb | RAR_VB(rar) | num_buffers; | |
| 916 | − dma_wmb(); | |
| 917 | − qbman_write_register(s, QBMAN_CINH_SWP_RCR_AM_RT + | |
| 918 | − RAR_IDX(rar) * 4, QMAN_RT_MODE); | |
| 919 | − } | |
| 1479 | + /* | |
| 1480 | + * Set the verb byte, have to substitute in the valid-bit | |
| 1481 | + * and the number of buffers. | |
| 1482 | + */ | |
| 1483 | + dma_wmb(); | |
| 1484 | + p->verb = d->verb | RAR_VB(rar) | num_buffers; | |
| 1485 | + | |
| 1486 | + return 0; | |
| 1487 | +} | |
| 1488 | + | |
| 1489 | +/** | |
| 1490 | + * qbman_swp_release_mem_back() - Issue a buffer release command | |
| 1491 | + * @s: the software portal object | |
| 1492 | + * @d: the release descriptor | |
| 1493 | + * @buffers: a pointer pointing to the buffer address to be released | |
| 1494 | + * @num_buffers: number of buffers to be released, must be less than 8 | |
| 1495 | + * | |
| 1496 | + * Return 0 for success, -EBUSY if the release command ring is not ready. | |
| 1497 | + */ | |
| 1498 | +int qbman_swp_release_mem_back(struct qbman_swp *s, | |
| 1499 | + const struct qbman_release_desc *d, | |
| 1500 | + const u64 *buffers, unsigned int num_buffers) | |
| 1501 | +{ | |
| 1502 | + int i; | |
| 1503 | + struct qbman_release_desc *p; | |
| 1504 | + u32 rar; | |
| 1505 | + | |
| 1506 | + if (!num_buffers || (num_buffers > 7)) | |
| 1507 | + return -EINVAL; | |
| 1508 | + | |
| 1509 | + rar = qbman_read_register(s, QBMAN_CINH_SWP_RAR); | |
| 1510 | + if (!RAR_SUCCESS(rar)) | |
| 1511 | + return -EBUSY; | |
| 1512 | + | |
| 1513 | + /* Start the release command */ | |
| 1514 | + p = qbman_get_cmd(s, QBMAN_CENA_SWP_RCR_MEM(RAR_IDX(rar))); | |
| 1515 | + | |
| 1516 | + /* Copy the caller's buffer pointers to the command */ | |
| 1517 | + for (i = 0; i < num_buffers; i++) | |
| 1518 | + p->buf[i] = cpu_to_le64(buffers[i]); | |
| 1519 | + p->bpid = d->bpid; | |
| 1520 | + | |
| 1521 | + p->verb = d->verb | RAR_VB(rar) | num_buffers; | |
| 1522 | + dma_wmb(); | |
| 1523 | + qbman_write_register(s, QBMAN_CINH_SWP_RCR_AM_RT + | |
| 1524 | + RAR_IDX(rar) * 4, QMAN_RT_MODE); | |
| 920 | 1525 | |
| 921 | 1526 | return 0; |
| 922 | 1527 | } |
| @@ -9,6 +9,13 @@ · 8 unmodified lines | ||
| 9 | 9 | |
| 10 | 10 | #include <soc/fsl/dpaa2-fd.h> |
| 11 | 11 | |
| 12 | +#define QMAN_REV_4000 0x04000000 | |
| 13 | +#define QMAN_REV_4100 0x04010000 | |
| 14 | +#define QMAN_REV_4101 0x04010001 | |
| 15 | +#define QMAN_REV_5000 0x05000000 | |
| 16 | + | |
| 17 | +#define QMAN_REV_MASK 0xffff0000 | |
| 18 | + | |
| 12 | 19 | struct dpaa2_dq; |
| 13 | 20 | struct qbman_swp; |
| 14 | 21 | |
| @@ -81,6 +88,10 @@ struct qbman_eq_desc { · 66 unmodified lines | ||
| 81 | 88 | u8 wae; |
| 82 | 89 | u8 rspid; |
| 83 | 90 | __le64 rsp_addr; |
| 91 | +}; | |
| 92 | + | |
| 93 | +struct qbman_eq_desc_with_fd { | |
| 94 | + struct qbman_eq_desc desc; | |
| 84 | 95 | u8 fd[32]; |
| 85 | 96 | }; |
| 86 | 97 | |
| @@ -132,8 +143,48 @@ struct qbman_swp { · 45 unmodified lines | ||
| 132 | 143 | u8 dqrr_size; |
| 133 | 144 | int reset_bug; /* indicates dqrr reset workaround is needed */ |
| 134 | 145 | } dqrr; |
| 146 | + | |
| 147 | + struct { | |
| 148 | + u32 pi; | |
| 149 | + u32 pi_vb; | |
| 150 | + u32 pi_ring_size; | |
| 151 | + u32 pi_ci_mask; | |
| 152 | + u32 ci; | |
| 153 | + int available; | |
| 154 | + u32 pend; | |
| 155 | + u32 no_pfdr; | |
| 156 | + } eqcr; | |
| 157 | + | |
| 158 | + spinlock_t access_spinlock; | |
| 135 | 159 | }; |
| 136 | 160 | |
| 161 | +/* Function pointers */ | |
| 162 | +extern | |
| 163 | +int (*qbman_swp_enqueue_ptr)(struct qbman_swp *s, | |
| 164 | + const struct qbman_eq_desc *d, | |
| 165 | + const struct dpaa2_fd *fd); | |
| 166 | +extern | |
| 167 | +int (*qbman_swp_enqueue_multiple_ptr)(struct qbman_swp *s, | |
| 168 | + const struct qbman_eq_desc *d, | |
| 169 | + const struct dpaa2_fd *fd, | |
| 170 | + uint32_t *flags, | |
| 171 | + int num_frames); | |
| 172 | +extern | |
| 173 | +int (*qbman_swp_enqueue_multiple_desc_ptr)(struct qbman_swp *s, | |
| 174 | + const struct qbman_eq_desc *d, | |
| 175 | + const struct dpaa2_fd *fd, | |
| 176 | + int num_frames); | |
| 177 | +extern | |
| 178 | +int (*qbman_swp_pull_ptr)(struct qbman_swp *s, struct qbman_pull_desc *d); | |
| 179 | +extern | |
| 180 | +const struct dpaa2_dq *(*qbman_swp_dqrr_next_ptr)(struct qbman_swp *s); | |
| 181 | +extern | |
| 182 | +int (*qbman_swp_release_ptr)(struct qbman_swp *s, | |
| 183 | + const struct qbman_release_desc *d, | |
| 184 | + const u64 *buffers, | |
| 185 | + unsigned int num_buffers); | |
| 186 | + | |
| 187 | +/* Functions */ | |
| 137 | 188 | struct qbman_swp *qbman_swp_init(const struct qbman_swp_desc *d); |
| 138 | 189 | void qbman_swp_finish(struct qbman_swp *p); |
| 139 | 190 | u32 qbman_swp_interrupt_read_status(struct qbman_swp *p); |
| @@ -158,9 +209,6 @@ void qbman_pull_desc_set_wq(struct qbman_pull_desc *d, u32 wqid, · 18 unmodified lines | ||
| 158 | 209 | void qbman_pull_desc_set_channel(struct qbman_pull_desc *d, u32 chid, |
| 159 | 210 | enum qbman_pull_type_e dct); |
| 160 | 211 | |
| 161 | −int qbman_swp_pull(struct qbman_swp *p, struct qbman_pull_desc *d); | |
| 162 | − | |
| 163 | −const struct dpaa2_dq *qbman_swp_dqrr_next(struct qbman_swp *s); | |
| 164 | 212 | void qbman_swp_dqrr_consume(struct qbman_swp *s, const struct dpaa2_dq *dq); |
| 165 | 213 | |
| 166 | 214 | int qbman_result_has_new_result(struct qbman_swp *p, const struct dpaa2_dq *dq); |
| @@ -172,15 +220,11 @@ void qbman_eq_desc_set_fq(struct qbman_eq_desc *d, u32 fqid); · 5 unmodified lines | ||
| 172 | 220 | void qbman_eq_desc_set_qd(struct qbman_eq_desc *d, u32 qdid, |
| 173 | 221 | u32 qd_bin, u32 qd_prio); |
| 174 | 222 | |
| 175 | −int qbman_swp_enqueue(struct qbman_swp *p, const struct qbman_eq_desc *d, | |
| 176 | − const struct dpaa2_fd *fd); | |
| 177 | 223 | |
| 178 | 224 | void qbman_release_desc_clear(struct qbman_release_desc *d); |
| 179 | 225 | void qbman_release_desc_set_bpid(struct qbman_release_desc *d, u16 bpid); |
| 180 | 226 | void qbman_release_desc_set_rcdi(struct qbman_release_desc *d, int enable); |
| 181 | 227 | |
| 182 | −int qbman_swp_release(struct qbman_swp *s, const struct qbman_release_desc *d, | |
| 183 | − const u64 *buffers, unsigned int num_buffers); | |
| 184 | 228 | int qbman_swp_acquire(struct qbman_swp *s, u16 bpid, u64 *buffers, |
| 185 | 229 | unsigned int num_buffers); |
| 186 | 230 | int qbman_swp_alt_fq_state(struct qbman_swp *s, u32 fqid, |
| @@ -193,6 +237,61 @@ void *qbman_swp_mc_start(struct qbman_swp *p); · 6 unmodified lines | ||
| 193 | 237 | void qbman_swp_mc_submit(struct qbman_swp *p, void *cmd, u8 cmd_verb); |
| 194 | 238 | void *qbman_swp_mc_result(struct qbman_swp *p); |
| 195 | 239 | |
| 240 | +/** | |
| 241 | + * qbman_swp_enqueue() - Issue an enqueue command | |
| 242 | + * @s: the software portal used for enqueue | |
| 243 | + * @d: the enqueue descriptor | |
| 244 | + * @fd: the frame descriptor to be enqueued | |
| 245 | + * | |
| 246 | + * Return 0 for successful enqueue, -EBUSY if the EQCR is not ready. | |
| 247 | + */ | |
| 248 | +static inline int | |
| 249 | +qbman_swp_enqueue(struct qbman_swp *s, const struct qbman_eq_desc *d, | |
| 250 | + const struct dpaa2_fd *fd) | |
| 251 | +{ | |
| 252 | + return qbman_swp_enqueue_ptr(s, d, fd); | |
| 253 | +} | |
| 254 | + | |
| 255 | +/** | |
| 256 | + * qbman_swp_enqueue_multiple() - Issue a multi enqueue command | |
| 257 | + * using one enqueue descriptor | |
| 258 | + * @s: the software portal used for enqueue | |
| 259 | + * @d: the enqueue descriptor | |
| 260 | + * @fd: table pointer of frame descriptor table to be enqueued | |
| 261 | + * @flags: table pointer of QBMAN_ENQUEUE_FLAG_DCA flags, not used if NULL | |
| 262 | + * @num_frames: number of fd to be enqueued | |
| 263 | + * | |
| 264 | + * Return the number of fd enqueued, or a negative error number. | |
| 265 | + */ | |
| 266 | +static inline int | |
| 267 | +qbman_swp_enqueue_multiple(struct qbman_swp *s, | |
| 268 | + const struct qbman_eq_desc *d, | |
| 269 | + const struct dpaa2_fd *fd, | |
| 270 | + uint32_t *flags, | |
| 271 | + int num_frames) | |
| 272 | +{ | |
| 273 | + return qbman_swp_enqueue_multiple_ptr(s, d, fd, flags, num_frames); | |
| 274 | +} | |
| 275 | + | |
| 276 | +/** | |
| 277 | + * qbman_swp_enqueue_multiple_desc() - Issue a multi enqueue command | |
| 278 | + * using multiple enqueue descriptor | |
| 279 | + * @s: the software portal used for enqueue | |
| 280 | + * @d: table of minimal enqueue descriptor | |
| 281 | + * @fd: table pointer of frame descriptor table to be enqueued | |
| 282 | + * @num_frames: number of fd to be enqueued | |
| 283 | + * | |
| 284 | + * Return the number of fd enqueued, or a negative error number. | |
| 285 | + */ | |
| 286 | +static inline int | |
| 287 | +qbman_swp_enqueue_multiple_desc(struct qbman_swp *s, | |
| 288 | + const struct qbman_eq_desc *d, | |
| 289 | + const struct dpaa2_fd *fd, | |
| 290 | + int num_frames) | |
| 291 | +{ | |
| 292 | + return qbman_swp_enqueue_multiple_desc_ptr(s, d, fd, num_frames); | |
| 293 | +} | |
| 294 | + | |
| 196 | 295 | /** |
| 197 | 296 | * qbman_result_is_DQ() - check if the dequeue result is a dequeue response |
| 198 | 297 | * @dq: the dequeue result to be checked |
| @@ -504,4 +603,49 @@ int qbman_bp_query(struct qbman_swp *s, u16 bpid, · 305 unmodified lines | ||
| 504 | 603 | |
| 505 | 604 | u32 qbman_bp_info_num_free_bufs(struct qbman_bp_query_rslt *a); |
| 506 | 605 | |
| 606 | +/** | |
| 607 | + * qbman_swp_release() - Issue a buffer release command | |
| 608 | + * @s: the software portal object | |
| 609 | + * @d: the release descriptor | |
| 610 | + * @buffers: a pointer pointing to the buffer address to be released | |
| 611 | + * @num_buffers: number of buffers to be released, must be less than 8 | |
| 612 | + * | |
| 613 | + * Return 0 for success, -EBUSY if the release command ring is not ready. | |
| 614 | + */ | |
| 615 | +static inline int qbman_swp_release(struct qbman_swp *s, | |
| 616 | + const struct qbman_release_desc *d, | |
| 617 | + const u64 *buffers, | |
| 618 | + unsigned int num_buffers) | |
| 619 | +{ | |
| 620 | + return qbman_swp_release_ptr(s, d, buffers, num_buffers); | |
| 621 | +} | |
| 622 | + | |
| 623 | +/** | |
| 624 | + * qbman_swp_pull() - Issue the pull dequeue command | |
| 625 | + * @s: the software portal object | |
| 626 | + * @d: the software portal descriptor which has been configured with | |
| 627 | + * the set of qbman_pull_desc_set_*() calls | |
| 628 | + * | |
| 629 | + * Return 0 for success, and -EBUSY if the software portal is not ready | |
| 630 | + * to do pull dequeue. | |
| 631 | + */ | |
| 632 | +static inline int qbman_swp_pull(struct qbman_swp *s, | |
| 633 | + struct qbman_pull_desc *d) | |
| 634 | +{ | |
| 635 | + return qbman_swp_pull_ptr(s, d); | |
| 636 | +} | |
| 637 | + | |
| 638 | +/** | |
| 639 | + * qbman_swp_dqrr_next() - Get an valid DQRR entry | |
| 640 | + * @s: the software portal object | |
| 641 | + * | |
| 642 | + * Return NULL if there are no unconsumed DQRR entries. Return a DQRR entry | |
| 643 | + * only once, so repeated calls can return a sequence of DQRR entries, without | |
| 644 | + * requiring they be consumed immediately or in any particular order. | |
| 645 | + */ | |
| 646 | +static inline const struct dpaa2_dq *qbman_swp_dqrr_next(struct qbman_swp *s) | |
| 647 | +{ | |
| 648 | + return qbman_swp_dqrr_next_ptr(s); | |
| 649 | +} | |
| 650 | + | |
| 507 | 651 | #endif /* __FSL_QBMAN_PORTAL_H */ |
| @@ -423,7 +423,7 @@ static void qe_upload_microcode(const void *base, · 422 unmodified lines | ||
| 423 | 423 | qe_iowrite32be(be32_to_cpu(code[i]), &qe_immr->iram.idata); |
| 424 | 424 | |
| 425 | 425 | /* Set I-RAM Ready Register */ |
| 426 | − qe_iowrite32be(be32_to_cpu(QE_IRAM_READY), &qe_immr->iram.iready); | |
| 426 | + qe_iowrite32be(QE_IRAM_READY, &qe_immr->iram.iready); | |
| 427 | 427 | } |
| 428 | 428 | |
| 429 | 429 | /* |
| @@ -525,7 +525,7 @@ int qe_upload_firmware(const struct qe_firmware *firmware) · 95 unmodified lines | ||
| 525 | 525 | */ |
| 526 | 526 | memset(&qe_firmware_info, 0, sizeof(qe_firmware_info)); |
| 527 | 527 | strlcpy(qe_firmware_info.id, firmware->id, sizeof(qe_firmware_info.id)); |
| 528 | − qe_firmware_info.extended_modes = firmware->extended_modes; | |
| 528 | + qe_firmware_info.extended_modes = be64_to_cpu(firmware->extended_modes); | |
| 529 | 529 | memcpy(qe_firmware_info.vtraps, firmware->vtraps, |
| 530 | 530 | sizeof(firmware->vtraps)); |
| 531 | 531 | |
| @@ -46,7 +46,7 @@ int cpm_muram_init(void) · 45 unmodified lines | ||
| 46 | 46 | { |
| 47 | 47 | struct device_node *np; |
| 48 | 48 | struct resource r; |
| 49 | − u32 zero[OF_MAX_ADDR_CELLS] = {}; | |
| 49 | + __be32 zero[OF_MAX_ADDR_CELLS] = {}; | |
| 50 | 50 | resource_size_t max = 0; |
| 51 | 51 | int i = 0; |
| 52 | 52 | int ret = 0; |
| @@ -44,7 +44,7 @@ · 43 unmodified lines | ||
| 44 | 44 | |
| 45 | 45 | struct qe_ic { |
| 46 | 46 | /* Control registers offset */ |
| 47 | − u32 __iomem *regs; | |
| 47 | + __be32 __iomem *regs; | |
| 48 | 48 | |
| 49 | 49 | /* The remapper for this QEIC */ |
| 50 | 50 | struct irq_domain *irqhost; |
| @@ -632,7 +632,7 @@ int ucc_set_tdm_rxtx_sync(u32 tdm_num, enum qe_clock clock, · 631 unmodified lines | ||
| 632 | 632 | { |
| 633 | 633 | int source; |
| 634 | 634 | u32 shift; |
| 635 | − struct qe_mux *qe_mux_reg; | |
| 635 | + struct qe_mux __iomem *qe_mux_reg; | |
| 636 | 636 | |
| 637 | 637 | qe_mux_reg = &qe_immr->qmx; |
| 638 | 638 | |
| @@ -72,7 +72,7 @@ EXPORT_SYMBOL(ucc_slow_restart_tx); · 71 unmodified lines | ||
| 72 | 72 | |
| 73 | 73 | void ucc_slow_enable(struct ucc_slow_private * uccs, enum comm_dir mode) |
| 74 | 74 | { |
| 75 | − struct ucc_slow *us_regs; | |
| 75 | + struct ucc_slow __iomem *us_regs; | |
| 76 | 76 | u32 gumr_l; |
| 77 | 77 | |
| 78 | 78 | us_regs = uccs->us_regs; |
| @@ -93,7 +93,7 @@ EXPORT_SYMBOL(ucc_slow_enable); · 14 unmodified lines | ||
| 93 | 93 | |
| 94 | 94 | void ucc_slow_disable(struct ucc_slow_private * uccs, enum comm_dir mode) |
| 95 | 95 | { |
| 96 | − struct ucc_slow *us_regs; | |
| 96 | + struct ucc_slow __iomem *us_regs; | |
| 97 | 97 | u32 gumr_l; |
| 98 | 98 | |
| 99 | 99 | us_regs = uccs->us_regs; |
| @@ -122,7 +122,7 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc · 22 unmodified lines | ||
| 122 | 122 | u32 i; |
| 123 | 123 | struct ucc_slow __iomem *us_regs; |
| 124 | 124 | u32 gumr; |
| 125 | − struct qe_bd *bd; | |
| 125 | + struct qe_bd __iomem *bd; | |
| 126 | 126 | u32 id; |
| 127 | 127 | u32 command; |
| 128 | 128 | int ret = 0; |
| @@ -168,16 +168,9 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc · 39 unmodified lines | ||
| 168 | 168 | return -ENOMEM; |
| 169 | 169 | } |
| 170 | 170 | |
| 171 | − uccs->saved_uccm = 0; | |
| 172 | − uccs->p_rx_frame = 0; | |
| 173 | 171 | us_regs = uccs->us_regs; |
| 174 | − uccs->p_ucce = (u16 *) & (us_regs->ucce); | |
| 175 | − uccs->p_uccm = (u16 *) & (us_regs->uccm); | |
| 176 | −#ifdef STATISTICS | |
| 177 | − uccs->rx_frames = 0; | |
| 178 | − uccs->tx_frames = 0; | |
| 179 | − uccs->rx_discarded = 0; | |
| 180 | −#endif /* STATISTICS */ | |
| 172 | + uccs->p_ucce = &us_regs->ucce; | |
| 173 | + uccs->p_uccm = &us_regs->uccm; | |
| 181 | 174 | |
| 182 | 175 | /* Get PRAM base */ |
| 183 | 176 | uccs->us_pram_offset = |
| @@ -231,24 +224,24 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc · 47 unmodified lines | ||
| 231 | 224 | /* clear bd buffer */ |
| 232 | 225 | qe_iowrite32be(0, &bd->buf); |
| 233 | 226 | /* set bd status and length */ |
| 234 | − qe_iowrite32be(0, (u32 *)bd); | |
| 227 | + qe_iowrite32be(0, (u32 __iomem *)bd); | |
| 235 | 228 | bd++; |
| 236 | 229 | } |
| 237 | 230 | /* for last BD set Wrap bit */ |
| 238 | 231 | qe_iowrite32be(0, &bd->buf); |
| 239 | − qe_iowrite32be(cpu_to_be32(T_W), (u32 *)bd); | |
| 232 | + qe_iowrite32be(T_W, (u32 __iomem *)bd); | |
| 240 | 233 | |
| 241 | 234 | /* Init Rx bds */ |
| 242 | 235 | bd = uccs->rx_bd = qe_muram_addr(uccs->rx_base_offset); |
| 243 | 236 | for (i = 0; i < us_info->rx_bd_ring_len - 1; i++) { |
| 244 | 237 | /* set bd status and length */ |
| 245 | − qe_iowrite32be(0, (u32 *)bd); | |
| 238 | + qe_iowrite32be(0, (u32 __iomem *)bd); | |
| 246 | 239 | /* clear bd buffer */ |
| 247 | 240 | qe_iowrite32be(0, &bd->buf); |
| 248 | 241 | bd++; |
| 249 | 242 | } |
| 250 | 243 | /* for last BD set Wrap bit */ |
| 251 | − qe_iowrite32be(cpu_to_be32(R_W), (u32 *)bd); | |
| 244 | + qe_iowrite32be(R_W, (u32 __iomem *)bd); | |
| 252 | 245 | qe_iowrite32be(0, &bd->buf); |
| 253 | 246 | |
| 254 | 247 | /* Set GUMR (For more details see the hardware spec.). */ |
| @@ -273,8 +266,8 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc · 18 unmodified lines | ||
| 273 | 266 | qe_iowrite32be(gumr, &us_regs->gumr_h); |
| 274 | 267 | |
| 275 | 268 | /* gumr_l */ |
| 276 | − gumr = us_info->tdcr | us_info->rdcr | us_info->tenc | us_info->renc | | |
| 277 | − us_info->diag | us_info->mode; | |
| 269 | + gumr = (u32)us_info->tdcr | (u32)us_info->rdcr | (u32)us_info->tenc | | |
| 270 | + (u32)us_info->renc | (u32)us_info->diag | (u32)us_info->mode; | |
| 278 | 271 | if (us_info->tci) |
| 279 | 272 | gumr |= UCC_SLOW_GUMR_L_TCI; |
| 280 | 273 | if (us_info->rinv) |
| @@ -289,8 +282,8 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc · 8 unmodified lines | ||
| 289 | 282 | |
| 290 | 283 | /* if the data is in cachable memory, the 'global' */ |
| 291 | 284 | /* in the function code should be set. */ |
| 292 | − uccs->us_pram->tbmr = UCC_BMR_BO_BE; | |
| 293 | − uccs->us_pram->rbmr = UCC_BMR_BO_BE; | |
| 285 | + qe_iowrite8(UCC_BMR_BO_BE, &uccs->us_pram->tbmr); | |
| 286 | + qe_iowrite8(UCC_BMR_BO_BE, &uccs->us_pram->rbmr); | |
| 294 | 287 | |
| 295 | 288 | /* rbase, tbase are offsets from MURAM base */ |
| 296 | 289 | qe_iowrite16be(uccs->rx_base_offset, &uccs->us_pram->rbase); |
| @@ -10,11 +10,20 @@ config IMX_GPCV2_PM_DOMAINS · 9 unmodified lines | ||
| 10 | 10 | |
| 11 | 11 | config IMX_SCU_SOC |
| 12 | 12 | bool "i.MX System Controller Unit SoC info support" |
| 13 | − depends on IMX_SCU || COMPILE_TEST | |
| 13 | + depends on IMX_SCU | |
| 14 | 14 | select SOC_BUS |
| 15 | 15 | help |
| 16 | 16 | If you say yes here you get support for the NXP i.MX System |
| 17 | 17 | Controller Unit SoC info module, it will provide the SoC info |
| 18 | 18 | like SoC family, ID and revision etc. |
| 19 | 19 | |
| 20 | +config SOC_IMX8M | |
| 21 | + bool "i.MX8M SoC family support" | |
| 22 | + depends on ARCH_MXC || COMPILE_TEST | |
| 23 | + default ARCH_MXC && ARM64 | |
| 24 | + help | |
| 25 | + If you say yes here you get support for the NXP i.MX8M family | |
| 26 | + support, it will provide the SoC info like SoC family, | |
| 27 | + ID and revision etc. | |
| 28 | + | |
| 20 | 29 | endmenu |
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | # SPDX-License-Identifier: GPL-2.0-only |
| 2 | 2 | obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o |
| 3 | 3 | obj-$(CONFIG_IMX_GPCV2_PM_DOMAINS) += gpcv2.o |
| 4 | −obj-$(CONFIG_ARCH_MXC) += soc-imx8.o | |
| 4 | +obj-$(CONFIG_SOC_IMX8M) += soc-imx8m.o | |
| 5 | 5 | obj-$(CONFIG_IMX_SCU_SOC) += soc-imx-scu.o |
| @@ -87,8 +87,8 @@ static int imx6_pm_domain_power_off(struct generic_pm_domain *genpd) · 86 unmodified lines | ||
| 87 | 87 | static int imx6_pm_domain_power_on(struct generic_pm_domain *genpd) |
| 88 | 88 | { |
| 89 | 89 | struct imx_pm_domain *pd = to_imx_pm_domain(genpd); |
| 90 | − int i, ret, sw, sw2iso; | |
| 91 | − u32 val; | |
| 90 | + int i, ret; | |
| 91 | + u32 val, req; | |
| 92 | 92 | |
| 93 | 93 | if (pd->supply) { |
| 94 | 94 | ret = regulator_enable(pd->supply); |
| @@ -107,17 +107,18 @@ static int imx6_pm_domain_power_on(struct generic_pm_domain *genpd) · 12 unmodified lines | ||
| 107 | 107 | regmap_update_bits(pd->regmap, pd->reg_offs + GPC_PGC_CTRL_OFFS, |
| 108 | 108 | 0x1, 0x1); |
| 109 | 109 | |
| 110 | − /* Read ISO and ISO2SW power up delays */ | |
| 111 | − regmap_read(pd->regmap, pd->reg_offs + GPC_PGC_PUPSCR_OFFS, &val); | |
| 112 | − sw = val & 0x3f; | |
| 113 | − sw2iso = (val >> 8) & 0x3f; | |
| 114 | − | |
| 115 | 110 | /* Request GPC to power up domain */ |
| 116 | − val = BIT(pd->cntr_pdn_bit + 1); | |
| 117 | − regmap_update_bits(pd->regmap, GPC_CNTR, val, val); | |
| 111 | + req = BIT(pd->cntr_pdn_bit + 1); | |
| 112 | + regmap_update_bits(pd->regmap, GPC_CNTR, req, req); | |
| 118 | 113 | |
| 119 | − /* Wait ISO + ISO2SW IPG clock cycles */ | |
| 120 | − udelay(DIV_ROUND_UP(sw + sw2iso, pd->ipg_rate_mhz)); | |
| 114 | + /* Wait for the PGC to handle the request */ | |
| 115 | + ret = regmap_read_poll_timeout(pd->regmap, GPC_CNTR, val, !(val & req), | |
| 116 | + 1, 50); | |
| 117 | + if (ret) | |
| 118 | + pr_err("powerup request on domain %s timed out\n", genpd->name); | |
| 119 | + | |
| 120 | + /* Wait for reset to propagate through peripherals */ | |
| 121 | + usleep_range(5, 10); | |
| 121 | 122 | |
| 122 | 123 | /* Disable reset clocks for all devices in the domain */ |
| 123 | 124 | for (i = 0; i < pd->num_clks; i++) |
| @@ -343,6 +344,7 @@ static const struct regmap_config imx_gpc_regmap_config = { · 219 unmodified lines | ||
| 343 | 344 | .rd_table = &access_table, |
| 344 | 345 | .wr_table = &access_table, |
| 345 | 346 | .max_register = 0x2ac, |
| 347 | + .fast_io = true, | |
| 346 | 348 | }; |
| 347 | 349 | |
| 348 | 350 | static struct generic_pm_domain *imx_gpc_onecell_domains[] = { |
| @@ -14,6 +14,7 @@ · 13 unmodified lines | ||
| 14 | 14 | #include <linux/pm_domain.h> |
| 15 | 15 | #include <linux/regmap.h> |
| 16 | 16 | #include <linux/regulator/consumer.h> |
| 17 | +#include <linux/sizes.h> | |
| 17 | 18 | #include <dt-bindings/power/imx7-power.h> |
| 18 | 19 | #include <dt-bindings/power/imx8mq-power.h> |
| 19 | 20 | |
File renamed without changes.
| @@ -76,6 +76,10 @@ config QCOM_OCMEM · 75 unmodified lines | ||
| 76 | 76 | requirements. This is typically used by the GPU, camera/video, and |
| 77 | 77 | audio components on some Snapdragon SoCs. |
| 78 | 78 | |
| 79 | +config QCOM_PDR_HELPERS | |
| 80 | + tristate | |
| 81 | + select QCOM_QMI_HELPERS | |
| 82 | + | |
| 79 | 83 | config QCOM_PM |
| 80 | 84 | bool "Qualcomm Power Management" |
| 81 | 85 | depends on ARCH_QCOM && !ARM64 |
| @@ -88,7 +92,6 @@ config QCOM_PM · 6 unmodified lines | ||
| 88 | 92 | |
| 89 | 93 | config QCOM_QMI_HELPERS |
| 90 | 94 | tristate |
| 91 | − depends on ARCH_QCOM || COMPILE_TEST | |
| 92 | 95 | depends on NET |
| 93 | 96 | |
| 94 | 97 | config QCOM_RMTFS_MEM |
| @@ -197,6 +200,8 @@ config QCOM_APR · 102 unmodified lines | ||
| 197 | 200 | tristate "Qualcomm APR Bus (Asynchronous Packet Router)" |
| 198 | 201 | depends on ARCH_QCOM || COMPILE_TEST |
| 199 | 202 | depends on RPMSG |
| 203 | + depends on NET | |
| 204 | + select QCOM_PDR_HELPERS | |
| 200 | 205 | help |
| 201 | 206 | Enable APR IPC protocol support between |
| 202 | 207 | application processor and QDSP6. APR is |
| @@ -7,6 +7,7 @@ obj-$(CONFIG_QCOM_GLINK_SSR) += glink_ssr.o · 6 unmodified lines | ||
| 7 | 7 | obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o |
| 8 | 8 | obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o |
| 9 | 9 | obj-$(CONFIG_QCOM_OCMEM) += ocmem.o |
| 10 | +obj-$(CONFIG_QCOM_PDR_HELPERS) += pdr_interface.o | |
| 10 | 11 | obj-$(CONFIG_QCOM_PM) += spm.o |
| 11 | 12 | obj-$(CONFIG_QCOM_QMI_HELPERS) += qmi_helpers.o |
| 12 | 13 | qmi_helpers-y += qmi_encdec.o qmi_interface.o |
| @@ -11,6 +11,7 @@ · 10 unmodified lines | ||
| 11 | 11 | #include <linux/workqueue.h> |
| 12 | 12 | #include <linux/of_device.h> |
| 13 | 13 | #include <linux/soc/qcom/apr.h> |
| 14 | +#include <linux/soc/qcom/pdr.h> | |
| 14 | 15 | #include <linux/rpmsg.h> |
| 15 | 16 | #include <linux/of.h> |
| 16 | 17 | |
| @@ -21,6 +22,7 @@ struct apr { · 4 unmodified lines | ||
| 21 | 22 | spinlock_t rx_lock; |
| 22 | 23 | struct idr svcs_idr; |
| 23 | 24 | int dest_domain_id; |
| 25 | + struct pdr_handle *pdr; | |
| 24 | 26 | struct workqueue_struct *rxwq; |
| 25 | 27 | struct work_struct rx_work; |
| 26 | 28 | struct list_head rx_list; |
| @@ -289,6 +291,9 @@ static int apr_add_device(struct device *dev, struct device_node *np, · 262 unmodified lines | ||
| 289 | 291 | id->svc_id + 1, GFP_ATOMIC); |
| 290 | 292 | spin_unlock(&apr->svcs_lock); |
| 291 | 293 | |
| 294 | + of_property_read_string_index(np, "qcom,protection-domain", | |
| 295 | + 1, &adev->service_path); | |
| 296 | + | |
| 292 | 297 | dev_info(dev, "Adding APR dev: %s\n", dev_name(&adev->dev)); |
| 293 | 298 | |
| 294 | 299 | ret = device_register(&adev->dev); |
| @@ -300,14 +305,75 @@ static int apr_add_device(struct device *dev, struct device_node *np, · 5 unmodified lines | ||
| 300 | 305 | return ret; |
| 301 | 306 | } |
| 302 | 307 | |
| 303 | −static void of_register_apr_devices(struct device *dev) | |
| 308 | +static int of_apr_add_pd_lookups(struct device *dev) | |
| 309 | +{ | |
| 310 | + const char *service_name, *service_path; | |
| 311 | + struct apr *apr = dev_get_drvdata(dev); | |
| 312 | + struct device_node *node; | |
| 313 | + struct pdr_service *pds; | |
| 314 | + int ret; | |
| 315 | + | |
| 316 | + for_each_child_of_node(dev->of_node, node) { | |
| 317 | + ret = of_property_read_string_index(node, "qcom,protection-domain", | |
| 318 | + 0, &service_name); | |
| 319 | + if (ret < 0) | |
| 320 | + continue; | |
| 321 | + | |
| 322 | + ret = of_property_read_string_index(node, "qcom,protection-domain", | |
| 323 | + 1, &service_path); | |
| 324 | + if (ret < 0) { | |
| 325 | + dev_err(dev, "pdr service path missing: %d\n", ret); | |
| 326 | + return ret; | |
| 327 | + } | |
| 328 | + | |
| 329 | + pds = pdr_add_lookup(apr->pdr, service_name, service_path); | |
| 330 | + if (IS_ERR(pds) && PTR_ERR(pds) != -EALREADY) { | |
| 331 | + dev_err(dev, "pdr add lookup failed: %d\n", ret); | |
| 332 | + return PTR_ERR(pds); | |
| 333 | + } | |
| 334 | + } | |
| 335 | + | |
| 336 | + return 0; | |
| 337 | +} | |
| 338 | + | |
| 339 | +static void of_register_apr_devices(struct device *dev, const char *svc_path) | |
| 304 | 340 | { |
| 305 | 341 | struct apr *apr = dev_get_drvdata(dev); |
| 306 | 342 | struct device_node *node; |
| 343 | + const char *service_path; | |
| 344 | + int ret; | |
| 307 | 345 | |
| 308 | 346 | for_each_child_of_node(dev->of_node, node) { |
| 309 | 347 | struct apr_device_id id = { {0} }; |
| 310 | 348 | |
| 349 | + /* | |
| 350 | + * This function is called with svc_path NULL during | |
| 351 | + * apr_probe(), in which case we register any apr devices | |
| 352 | + * without a qcom,protection-domain specified. | |
| 353 | + * | |
| 354 | + * Then as the protection domains becomes available | |
| 355 | + * (if applicable) this function is again called, but with | |
| 356 | + * svc_path representing the service becoming available. In | |
| 357 | + * this case we register any apr devices with a matching | |
| 358 | + * qcom,protection-domain. | |
| 359 | + */ | |
| 360 | + | |
| 361 | + ret = of_property_read_string_index(node, "qcom,protection-domain", | |
| 362 | + 1, &service_path); | |
| 363 | + if (svc_path) { | |
| 364 | + /* skip APR services that are PD independent */ | |
| 365 | + if (ret) | |
| 366 | + continue; | |
| 367 | + | |
| 368 | + /* skip APR services whose PD paths don't match */ | |
| 369 | + if (strcmp(service_path, svc_path)) | |
| 370 | + continue; | |
| 371 | + } else { | |
| 372 | + /* skip APR services whose PD lookups are registered */ | |
| 373 | + if (ret == 0) | |
| 374 | + continue; | |
| 375 | + } | |
| 376 | + | |
| 311 | 377 | if (of_property_read_u32(node, "reg", &id.svc_id)) |
| 312 | 378 | continue; |
| 313 | 379 | |
| @@ -318,6 +384,34 @@ static void of_register_apr_devices(struct device *dev) · 4 unmodified lines | ||
| 318 | 384 | } |
| 319 | 385 | } |
| 320 | 386 | |
| 387 | +static int apr_remove_device(struct device *dev, void *svc_path) | |
| 388 | +{ | |
| 389 | + struct apr_device *adev = to_apr_device(dev); | |
| 390 | + | |
| 391 | + if (svc_path && adev->service_path) { | |
| 392 | + if (!strcmp(adev->service_path, (char *)svc_path)) | |
| 393 | + device_unregister(&adev->dev); | |
| 394 | + } else { | |
| 395 | + device_unregister(&adev->dev); | |
| 396 | + } | |
| 397 | + | |
| 398 | + return 0; | |
| 399 | +} | |
| 400 | + | |
| 401 | +static void apr_pd_status(int state, char *svc_path, void *priv) | |
| 402 | +{ | |
| 403 | + struct apr *apr = (struct apr *)priv; | |
| 404 | + | |
| 405 | + switch (state) { | |
| 406 | + case SERVREG_SERVICE_STATE_UP: | |
| 407 | + of_register_apr_devices(apr->dev, svc_path); | |
| 408 | + break; | |
| 409 | + case SERVREG_SERVICE_STATE_DOWN: | |
| 410 | + device_for_each_child(apr->dev, svc_path, apr_remove_device); | |
| 411 | + break; | |
| 412 | + } | |
| 413 | +} | |
| 414 | + | |
| 321 | 415 | static int apr_probe(struct rpmsg_device *rpdev) |
| 322 | 416 | { |
| 323 | 417 | struct device *dev = &rpdev->dev; |
| @@ -343,28 +437,39 @@ static int apr_probe(struct rpmsg_device *rpdev) · 19 unmodified lines | ||
| 343 | 437 | return -ENOMEM; |
| 344 | 438 | } |
| 345 | 439 | INIT_WORK(&apr->rx_work, apr_rxwq); |
| 440 | + | |
| 441 | + apr->pdr = pdr_handle_alloc(apr_pd_status, apr); | |
| 442 | + if (IS_ERR(apr->pdr)) { | |
| 443 | + dev_err(dev, "Failed to init PDR handle\n"); | |
| 444 | + ret = PTR_ERR(apr->pdr); | |
| 445 | + goto destroy_wq; | |
| 446 | + } | |
| 447 | + | |
| 346 | 448 | INIT_LIST_HEAD(&apr->rx_list); |
| 347 | 449 | spin_lock_init(&apr->rx_lock); |
| 348 | 450 | spin_lock_init(&apr->svcs_lock); |
| 349 | 451 | idr_init(&apr->svcs_idr); |
| 350 | − of_register_apr_devices(dev); | |
| 351 | − | |
| 352 | − return 0; | |
| 353 | −} | |
| 354 | 452 | |
| 355 | −static int apr_remove_device(struct device *dev, void *null) | |
| 356 | −{ | |
| 357 | − struct apr_device *adev = to_apr_device(dev); | |
| 453 | + ret = of_apr_add_pd_lookups(dev); | |
| 454 | + if (ret) | |
| 455 | + goto handle_release; | |
| 358 | 456 | |
| 359 | − device_unregister(&adev->dev); | |
| 457 | + of_register_apr_devices(dev, NULL); | |
| 360 | 458 | |
| 361 | 459 | return 0; |
| 460 | + | |
| 461 | +handle_release: | |
| 462 | + pdr_handle_release(apr->pdr); | |
| 463 | +destroy_wq: | |
| 464 | + destroy_workqueue(apr->rxwq); | |
| 465 | + return ret; | |
| 362 | 466 | } |
| 363 | 467 | |
| 364 | 468 | static void apr_remove(struct rpmsg_device *rpdev) |
| 365 | 469 | { |
| 366 | 470 | struct apr *apr = dev_get_drvdata(&rpdev->dev); |
| 367 | 471 | |
| 472 | + pdr_handle_release(apr->pdr); | |
| 368 | 473 | device_for_each_child(&rpdev->dev, NULL, apr_remove_device); |
| 369 | 474 | flush_workqueue(apr->rxwq); |
| 370 | 475 | destroy_workqueue(apr->rxwq); |
| @@ -0,0 +1,757 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* | |
| 3 | + * Copyright (C) 2020 The Linux Foundation. All rights reserved. | |
| 4 | + */ | |
| 5 | + | |
| 6 | +#include <linux/kernel.h> | |
| 7 | +#include <linux/module.h> | |
| 8 | +#include <linux/string.h> | |
| 9 | +#include <linux/workqueue.h> | |
| 10 | + | |
| 11 | +#include "pdr_internal.h" | |
| 12 | + | |
| 13 | +struct pdr_service { | |
| 14 | + char service_name[SERVREG_NAME_LENGTH + 1]; | |
| 15 | + char service_path[SERVREG_NAME_LENGTH + 1]; | |
| 16 | + | |
| 17 | + struct sockaddr_qrtr addr; | |
| 18 | + | |
| 19 | + unsigned int instance; | |
| 20 | + unsigned int service; | |
| 21 | + u8 service_data_valid; | |
| 22 | + u32 service_data; | |
| 23 | + int state; | |
| 24 | + | |
| 25 | + bool need_notifier_register; | |
| 26 | + bool need_notifier_remove; | |
| 27 | + bool need_locator_lookup; | |
| 28 | + bool service_connected; | |
| 29 | + | |
| 30 | + struct list_head node; | |
| 31 | +}; | |
| 32 | + | |
| 33 | +struct pdr_handle { | |
| 34 | + struct qmi_handle locator_hdl; | |
| 35 | + struct qmi_handle notifier_hdl; | |
| 36 | + | |
| 37 | + struct sockaddr_qrtr locator_addr; | |
| 38 | + | |
| 39 | + struct list_head lookups; | |
| 40 | + struct list_head indack_list; | |
| 41 | + | |
| 42 | + /* control access to pdr lookup/indack lists */ | |
| 43 | + struct mutex list_lock; | |
| 44 | + | |
| 45 | + /* serialize pd status invocation */ | |
| 46 | + struct mutex status_lock; | |
| 47 | + | |
| 48 | + /* control access to the locator state */ | |
| 49 | + struct mutex lock; | |
| 50 | + | |
| 51 | + bool locator_init_complete; | |
| 52 | + | |
| 53 | + struct work_struct locator_work; | |
| 54 | + struct work_struct notifier_work; | |
| 55 | + struct work_struct indack_work; | |
| 56 | + | |
| 57 | + struct workqueue_struct *notifier_wq; | |
| 58 | + struct workqueue_struct *indack_wq; | |
| 59 | + | |
| 60 | + void (*status)(int state, char *service_path, void *priv); | |
| 61 | + void *priv; | |
| 62 | +}; | |
| 63 | + | |
| 64 | +struct pdr_list_node { | |
| 65 | + enum servreg_service_state curr_state; | |
| 66 | + u16 transaction_id; | |
| 67 | + struct pdr_service *pds; | |
| 68 | + struct list_head node; | |
| 69 | +}; | |
| 70 | + | |
| 71 | +static int pdr_locator_new_server(struct qmi_handle *qmi, | |
| 72 | + struct qmi_service *svc) | |
| 73 | +{ | |
| 74 | + struct pdr_handle *pdr = container_of(qmi, struct pdr_handle, | |
| 75 | + locator_hdl); | |
| 76 | + struct pdr_service *pds; | |
| 77 | + | |
| 78 | + /* Create a local client port for QMI communication */ | |
| 79 | + pdr->locator_addr.sq_family = AF_QIPCRTR; | |
| 80 | + pdr->locator_addr.sq_node = svc->node; | |
| 81 | + pdr->locator_addr.sq_port = svc->port; | |
| 82 | + | |
| 83 | + mutex_lock(&pdr->lock); | |
| 84 | + pdr->locator_init_complete = true; | |
| 85 | + mutex_unlock(&pdr->lock); | |
| 86 | + | |
| 87 | + /* Service pending lookup requests */ | |
| 88 | + mutex_lock(&pdr->list_lock); | |
| 89 | + list_for_each_entry(pds, &pdr->lookups, node) { | |
| 90 | + if (pds->need_locator_lookup) | |
| 91 | + schedule_work(&pdr->locator_work); | |
| 92 | + } | |
| 93 | + mutex_unlock(&pdr->list_lock); | |
| 94 | + | |
| 95 | + return 0; | |
| 96 | +} | |
| 97 | + | |
| 98 | +static void pdr_locator_del_server(struct qmi_handle *qmi, | |
| 99 | + struct qmi_service *svc) | |
| 100 | +{ | |
| 101 | + struct pdr_handle *pdr = container_of(qmi, struct pdr_handle, | |
| 102 | + locator_hdl); | |
| 103 | + | |
| 104 | + mutex_lock(&pdr->lock); | |
| 105 | + pdr->locator_init_complete = false; | |
| 106 | + mutex_unlock(&pdr->lock); | |
| 107 | + | |
| 108 | + pdr->locator_addr.sq_node = 0; | |
| 109 | + pdr->locator_addr.sq_port = 0; | |
| 110 | +} | |
| 111 | + | |
| 112 | +static struct qmi_ops pdr_locator_ops = { | |
| 113 | + .new_server = pdr_locator_new_server, | |
| 114 | + .del_server = pdr_locator_del_server, | |
| 115 | +}; | |
| 116 | + | |
| 117 | +static int pdr_register_listener(struct pdr_handle *pdr, | |
| 118 | + struct pdr_service *pds, | |
| 119 | + bool enable) | |
| 120 | +{ | |
| 121 | + struct servreg_register_listener_resp resp; | |
| 122 | + struct servreg_register_listener_req req; | |
| 123 | + struct qmi_txn txn; | |
| 124 | + int ret; | |
| 125 | + | |
| 126 | + ret = qmi_txn_init(&pdr->notifier_hdl, &txn, | |
| 127 | + servreg_register_listener_resp_ei, | |
| 128 | + &resp); | |
| 129 | + if (ret < 0) | |
| 130 | + return ret; | |
| 131 | + | |
| 132 | + req.enable = enable; | |
| 133 | + strcpy(req.service_path, pds->service_path); | |
| 134 | + | |
| 135 | + ret = qmi_send_request(&pdr->notifier_hdl, &pds->addr, | |
| 136 | + &txn, SERVREG_REGISTER_LISTENER_REQ, | |
| 137 | + SERVREG_REGISTER_LISTENER_REQ_LEN, | |
| 138 | + servreg_register_listener_req_ei, | |
| 139 | + &req); | |
| 140 | + if (ret < 0) { | |
| 141 | + qmi_txn_cancel(&txn); | |
| 142 | + return ret; | |
| 143 | + } | |
| 144 | + | |
| 145 | + ret = qmi_txn_wait(&txn, 5 * HZ); | |
| 146 | + if (ret < 0) { | |
| 147 | + pr_err("PDR: %s register listener txn wait failed: %d\n", | |
| 148 | + pds->service_path, ret); | |
| 149 | + return ret; | |
| 150 | + } | |
| 151 | + | |
| 152 | + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { | |
| 153 | + pr_err("PDR: %s register listener failed: 0x%x\n", | |
| 154 | + pds->service_path, resp.resp.error); | |
| 155 | + return ret; | |
| 156 | + } | |
| 157 | + | |
| 158 | + if ((int)resp.curr_state < INT_MIN || (int)resp.curr_state > INT_MAX) | |
| 159 | + pr_err("PDR: %s notification state invalid: 0x%x\n", | |
| 160 | + pds->service_path, resp.curr_state); | |
| 161 | + | |
| 162 | + pds->state = resp.curr_state; | |
| 163 | + | |
| 164 | + return 0; | |
| 165 | +} | |
| 166 | + | |
| 167 | +static void pdr_notifier_work(struct work_struct *work) | |
| 168 | +{ | |
| 169 | + struct pdr_handle *pdr = container_of(work, struct pdr_handle, | |
| 170 | + notifier_work); | |
| 171 | + struct pdr_service *pds; | |
| 172 | + int ret; | |
| 173 | + | |
| 174 | + mutex_lock(&pdr->list_lock); | |
| 175 | + list_for_each_entry(pds, &pdr->lookups, node) { | |
| 176 | + if (pds->service_connected) { | |
| 177 | + if (!pds->need_notifier_register) | |
| 178 | + continue; | |
| 179 | + | |
| 180 | + pds->need_notifier_register = false; | |
| 181 | + ret = pdr_register_listener(pdr, pds, true); | |
| 182 | + if (ret < 0) | |
| 183 | + pds->state = SERVREG_SERVICE_STATE_DOWN; | |
| 184 | + } else { | |
| 185 | + if (!pds->need_notifier_remove) | |
| 186 | + continue; | |
| 187 | + | |
| 188 | + pds->need_notifier_remove = false; | |
| 189 | + pds->state = SERVREG_SERVICE_STATE_DOWN; | |
| 190 | + } | |
| 191 | + | |
| 192 | + mutex_lock(&pdr->status_lock); | |
| 193 | + pdr->status(pds->state, pds->service_path, pdr->priv); | |
| 194 | + mutex_unlock(&pdr->status_lock); | |
| 195 | + } | |
| 196 | + mutex_unlock(&pdr->list_lock); | |
| 197 | +} | |
| 198 | + | |
| 199 | +static int pdr_notifier_new_server(struct qmi_handle *qmi, | |
| 200 | + struct qmi_service *svc) | |
| 201 | +{ | |
| 202 | + struct pdr_handle *pdr = container_of(qmi, struct pdr_handle, | |
| 203 | + notifier_hdl); | |
| 204 | + struct pdr_service *pds; | |
| 205 | + | |
| 206 | + mutex_lock(&pdr->list_lock); | |
| 207 | + list_for_each_entry(pds, &pdr->lookups, node) { | |
| 208 | + if (pds->service == svc->service && | |
| 209 | + pds->instance == svc->instance) { | |
| 210 | + pds->service_connected = true; | |
| 211 | + pds->need_notifier_register = true; | |
| 212 | + pds->addr.sq_family = AF_QIPCRTR; | |
| 213 | + pds->addr.sq_node = svc->node; | |
| 214 | + pds->addr.sq_port = svc->port; | |
| 215 | + queue_work(pdr->notifier_wq, &pdr->notifier_work); | |
| 216 | + } | |
| 217 | + } | |
| 218 | + mutex_unlock(&pdr->list_lock); | |
| 219 | + | |
| 220 | + return 0; | |
| 221 | +} | |
| 222 | + | |
| 223 | +static void pdr_notifier_del_server(struct qmi_handle *qmi, | |
| 224 | + struct qmi_service *svc) | |
| 225 | +{ | |
| 226 | + struct pdr_handle *pdr = container_of(qmi, struct pdr_handle, | |
| 227 | + notifier_hdl); | |
| 228 | + struct pdr_service *pds; | |
| 229 | + | |
| 230 | + mutex_lock(&pdr->list_lock); | |
| 231 | + list_for_each_entry(pds, &pdr->lookups, node) { | |
| 232 | + if (pds->service == svc->service && | |
| 233 | + pds->instance == svc->instance) { | |
| 234 | + pds->service_connected = false; | |
| 235 | + pds->need_notifier_remove = true; | |
| 236 | + pds->addr.sq_node = 0; | |
| 237 | + pds->addr.sq_port = 0; | |
| 238 | + queue_work(pdr->notifier_wq, &pdr->notifier_work); | |
| 239 | + } | |
| 240 | + } | |
| 241 | + mutex_unlock(&pdr->list_lock); | |
| 242 | +} | |
| 243 | + | |
| 244 | +static struct qmi_ops pdr_notifier_ops = { | |
| 245 | + .new_server = pdr_notifier_new_server, | |
| 246 | + .del_server = pdr_notifier_del_server, | |
| 247 | +}; | |
| 248 | + | |
| 249 | +static int pdr_send_indack_msg(struct pdr_handle *pdr, struct pdr_service *pds, | |
| 250 | + u16 tid) | |
| 251 | +{ | |
| 252 | + struct servreg_set_ack_resp resp; | |
| 253 | + struct servreg_set_ack_req req; | |
| 254 | + struct qmi_txn txn; | |
| 255 | + int ret; | |
| 256 | + | |
| 257 | + ret = qmi_txn_init(&pdr->notifier_hdl, &txn, servreg_set_ack_resp_ei, | |
| 258 | + &resp); | |
| 259 | + if (ret < 0) | |
| 260 | + return ret; | |
| 261 | + | |
| 262 | + req.transaction_id = tid; | |
| 263 | + strcpy(req.service_path, pds->service_path); | |
| 264 | + | |
| 265 | + ret = qmi_send_request(&pdr->notifier_hdl, &pds->addr, | |
| 266 | + &txn, SERVREG_SET_ACK_REQ, | |
| 267 | + SERVREG_SET_ACK_REQ_LEN, | |
| 268 | + servreg_set_ack_req_ei, | |
| 269 | + &req); | |
| 270 | + | |
| 271 | + /* Skip waiting for response */ | |
| 272 | + qmi_txn_cancel(&txn); | |
| 273 | + return ret; | |
| 274 | +} | |
| 275 | + | |
| 276 | +static void pdr_indack_work(struct work_struct *work) | |
| 277 | +{ | |
| 278 | + struct pdr_handle *pdr = container_of(work, struct pdr_handle, | |
| 279 | + indack_work); | |
| 280 | + struct pdr_list_node *ind, *tmp; | |
| 281 | + struct pdr_service *pds; | |
| 282 | + | |
| 283 | + list_for_each_entry_safe(ind, tmp, &pdr->indack_list, node) { | |
| 284 | + pds = ind->pds; | |
| 285 | + pdr_send_indack_msg(pdr, pds, ind->transaction_id); | |
| 286 | + | |
| 287 | + mutex_lock(&pdr->status_lock); | |
| 288 | + pds->state = ind->curr_state; | |
| 289 | + pdr->status(pds->state, pds->service_path, pdr->priv); | |
| 290 | + mutex_unlock(&pdr->status_lock); | |
| 291 | + | |
| 292 | + mutex_lock(&pdr->list_lock); | |
| 293 | + list_del(&ind->node); | |
| 294 | + mutex_unlock(&pdr->list_lock); | |
| 295 | + | |
| 296 | + kfree(ind); | |
| 297 | + } | |
| 298 | +} | |
| 299 | + | |
| 300 | +static void pdr_indication_cb(struct qmi_handle *qmi, | |
| 301 | + struct sockaddr_qrtr *sq, | |
| 302 | + struct qmi_txn *txn, const void *data) | |
| 303 | +{ | |
| 304 | + struct pdr_handle *pdr = container_of(qmi, struct pdr_handle, | |
| 305 | + notifier_hdl); | |
| 306 | + const struct servreg_state_updated_ind *ind_msg = data; | |
| 307 | + struct pdr_list_node *ind; | |
| 308 | + struct pdr_service *pds; | |
| 309 | + bool found = false; | |
| 310 | + | |
| 311 | + if (!ind_msg || !ind_msg->service_path[0] || | |
| 312 | + strlen(ind_msg->service_path) > SERVREG_NAME_LENGTH) | |
| 313 | + return; | |
| 314 | + | |
| 315 | + mutex_lock(&pdr->list_lock); | |
| 316 | + list_for_each_entry(pds, &pdr->lookups, node) { | |
| 317 | + if (strcmp(pds->service_path, ind_msg->service_path)) | |
| 318 | + continue; | |
| 319 | + | |
| 320 | + found = true; | |
| 321 | + break; | |
| 322 | + } | |
| 323 | + mutex_unlock(&pdr->list_lock); | |
| 324 | + | |
| 325 | + if (!found) | |
| 326 | + return; | |
| 327 | + | |
| 328 | + pr_info("PDR: Indication received from %s, state: 0x%x, trans-id: %d\n", | |
| 329 | + ind_msg->service_path, ind_msg->curr_state, | |
| 330 | + ind_msg->transaction_id); | |
| 331 | + | |
| 332 | + ind = kzalloc(sizeof(*ind), GFP_KERNEL); | |
| 333 | + if (!ind) | |
| 334 | + return; | |
| 335 | + | |
| 336 | + ind->transaction_id = ind_msg->transaction_id; | |
| 337 | + ind->curr_state = ind_msg->curr_state; | |
| 338 | + ind->pds = pds; | |
| 339 | + | |
| 340 | + mutex_lock(&pdr->list_lock); | |
| 341 | + list_add_tail(&ind->node, &pdr->indack_list); | |
| 342 | + mutex_unlock(&pdr->list_lock); | |
| 343 | + | |
| 344 | + queue_work(pdr->indack_wq, &pdr->indack_work); | |
| 345 | +} | |
| 346 | + | |
| 347 | +static struct qmi_msg_handler qmi_indication_handler[] = { | |
| 348 | + { | |
| 349 | + .type = QMI_INDICATION, | |
| 350 | + .msg_id = SERVREG_STATE_UPDATED_IND_ID, | |
| 351 | + .ei = servreg_state_updated_ind_ei, | |
| 352 | + .decoded_size = sizeof(struct servreg_state_updated_ind), | |
| 353 | + .fn = pdr_indication_cb, | |
| 354 | + }, | |
| 355 | + {} | |
| 356 | +}; | |
| 357 | + | |
| 358 | +static int pdr_get_domain_list(struct servreg_get_domain_list_req *req, | |
| 359 | + struct servreg_get_domain_list_resp *resp, | |
| 360 | + struct pdr_handle *pdr) | |
| 361 | +{ | |
| 362 | + struct qmi_txn txn; | |
| 363 | + int ret; | |
| 364 | + | |
| 365 | + ret = qmi_txn_init(&pdr->locator_hdl, &txn, | |
| 366 | + servreg_get_domain_list_resp_ei, resp); | |
| 367 | + if (ret < 0) | |
| 368 | + return ret; | |
| 369 | + | |
| 370 | + ret = qmi_send_request(&pdr->locator_hdl, | |
| 371 | + &pdr->locator_addr, | |
| 372 | + &txn, SERVREG_GET_DOMAIN_LIST_REQ, | |
| 373 | + SERVREG_GET_DOMAIN_LIST_REQ_MAX_LEN, | |
| 374 | + servreg_get_domain_list_req_ei, | |
| 375 | + req); | |
| 376 | + if (ret < 0) { | |
| 377 | + qmi_txn_cancel(&txn); | |
| 378 | + return ret; | |
| 379 | + } | |
| 380 | + | |
| 381 | + ret = qmi_txn_wait(&txn, 5 * HZ); | |
| 382 | + if (ret < 0) { | |
| 383 | + pr_err("PDR: %s get domain list txn wait failed: %d\n", | |
| 384 | + req->service_name, ret); | |
| 385 | + return ret; | |
| 386 | + } | |
| 387 | + | |
| 388 | + if (resp->resp.result != QMI_RESULT_SUCCESS_V01) { | |
| 389 | + pr_err("PDR: %s get domain list failed: 0x%x\n", | |
| 390 | + req->service_name, resp->resp.error); | |
| 391 | + return -EREMOTEIO; | |
| 392 | + } | |
| 393 | + | |
| 394 | + return 0; | |
| 395 | +} | |
| 396 | + | |
| 397 | +static int pdr_locate_service(struct pdr_handle *pdr, struct pdr_service *pds) | |
| 398 | +{ | |
| 399 | + struct servreg_get_domain_list_resp *resp; | |
| 400 | + struct servreg_get_domain_list_req req; | |
| 401 | + struct servreg_location_entry *entry; | |
| 402 | + int domains_read = 0; | |
| 403 | + int ret, i; | |
| 404 | + | |
| 405 | + resp = kzalloc(sizeof(*resp), GFP_KERNEL); | |
| 406 | + if (!resp) | |
| 407 | + return -ENOMEM; | |
| 408 | + | |
| 409 | + /* Prepare req message */ | |
| 410 | + strcpy(req.service_name, pds->service_name); | |
| 411 | + req.domain_offset_valid = true; | |
| 412 | + req.domain_offset = 0; | |
| 413 | + | |
| 414 | + do { | |
| 415 | + req.domain_offset = domains_read; | |
| 416 | + ret = pdr_get_domain_list(&req, resp, pdr); | |
| 417 | + if (ret < 0) | |
| 418 | + goto out; | |
| 419 | + | |
| 420 | + for (i = domains_read; i < resp->domain_list_len; i++) { | |
| 421 | + entry = &resp->domain_list[i]; | |
| 422 | + | |
| 423 | + if (strnlen(entry->name, sizeof(entry->name)) == sizeof(entry->name)) | |
| 424 | + continue; | |
| 425 | + | |
| 426 | + if (!strcmp(entry->name, pds->service_path)) { | |
| 427 | + pds->service_data_valid = entry->service_data_valid; | |
| 428 | + pds->service_data = entry->service_data; | |
| 429 | + pds->instance = entry->instance; | |
| 430 | + goto out; | |
| 431 | + } | |
| 432 | + } | |
| 433 | + | |
| 434 | + /* Update ret to indicate that the service is not yet found */ | |
| 435 | + ret = -ENXIO; | |
| 436 | + | |
| 437 | + /* Always read total_domains from the response msg */ | |
| 438 | + if (resp->domain_list_len > resp->total_domains) | |
| 439 | + resp->domain_list_len = resp->total_domains; | |
| 440 | + | |
| 441 | + domains_read += resp->domain_list_len; | |
| 442 | + } while (domains_read < resp->total_domains); | |
| 443 | +out: | |
| 444 | + kfree(resp); | |
| 445 | + return ret; | |
| 446 | +} | |
| 447 | + | |
| 448 | +static void pdr_notify_lookup_failure(struct pdr_handle *pdr, | |
| 449 | + struct pdr_service *pds, | |
| 450 | + int err) | |
| 451 | +{ | |
| 452 | + pr_err("PDR: service lookup for %s failed: %d\n", | |
| 453 | + pds->service_name, err); | |
| 454 | + | |
| 455 | + if (err == -ENXIO) | |
| 456 | + return; | |
| 457 | + | |
| 458 | + list_del(&pds->node); | |
| 459 | + pds->state = SERVREG_LOCATOR_ERR; | |
| 460 | + mutex_lock(&pdr->status_lock); | |
| 461 | + pdr->status(pds->state, pds->service_path, pdr->priv); | |
| 462 | + mutex_unlock(&pdr->status_lock); | |
| 463 | + kfree(pds); | |
| 464 | +} | |
| 465 | + | |
| 466 | +static void pdr_locator_work(struct work_struct *work) | |
| 467 | +{ | |
| 468 | + struct pdr_handle *pdr = container_of(work, struct pdr_handle, | |
| 469 | + locator_work); | |
| 470 | + struct pdr_service *pds, *tmp; | |
| 471 | + int ret = 0; | |
| 472 | + | |
| 473 | + /* Bail out early if the SERVREG LOCATOR QMI service is not up */ | |
| 474 | + mutex_lock(&pdr->lock); | |
| 475 | + if (!pdr->locator_init_complete) { | |
| 476 | + mutex_unlock(&pdr->lock); | |
| 477 | + pr_debug("PDR: SERVICE LOCATOR service not available\n"); | |
| 478 | + return; | |
| 479 | + } | |
| 480 | + mutex_unlock(&pdr->lock); | |
| 481 | + | |
| 482 | + mutex_lock(&pdr->list_lock); | |
| 483 | + list_for_each_entry_safe(pds, tmp, &pdr->lookups, node) { | |
| 484 | + if (!pds->need_locator_lookup) | |
| 485 | + continue; | |
| 486 | + | |
| 487 | + ret = pdr_locate_service(pdr, pds); | |
| 488 | + if (ret < 0) { | |
| 489 | + pdr_notify_lookup_failure(pdr, pds, ret); | |
| 490 | + continue; | |
| 491 | + } | |
| 492 | + | |
| 493 | + ret = qmi_add_lookup(&pdr->notifier_hdl, pds->service, 1, | |
| 494 | + pds->instance); | |
| 495 | + if (ret < 0) { | |
| 496 | + pdr_notify_lookup_failure(pdr, pds, ret); | |
| 497 | + continue; | |
| 498 | + } | |
| 499 | + | |
| 500 | + pds->need_locator_lookup = false; | |
| 501 | + } | |
| 502 | + mutex_unlock(&pdr->list_lock); | |
| 503 | +} | |
| 504 | + | |
| 505 | +/** | |
| 506 | + * pdr_add_lookup() - register a tracking request for a PD | |
| 507 | + * @pdr: PDR client handle | |
| 508 | + * @service_name: service name of the tracking request | |
| 509 | + * @service_path: service path of the tracking request | |
| 510 | + * | |
| 511 | + * Registering a pdr lookup allows for tracking the life cycle of the PD. | |
| 512 | + * | |
| 513 | + * Return: pdr_service object on success, ERR_PTR on failure. -EALREADY is | |
| 514 | + * returned if a lookup is already in progress for the given service path. | |
| 515 | + */ | |
| 516 | +struct pdr_service *pdr_add_lookup(struct pdr_handle *pdr, | |
| 517 | + const char *service_name, | |
| 518 | + const char *service_path) | |
| 519 | +{ | |
| 520 | + struct pdr_service *pds, *tmp; | |
| 521 | + int ret; | |
| 522 | + | |
| 523 | + if (IS_ERR_OR_NULL(pdr)) | |
| 524 | + return ERR_PTR(-EINVAL); | |
| 525 | + | |
| 526 | + if (!service_name || strlen(service_name) > SERVREG_NAME_LENGTH || | |
| 527 | + !service_path || strlen(service_path) > SERVREG_NAME_LENGTH) | |
| 528 | + return ERR_PTR(-EINVAL); | |
| 529 | + | |
| 530 | + pds = kzalloc(sizeof(*pds), GFP_KERNEL); | |
| 531 | + if (!pds) | |
| 532 | + return ERR_PTR(-ENOMEM); | |
| 533 | + | |
| 534 | + pds->service = SERVREG_NOTIFIER_SERVICE; | |
| 535 | + strcpy(pds->service_name, service_name); | |
| 536 | + strcpy(pds->service_path, service_path); | |
| 537 | + pds->need_locator_lookup = true; | |
| 538 | + | |
| 539 | + mutex_lock(&pdr->list_lock); | |
| 540 | + list_for_each_entry(tmp, &pdr->lookups, node) { | |
| 541 | + if (strcmp(tmp->service_path, service_path)) | |
| 542 | + continue; | |
| 543 | + | |
| 544 | + mutex_unlock(&pdr->list_lock); | |
| 545 | + ret = -EALREADY; | |
| 546 | + goto err; | |
| 547 | + } | |
| 548 | + | |
| 549 | + list_add(&pds->node, &pdr->lookups); | |
| 550 | + mutex_unlock(&pdr->list_lock); | |
| 551 | + | |
| 552 | + schedule_work(&pdr->locator_work); | |
| 553 | + | |
| 554 | + return pds; | |
| 555 | +err: | |
| 556 | + kfree(pds); | |
| 557 | + return ERR_PTR(ret); | |
| 558 | +} | |
| 559 | +EXPORT_SYMBOL(pdr_add_lookup); | |
| 560 | + | |
| 561 | +/** | |
| 562 | + * pdr_restart_pd() - restart PD | |
| 563 | + * @pdr: PDR client handle | |
| 564 | + * @pds: PD service handle | |
| 565 | + * | |
| 566 | + * Restarts the PD tracked by the PDR client handle for a given service path. | |
| 567 | + * | |
| 568 | + * Return: 0 on success, negative errno on failure. | |
| 569 | + */ | |
| 570 | +int pdr_restart_pd(struct pdr_handle *pdr, struct pdr_service *pds) | |
| 571 | +{ | |
| 572 | + struct servreg_restart_pd_resp resp; | |
| 573 | + struct servreg_restart_pd_req req; | |
| 574 | + struct sockaddr_qrtr addr; | |
| 575 | + struct pdr_service *tmp; | |
| 576 | + struct qmi_txn txn; | |
| 577 | + int ret; | |
| 578 | + | |
| 579 | + if (IS_ERR_OR_NULL(pdr) || IS_ERR_OR_NULL(pds)) | |
| 580 | + return -EINVAL; | |
| 581 | + | |
| 582 | + mutex_lock(&pdr->list_lock); | |
| 583 | + list_for_each_entry(tmp, &pdr->lookups, node) { | |
| 584 | + if (tmp != pds) | |
| 585 | + continue; | |
| 586 | + | |
| 587 | + if (!pds->service_connected) | |
| 588 | + break; | |
| 589 | + | |
| 590 | + /* Prepare req message */ | |
| 591 | + strcpy(req.service_path, pds->service_path); | |
| 592 | + addr = pds->addr; | |
| 593 | + break; | |
| 594 | + } | |
| 595 | + mutex_unlock(&pdr->list_lock); | |
| 596 | + | |
| 597 | + if (!req.service_path[0]) | |
| 598 | + return -EINVAL; | |
| 599 | + | |
| 600 | + ret = qmi_txn_init(&pdr->notifier_hdl, &txn, | |
| 601 | + servreg_restart_pd_resp_ei, | |
| 602 | + &resp); | |
| 603 | + if (ret < 0) | |
| 604 | + return ret; | |
| 605 | + | |
| 606 | + ret = qmi_send_request(&pdr->notifier_hdl, &addr, | |
| 607 | + &txn, SERVREG_RESTART_PD_REQ, | |
| 608 | + SERVREG_RESTART_PD_REQ_MAX_LEN, | |
| 609 | + servreg_restart_pd_req_ei, &req); | |
| 610 | + if (ret < 0) { | |
| 611 | + qmi_txn_cancel(&txn); | |
| 612 | + return ret; | |
| 613 | + } | |
| 614 | + | |
| 615 | + ret = qmi_txn_wait(&txn, 5 * HZ); | |
| 616 | + if (ret < 0) { | |
| 617 | + pr_err("PDR: %s PD restart txn wait failed: %d\n", | |
| 618 | + req.service_path, ret); | |
| 619 | + return ret; | |
| 620 | + } | |
| 621 | + | |
| 622 | + /* Check response if PDR is disabled */ | |
| 623 | + if (resp.resp.result == QMI_RESULT_FAILURE_V01 && | |
| 624 | + resp.resp.error == QMI_ERR_DISABLED_V01) { | |
| 625 | + pr_err("PDR: %s PD restart is disabled: 0x%x\n", | |
| 626 | + req.service_path, resp.resp.error); | |
| 627 | + return -EOPNOTSUPP; | |
| 628 | + } | |
| 629 | + | |
| 630 | + /* Check the response for other error case*/ | |
| 631 | + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { | |
| 632 | + pr_err("PDR: %s request for PD restart failed: 0x%x\n", | |
| 633 | + req.service_path, resp.resp.error); | |
| 634 | + return -EREMOTEIO; | |
| 635 | + } | |
| 636 | + | |
| 637 | + return 0; | |
| 638 | +} | |
| 639 | +EXPORT_SYMBOL(pdr_restart_pd); | |
| 640 | + | |
| 641 | +/** | |
| 642 | + * pdr_handle_alloc() - initialize the PDR client handle | |
| 643 | + * @status: function to be called on PD state change | |
| 644 | + * @priv: handle for client's use | |
| 645 | + * | |
| 646 | + * Initializes the PDR client handle to allow for tracking/restart of PDs. | |
| 647 | + * | |
| 648 | + * Return: pdr_handle object on success, ERR_PTR on failure. | |
| 649 | + */ | |
| 650 | +struct pdr_handle *pdr_handle_alloc(void (*status)(int state, | |
| 651 | + char *service_path, | |
| 652 | + void *priv), void *priv) | |
| 653 | +{ | |
| 654 | + struct pdr_handle *pdr; | |
| 655 | + int ret; | |
| 656 | + | |
| 657 | + if (!status) | |
| 658 | + return ERR_PTR(-EINVAL); | |
| 659 | + | |
| 660 | + pdr = kzalloc(sizeof(*pdr), GFP_KERNEL); | |
| 661 | + if (!pdr) | |
| 662 | + return ERR_PTR(-ENOMEM); | |
| 663 | + | |
| 664 | + pdr->status = status; | |
| 665 | + pdr->priv = priv; | |
| 666 | + | |
| 667 | + mutex_init(&pdr->status_lock); | |
| 668 | + mutex_init(&pdr->list_lock); | |
| 669 | + mutex_init(&pdr->lock); | |
| 670 | + | |
| 671 | + INIT_LIST_HEAD(&pdr->lookups); | |
| 672 | + INIT_LIST_HEAD(&pdr->indack_list); | |
| 673 | + | |
| 674 | + INIT_WORK(&pdr->locator_work, pdr_locator_work); | |
| 675 | + INIT_WORK(&pdr->notifier_work, pdr_notifier_work); | |
| 676 | + INIT_WORK(&pdr->indack_work, pdr_indack_work); | |
| 677 | + | |
| 678 | + pdr->notifier_wq = create_singlethread_workqueue("pdr_notifier_wq"); | |
| 679 | + if (!pdr->notifier_wq) { | |
| 680 | + ret = -ENOMEM; | |
| 681 | + goto free_pdr_handle; | |
| 682 | + } | |
| 683 | + | |
| 684 | + pdr->indack_wq = alloc_ordered_workqueue("pdr_indack_wq", WQ_HIGHPRI); | |
| 685 | + if (!pdr->indack_wq) { | |
| 686 | + ret = -ENOMEM; | |
| 687 | + goto destroy_notifier; | |
| 688 | + } | |
| 689 | + | |
| 690 | + ret = qmi_handle_init(&pdr->locator_hdl, | |
| 691 | + SERVREG_GET_DOMAIN_LIST_RESP_MAX_LEN, | |
| 692 | + &pdr_locator_ops, NULL); | |
| 693 | + if (ret < 0) | |
| 694 | + goto destroy_indack; | |
| 695 | + | |
| 696 | + ret = qmi_add_lookup(&pdr->locator_hdl, SERVREG_LOCATOR_SERVICE, 1, 1); | |
| 697 | + if (ret < 0) | |
| 698 | + goto release_qmi_handle; | |
| 699 | + | |
| 700 | + ret = qmi_handle_init(&pdr->notifier_hdl, | |
| 701 | + SERVREG_STATE_UPDATED_IND_MAX_LEN, | |
| 702 | + &pdr_notifier_ops, | |
| 703 | + qmi_indication_handler); | |
| 704 | + if (ret < 0) | |
| 705 | + goto release_qmi_handle; | |
| 706 | + | |
| 707 | + return pdr; | |
| 708 | + | |
| 709 | +release_qmi_handle: | |
| 710 | + qmi_handle_release(&pdr->locator_hdl); | |
| 711 | +destroy_indack: | |
| 712 | + destroy_workqueue(pdr->indack_wq); | |
| 713 | +destroy_notifier: | |
| 714 | + destroy_workqueue(pdr->notifier_wq); | |
| 715 | +free_pdr_handle: | |
| 716 | + kfree(pdr); | |
| 717 | + | |
| 718 | + return ERR_PTR(ret); | |
| 719 | +} | |
| 720 | +EXPORT_SYMBOL(pdr_handle_alloc); | |
| 721 | + | |
| 722 | +/** | |
| 723 | + * pdr_handle_release() - release the PDR client handle | |
| 724 | + * @pdr: PDR client handle | |
| 725 | + * | |
| 726 | + * Cleans up pending tracking requests and releases the underlying qmi handles. | |
| 727 | + */ | |
| 728 | +void pdr_handle_release(struct pdr_handle *pdr) | |
| 729 | +{ | |
| 730 | + struct pdr_service *pds, *tmp; | |
| 731 | + | |
| 732 | + if (IS_ERR_OR_NULL(pdr)) | |
| 733 | + return; | |
| 734 | + | |
| 735 | + mutex_lock(&pdr->list_lock); | |
| 736 | + list_for_each_entry_safe(pds, tmp, &pdr->lookups, node) { | |
| 737 | + list_del(&pds->node); | |
| 738 | + kfree(pds); | |
| 739 | + } | |
| 740 | + mutex_unlock(&pdr->list_lock); | |
| 741 | + | |
| 742 | + cancel_work_sync(&pdr->locator_work); | |
| 743 | + cancel_work_sync(&pdr->notifier_work); | |
| 744 | + cancel_work_sync(&pdr->indack_work); | |
| 745 | + | |
| 746 | + destroy_workqueue(pdr->notifier_wq); | |
| 747 | + destroy_workqueue(pdr->indack_wq); | |
| 748 | + | |
| 749 | + qmi_handle_release(&pdr->locator_hdl); | |
| 750 | + qmi_handle_release(&pdr->notifier_hdl); | |
| 751 | + | |
| 752 | + kfree(pdr); | |
| 753 | +} | |
| 754 | +EXPORT_SYMBOL(pdr_handle_release); | |
| 755 | + | |
| 756 | +MODULE_LICENSE("GPL v2"); | |
| 757 | +MODULE_DESCRIPTION("Qualcomm Protection Domain Restart helpers"); | |
| @@ -0,0 +1,379 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0 */ | |
| 2 | +#ifndef __QCOM_PDR_HELPER_INTERNAL__ | |
| 3 | +#define __QCOM_PDR_HELPER_INTERNAL__ | |
| 4 | + | |
| 5 | +#include <linux/soc/qcom/pdr.h> | |
| 6 | + | |
| 7 | +#define SERVREG_LOCATOR_SERVICE 0x40 | |
| 8 | +#define SERVREG_NOTIFIER_SERVICE 0x42 | |
| 9 | + | |
| 10 | +#define SERVREG_REGISTER_LISTENER_REQ 0x20 | |
| 11 | +#define SERVREG_GET_DOMAIN_LIST_REQ 0x21 | |
| 12 | +#define SERVREG_STATE_UPDATED_IND_ID 0x22 | |
| 13 | +#define SERVREG_SET_ACK_REQ 0x23 | |
| 14 | +#define SERVREG_RESTART_PD_REQ 0x24 | |
| 15 | + | |
| 16 | +#define SERVREG_DOMAIN_LIST_LENGTH 32 | |
| 17 | +#define SERVREG_RESTART_PD_REQ_MAX_LEN 67 | |
| 18 | +#define SERVREG_REGISTER_LISTENER_REQ_LEN 71 | |
| 19 | +#define SERVREG_SET_ACK_REQ_LEN 72 | |
| 20 | +#define SERVREG_GET_DOMAIN_LIST_REQ_MAX_LEN 74 | |
| 21 | +#define SERVREG_STATE_UPDATED_IND_MAX_LEN 79 | |
| 22 | +#define SERVREG_GET_DOMAIN_LIST_RESP_MAX_LEN 2389 | |
| 23 | + | |
| 24 | +struct servreg_location_entry { | |
| 25 | + char name[SERVREG_NAME_LENGTH + 1]; | |
| 26 | + u8 service_data_valid; | |
| 27 | + u32 service_data; | |
| 28 | + u32 instance; | |
| 29 | +}; | |
| 30 | + | |
| 31 | +struct qmi_elem_info servreg_location_entry_ei[] = { | |
| 32 | + { | |
| 33 | + .data_type = QMI_STRING, | |
| 34 | + .elem_len = SERVREG_NAME_LENGTH + 1, | |
| 35 | + .elem_size = sizeof(char), | |
| 36 | + .array_type = NO_ARRAY, | |
| 37 | + .tlv_type = 0, | |
| 38 | + .offset = offsetof(struct servreg_location_entry, | |
| 39 | + name), | |
| 40 | + }, | |
| 41 | + { | |
| 42 | + .data_type = QMI_UNSIGNED_4_BYTE, | |
| 43 | + .elem_len = 1, | |
| 44 | + .elem_size = sizeof(u32), | |
| 45 | + .array_type = NO_ARRAY, | |
| 46 | + .tlv_type = 0, | |
| 47 | + .offset = offsetof(struct servreg_location_entry, | |
| 48 | + instance), | |
| 49 | + }, | |
| 50 | + { | |
| 51 | + .data_type = QMI_UNSIGNED_1_BYTE, | |
| 52 | + .elem_len = 1, | |
| 53 | + .elem_size = sizeof(u8), | |
| 54 | + .array_type = NO_ARRAY, | |
| 55 | + .tlv_type = 0, | |
| 56 | + .offset = offsetof(struct servreg_location_entry, | |
| 57 | + service_data_valid), | |
| 58 | + }, | |
| 59 | + { | |
| 60 | + .data_type = QMI_UNSIGNED_4_BYTE, | |
| 61 | + .elem_len = 1, | |
| 62 | + .elem_size = sizeof(u32), | |
| 63 | + .array_type = NO_ARRAY, | |
| 64 | + .tlv_type = 0, | |
| 65 | + .offset = offsetof(struct servreg_location_entry, | |
| 66 | + service_data), | |
| 67 | + }, | |
| 68 | + {} | |
| 69 | +}; | |
| 70 | + | |
| 71 | +struct servreg_get_domain_list_req { | |
| 72 | + char service_name[SERVREG_NAME_LENGTH + 1]; | |
| 73 | + u8 domain_offset_valid; | |
| 74 | + u32 domain_offset; | |
| 75 | +}; | |
| 76 | + | |
| 77 | +struct qmi_elem_info servreg_get_domain_list_req_ei[] = { | |
| 78 | + { | |
| 79 | + .data_type = QMI_STRING, | |
| 80 | + .elem_len = SERVREG_NAME_LENGTH + 1, | |
| 81 | + .elem_size = sizeof(char), | |
| 82 | + .array_type = NO_ARRAY, | |
| 83 | + .tlv_type = 0x01, | |
| 84 | + .offset = offsetof(struct servreg_get_domain_list_req, | |
| 85 | + service_name), | |
| 86 | + }, | |
| 87 | + { | |
| 88 | + .data_type = QMI_OPT_FLAG, | |
| 89 | + .elem_len = 1, | |
| 90 | + .elem_size = sizeof(u8), | |
| 91 | + .array_type = NO_ARRAY, | |
| 92 | + .tlv_type = 0x10, | |
| 93 | + .offset = offsetof(struct servreg_get_domain_list_req, | |
| 94 | + domain_offset_valid), | |
| 95 | + }, | |
| 96 | + { | |
| 97 | + .data_type = QMI_UNSIGNED_4_BYTE, | |
| 98 | + .elem_len = 1, | |
| 99 | + .elem_size = sizeof(u32), | |
| 100 | + .array_type = NO_ARRAY, | |
| 101 | + .tlv_type = 0x10, | |
| 102 | + .offset = offsetof(struct servreg_get_domain_list_req, | |
| 103 | + domain_offset), | |
| 104 | + }, | |
| 105 | + {} | |
| 106 | +}; | |
| 107 | + | |
| 108 | +struct servreg_get_domain_list_resp { | |
| 109 | + struct qmi_response_type_v01 resp; | |
| 110 | + u8 total_domains_valid; | |
| 111 | + u16 total_domains; | |
| 112 | + u8 db_rev_count_valid; | |
| 113 | + u16 db_rev_count; | |
| 114 | + u8 domain_list_valid; | |
| 115 | + u32 domain_list_len; | |
| 116 | + struct servreg_location_entry domain_list[SERVREG_DOMAIN_LIST_LENGTH]; | |
| 117 | +}; | |
| 118 | + | |
| 119 | +struct qmi_elem_info servreg_get_domain_list_resp_ei[] = { | |
| 120 | + { | |
| 121 | + .data_type = QMI_STRUCT, | |
| 122 | + .elem_len = 1, | |
| 123 | + .elem_size = sizeof(struct qmi_response_type_v01), | |
| 124 | + .array_type = NO_ARRAY, | |
| 125 | + .tlv_type = 0x02, | |
| 126 | + .offset = offsetof(struct servreg_get_domain_list_resp, | |
| 127 | + resp), | |
| 128 | + .ei_array = qmi_response_type_v01_ei, | |
| 129 | + }, | |
| 130 | + { | |
| 131 | + .data_type = QMI_OPT_FLAG, | |
| 132 | + .elem_len = 1, | |
| 133 | + .elem_size = sizeof(u8), | |
| 134 | + .array_type = NO_ARRAY, | |
| 135 | + .tlv_type = 0x10, | |
| 136 | + .offset = offsetof(struct servreg_get_domain_list_resp, | |
| 137 | + total_domains_valid), | |
| 138 | + }, | |
| 139 | + { | |
| 140 | + .data_type = QMI_UNSIGNED_2_BYTE, | |
| 141 | + .elem_len = 1, | |
| 142 | + .elem_size = sizeof(u16), | |
| 143 | + .array_type = NO_ARRAY, | |
| 144 | + .tlv_type = 0x10, | |
| 145 | + .offset = offsetof(struct servreg_get_domain_list_resp, | |
| 146 | + total_domains), | |
| 147 | + }, | |
| 148 | + { | |
| 149 | + .data_type = QMI_OPT_FLAG, | |
| 150 | + .elem_len = 1, | |
| 151 | + .elem_size = sizeof(u8), | |
| 152 | + .array_type = NO_ARRAY, | |
| 153 | + .tlv_type = 0x11, | |
| 154 | + .offset = offsetof(struct servreg_get_domain_list_resp, | |
| 155 | + db_rev_count_valid), | |
| 156 | + }, | |
| 157 | + { | |
| 158 | + .data_type = QMI_UNSIGNED_2_BYTE, | |
| 159 | + .elem_len = 1, | |
| 160 | + .elem_size = sizeof(u16), | |
| 161 | + .array_type = NO_ARRAY, | |
| 162 | + .tlv_type = 0x11, | |
| 163 | + .offset = offsetof(struct servreg_get_domain_list_resp, | |
| 164 | + db_rev_count), | |
| 165 | + }, | |
| 166 | + { | |
| 167 | + .data_type = QMI_OPT_FLAG, | |
| 168 | + .elem_len = 1, | |
| 169 | + .elem_size = sizeof(u8), | |
| 170 | + .array_type = NO_ARRAY, | |
| 171 | + .tlv_type = 0x12, | |
| 172 | + .offset = offsetof(struct servreg_get_domain_list_resp, | |
| 173 | + domain_list_valid), | |
| 174 | + }, | |
| 175 | + { | |
| 176 | + .data_type = QMI_DATA_LEN, | |
| 177 | + .elem_len = 1, | |
| 178 | + .elem_size = sizeof(u8), | |
| 179 | + .array_type = NO_ARRAY, | |
| 180 | + .tlv_type = 0x12, | |
| 181 | + .offset = offsetof(struct servreg_get_domain_list_resp, | |
| 182 | + domain_list_len), | |
| 183 | + }, | |
| 184 | + { | |
| 185 | + .data_type = QMI_STRUCT, | |
| 186 | + .elem_len = SERVREG_DOMAIN_LIST_LENGTH, | |
| 187 | + .elem_size = sizeof(struct servreg_location_entry), | |
| 188 | + .array_type = NO_ARRAY, | |
| 189 | + .tlv_type = 0x12, | |
| 190 | + .offset = offsetof(struct servreg_get_domain_list_resp, | |
| 191 | + domain_list), | |
| 192 | + .ei_array = servreg_location_entry_ei, | |
| 193 | + }, | |
| 194 | + {} | |
| 195 | +}; | |
| 196 | + | |
| 197 | +struct servreg_register_listener_req { | |
| 198 | + u8 enable; | |
| 199 | + char service_path[SERVREG_NAME_LENGTH + 1]; | |
| 200 | +}; | |
| 201 | + | |
| 202 | +struct qmi_elem_info servreg_register_listener_req_ei[] = { | |
| 203 | + { | |
| 204 | + .data_type = QMI_UNSIGNED_1_BYTE, | |
| 205 | + .elem_len = 1, | |
| 206 | + .elem_size = sizeof(u8), | |
| 207 | + .array_type = NO_ARRAY, | |
| 208 | + .tlv_type = 0x01, | |
| 209 | + .offset = offsetof(struct servreg_register_listener_req, | |
| 210 | + enable), | |
| 211 | + }, | |
| 212 | + { | |
| 213 | + .data_type = QMI_STRING, | |
| 214 | + .elem_len = SERVREG_NAME_LENGTH + 1, | |
| 215 | + .elem_size = sizeof(char), | |
| 216 | + .array_type = NO_ARRAY, | |
| 217 | + .tlv_type = 0x02, | |
| 218 | + .offset = offsetof(struct servreg_register_listener_req, | |
| 219 | + service_path), | |
| 220 | + }, | |
| 221 | + {} | |
| 222 | +}; | |
| 223 | + | |
| 224 | +struct servreg_register_listener_resp { | |
| 225 | + struct qmi_response_type_v01 resp; | |
| 226 | + u8 curr_state_valid; | |
| 227 | + enum servreg_service_state curr_state; | |
| 228 | +}; | |
| 229 | + | |
| 230 | +struct qmi_elem_info servreg_register_listener_resp_ei[] = { | |
| 231 | + { | |
| 232 | + .data_type = QMI_STRUCT, | |
| 233 | + .elem_len = 1, | |
| 234 | + .elem_size = sizeof(struct qmi_response_type_v01), | |
| 235 | + .array_type = NO_ARRAY, | |
| 236 | + .tlv_type = 0x02, | |
| 237 | + .offset = offsetof(struct servreg_register_listener_resp, | |
| 238 | + resp), | |
| 239 | + .ei_array = qmi_response_type_v01_ei, | |
| 240 | + }, | |
| 241 | + { | |
| 242 | + .data_type = QMI_OPT_FLAG, | |
| 243 | + .elem_len = 1, | |
| 244 | + .elem_size = sizeof(u8), | |
| 245 | + .array_type = NO_ARRAY, | |
| 246 | + .tlv_type = 0x10, | |
| 247 | + .offset = offsetof(struct servreg_register_listener_resp, | |
| 248 | + curr_state_valid), | |
| 249 | + }, | |
| 250 | + { | |
| 251 | + .data_type = QMI_SIGNED_4_BYTE_ENUM, | |
| 252 | + .elem_len = 1, | |
| 253 | + .elem_size = sizeof(enum servreg_service_state), | |
| 254 | + .array_type = NO_ARRAY, | |
| 255 | + .tlv_type = 0x10, | |
| 256 | + .offset = offsetof(struct servreg_register_listener_resp, | |
| 257 | + curr_state), | |
| 258 | + }, | |
| 259 | + {} | |
| 260 | +}; | |
| 261 | + | |
| 262 | +struct servreg_restart_pd_req { | |
| 263 | + char service_path[SERVREG_NAME_LENGTH + 1]; | |
| 264 | +}; | |
| 265 | + | |
| 266 | +struct qmi_elem_info servreg_restart_pd_req_ei[] = { | |
| 267 | + { | |
| 268 | + .data_type = QMI_STRING, | |
| 269 | + .elem_len = SERVREG_NAME_LENGTH + 1, | |
| 270 | + .elem_size = sizeof(char), | |
| 271 | + .array_type = NO_ARRAY, | |
| 272 | + .tlv_type = 0x01, | |
| 273 | + .offset = offsetof(struct servreg_restart_pd_req, | |
| 274 | + service_path), | |
| 275 | + }, | |
| 276 | + {} | |
| 277 | +}; | |
| 278 | + | |
| 279 | +struct servreg_restart_pd_resp { | |
| 280 | + struct qmi_response_type_v01 resp; | |
| 281 | +}; | |
| 282 | + | |
| 283 | +struct qmi_elem_info servreg_restart_pd_resp_ei[] = { | |
| 284 | + { | |
| 285 | + .data_type = QMI_STRUCT, | |
| 286 | + .elem_len = 1, | |
| 287 | + .elem_size = sizeof(struct qmi_response_type_v01), | |
| 288 | + .array_type = NO_ARRAY, | |
| 289 | + .tlv_type = 0x02, | |
| 290 | + .offset = offsetof(struct servreg_restart_pd_resp, | |
| 291 | + resp), | |
| 292 | + .ei_array = qmi_response_type_v01_ei, | |
| 293 | + }, | |
| 294 | + {} | |
| 295 | +}; | |
| 296 | + | |
| 297 | +struct servreg_state_updated_ind { | |
| 298 | + enum servreg_service_state curr_state; | |
| 299 | + char service_path[SERVREG_NAME_LENGTH + 1]; | |
| 300 | + u16 transaction_id; | |
| 301 | +}; | |
| 302 | + | |
| 303 | +struct qmi_elem_info servreg_state_updated_ind_ei[] = { | |
| 304 | + { | |
| 305 | + .data_type = QMI_SIGNED_4_BYTE_ENUM, | |
| 306 | + .elem_len = 1, | |
| 307 | + .elem_size = sizeof(u32), | |
| 308 | + .array_type = NO_ARRAY, | |
| 309 | + .tlv_type = 0x01, | |
| 310 | + .offset = offsetof(struct servreg_state_updated_ind, | |
| 311 | + curr_state), | |
| 312 | + }, | |
| 313 | + { | |
| 314 | + .data_type = QMI_STRING, | |
| 315 | + .elem_len = SERVREG_NAME_LENGTH + 1, | |
| 316 | + .elem_size = sizeof(char), | |
| 317 | + .array_type = NO_ARRAY, | |
| 318 | + .tlv_type = 0x02, | |
| 319 | + .offset = offsetof(struct servreg_state_updated_ind, | |
| 320 | + service_path), | |
| 321 | + }, | |
| 322 | + { | |
| 323 | + .data_type = QMI_UNSIGNED_2_BYTE, | |
| 324 | + .elem_len = 1, | |
| 325 | + .elem_size = sizeof(u16), | |
| 326 | + .array_type = NO_ARRAY, | |
| 327 | + .tlv_type = 0x03, | |
| 328 | + .offset = offsetof(struct servreg_state_updated_ind, | |
| 329 | + transaction_id), | |
| 330 | + }, | |
| 331 | + {} | |
| 332 | +}; | |
| 333 | + | |
| 334 | +struct servreg_set_ack_req { | |
| 335 | + char service_path[SERVREG_NAME_LENGTH + 1]; | |
| 336 | + u16 transaction_id; | |
| 337 | +}; | |
| 338 | + | |
| 339 | +struct qmi_elem_info servreg_set_ack_req_ei[] = { | |
| 340 | + { | |
| 341 | + .data_type = QMI_STRING, | |
| 342 | + .elem_len = SERVREG_NAME_LENGTH + 1, | |
| 343 | + .elem_size = sizeof(char), | |
| 344 | + .array_type = NO_ARRAY, | |
| 345 | + .tlv_type = 0x01, | |
| 346 | + .offset = offsetof(struct servreg_set_ack_req, | |
| 347 | + service_path), | |
| 348 | + }, | |
| 349 | + { | |
| 350 | + .data_type = QMI_UNSIGNED_2_BYTE, | |
| 351 | + .elem_len = 1, | |
| 352 | + .elem_size = sizeof(u16), | |
| 353 | + .array_type = NO_ARRAY, | |
| 354 | + .tlv_type = 0x02, | |
| 355 | + .offset = offsetof(struct servreg_set_ack_req, | |
| 356 | + transaction_id), | |
| 357 | + }, | |
| 358 | + {} | |
| 359 | +}; | |
| 360 | + | |
| 361 | +struct servreg_set_ack_resp { | |
| 362 | + struct qmi_response_type_v01 resp; | |
| 363 | +}; | |
| 364 | + | |
| 365 | +struct qmi_elem_info servreg_set_ack_resp_ei[] = { | |
| 366 | + { | |
| 367 | + .data_type = QMI_STRUCT, | |
| 368 | + .elem_len = 1, | |
| 369 | + .elem_size = sizeof(struct qmi_response_type_v01), | |
| 370 | + .array_type = NO_ARRAY, | |
| 371 | + .tlv_type = 0x02, | |
| 372 | + .offset = offsetof(struct servreg_set_ack_resp, | |
| 373 | + resp), | |
| 374 | + .ei_array = qmi_response_type_v01_ei, | |
| 375 | + }, | |
| 376 | + {} | |
| 377 | +}; | |
| 378 | + | |
| 379 | +#endif | |
| @@ -200,7 +200,7 @@ static irqreturn_t qmp_intr(int irq, void *data) · 199 unmodified lines | ||
| 200 | 200 | { |
| 201 | 201 | struct qmp *qmp = data; |
| 202 | 202 | |
| 203 | − wake_up_interruptible_all(&qmp->event); | |
| 203 | + wake_up_all(&qmp->event); | |
| 204 | 204 | |
| 205 | 205 | return IRQ_HANDLED; |
| 206 | 206 | } |
| @@ -225,6 +225,7 @@ static bool qmp_message_empty(struct qmp *qmp) · 18 unmodified lines | ||
| 225 | 225 | static int qmp_send(struct qmp *qmp, const void *data, size_t len) |
| 226 | 226 | { |
| 227 | 227 | long time_left; |
| 228 | + size_t tlen; | |
| 228 | 229 | int ret; |
| 229 | 230 | |
| 230 | 231 | if (WARN_ON(len + sizeof(u32) > qmp->size)) |
| @@ -239,6 +240,9 @@ static int qmp_send(struct qmp *qmp, const void *data, size_t len) · 8 unmodified lines | ||
| 239 | 240 | __iowrite32_copy(qmp->msgram + qmp->offset + sizeof(u32), |
| 240 | 241 | data, len / sizeof(u32)); |
| 241 | 242 | writel(len, qmp->msgram + qmp->offset); |
| 243 | + | |
| 244 | + /* Read back len to confirm data written in message RAM */ | |
| 245 | + tlen = readl(qmp->msgram + qmp->offset); | |
| 242 | 246 | qmp_kick(qmp); |
| 243 | 247 | |
| 244 | 248 | time_left = wait_event_interruptible_timeout(qmp->event, |
| @@ -110,5 +110,6 @@ int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, · 109 unmodified lines | ||
| 110 | 110 | int rpmh_rsc_invalidate(struct rsc_drv *drv); |
| 111 | 111 | |
| 112 | 112 | void rpmh_tx_done(const struct tcs_request *msg, int r); |
| 113 | +int rpmh_flush(struct rpmh_ctrlr *ctrlr); | |
| 113 | 114 | |
| 114 | 115 | #endif /* __RPM_INTERNAL_H__ */ |
| @@ -277,7 +277,7 @@ static void __tcs_buffer_write(struct rsc_drv *drv, int tcs_id, int cmd_id, · 276 unmodified lines | ||
| 277 | 277 | write_tcs_cmd(drv, RSC_DRV_CMD_MSGID, tcs_id, j, msgid); |
| 278 | 278 | write_tcs_cmd(drv, RSC_DRV_CMD_ADDR, tcs_id, j, cmd->addr); |
| 279 | 279 | write_tcs_cmd(drv, RSC_DRV_CMD_DATA, tcs_id, j, cmd->data); |
| 280 | − trace_rpmh_send_msg(drv, tcs_id, j, msgid, cmd); | |
| 280 | + trace_rpmh_send_msg_rcuidle(drv, tcs_id, j, msgid, cmd); | |
| 281 | 281 | } |
| 282 | 282 | |
| 283 | 283 | write_tcs_reg(drv, RSC_DRV_CMD_WAIT_FOR_CMPL, tcs_id, cmd_complete); |
| @@ -23,7 +23,7 @@ · 22 unmodified lines | ||
| 23 | 23 | |
| 24 | 24 | #define RPMH_TIMEOUT_MS msecs_to_jiffies(10000) |
| 25 | 25 | |
| 26 | −#define DEFINE_RPMH_MSG_ONSTACK(dev, s, q, name) \ | |
| 26 | +#define DEFINE_RPMH_MSG_ONSTACK(device, s, q, name) \ | |
| 27 | 27 | struct rpmh_request name = { \ |
| 28 | 28 | .msg = { \ |
| 29 | 29 | .state = s, \ |
| @@ -33,7 +33,7 @@ · 3 unmodified lines | ||
| 33 | 33 | }, \ |
| 34 | 34 | .cmd = { { 0 } }, \ |
| 35 | 35 | .completion = q, \ |
| 36 | − .dev = dev, \ | |
| 36 | + .dev = device, \ | |
| 37 | 37 | .needs_free = false, \ |
| 38 | 38 | } |
| 39 | 39 | |
| @@ -427,11 +427,10 @@ static int is_req_valid(struct cache_req *req) · 387 unmodified lines | ||
| 427 | 427 | req->sleep_val != req->wake_val); |
| 428 | 428 | } |
| 429 | 429 | |
| 430 | −static int send_single(const struct device *dev, enum rpmh_state state, | |
| 430 | +static int send_single(struct rpmh_ctrlr *ctrlr, enum rpmh_state state, | |
| 431 | 431 | u32 addr, u32 data) |
| 432 | 432 | { |
| 433 | − DEFINE_RPMH_MSG_ONSTACK(dev, state, NULL, rpm_msg); | |
| 434 | − struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); | |
| 433 | + DEFINE_RPMH_MSG_ONSTACK(NULL, state, NULL, rpm_msg); | |
| 435 | 434 | |
| 436 | 435 | /* Wake sets are always complete and sleep sets are not */ |
| 437 | 436 | rpm_msg.msg.wait_for_compl = (state == RPMH_WAKE_ONLY_STATE); |
| @@ -445,7 +444,7 @@ static int send_single(const struct device *dev, enum rpmh_state state, · 7 unmodified lines | ||
| 445 | 444 | /** |
| 446 | 445 | * rpmh_flush: Flushes the buffered active and sleep sets to TCS |
| 447 | 446 | * |
| 448 | − * @dev: The device making the request | |
| 447 | + * @ctrlr: controller making request to flush cached data | |
| 449 | 448 | * |
| 450 | 449 | * Return: -EBUSY if the controller is busy, probably waiting on a response |
| 451 | 450 | * to a RPMH request sent earlier. |
| @@ -454,10 +453,9 @@ static int send_single(const struct device *dev, enum rpmh_state state, · 2 unmodified lines | ||
| 454 | 453 | * that is powering down the entire system. Since no other RPMH API would be |
| 455 | 454 | * executing at this time, it is safe to run lockless. |
| 456 | 455 | */ |
| 457 | −int rpmh_flush(const struct device *dev) | |
| 456 | +int rpmh_flush(struct rpmh_ctrlr *ctrlr) | |
| 458 | 457 | { |
| 459 | 458 | struct cache_req *p; |
| 460 | − struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); | |
| 461 | 459 | int ret; |
| 462 | 460 | |
| 463 | 461 | if (!ctrlr->dirty) { |
| @@ -480,11 +478,12 @@ int rpmh_flush(const struct device *dev) · 16 unmodified lines | ||
| 480 | 478 | __func__, p->addr, p->sleep_val, p->wake_val); |
| 481 | 479 | continue; |
| 482 | 480 | } |
| 483 | − ret = send_single(dev, RPMH_SLEEP_STATE, p->addr, p->sleep_val); | |
| 481 | + ret = send_single(ctrlr, RPMH_SLEEP_STATE, p->addr, | |
| 482 | + p->sleep_val); | |
| 484 | 483 | if (ret) |
| 485 | 484 | return ret; |
| 486 | − ret = send_single(dev, RPMH_WAKE_ONLY_STATE, | |
| 487 | − p->addr, p->wake_val); | |
| 485 | + ret = send_single(ctrlr, RPMH_WAKE_ONLY_STATE, p->addr, | |
| 486 | + p->wake_val); | |
| 488 | 487 | if (ret) |
| 489 | 488 | return ret; |
| 490 | 489 | } |
| @@ -493,7 +492,6 @@ int rpmh_flush(const struct device *dev) · 2 unmodified lines | ||
| 493 | 492 | |
| 494 | 493 | return 0; |
| 495 | 494 | } |
| 496 | −EXPORT_SYMBOL(rpmh_flush); | |
| 497 | 495 | |
| 498 | 496 | /** |
| 499 | 497 | * rpmh_invalidate: Invalidate all sleep and active sets |
| @@ -277,7 +277,7 @@ static int show_image_##type(struct seq_file *seq, void *p) \ · 276 unmodified lines | ||
| 277 | 277 | { \ |
| 278 | 278 | struct smem_image_version *image_version = seq->private; \ |
| 279 | 279 | seq_puts(seq, image_version->type); \ |
| 280 | − seq_puts(seq, "\n"); \ | |
| 280 | + seq_putc(seq, '\n'); \ | |
| 281 | 281 | return 0; \ |
| 282 | 282 | } \ |
| 283 | 283 | static int open_image_##type(struct inode *inode, struct file *file) \ |
| @@ -195,19 +195,19 @@ config ARCH_R8A774C0 · 194 unmodified lines | ||
| 195 | 195 | This enables support for the Renesas RZ/G2E SoC. |
| 196 | 196 | |
| 197 | 197 | config ARCH_R8A77950 |
| 198 | − bool | |
| 198 | + bool "Renesas R-Car H3 ES1.x SoC Platform" | |
| 199 | + select ARCH_RCAR_GEN3 | |
| 200 | + select SYSC_R8A7795 | |
| 201 | + help | |
| 202 | + This enables support for the Renesas R-Car H3 SoC (revision 1.x). | |
| 199 | 203 | |
| 200 | 204 | config ARCH_R8A77951 |
| 201 | − bool | |
| 202 | − | |
| 203 | −config ARCH_R8A7795 | |
| 204 | − bool "Renesas R-Car H3 SoC Platform" | |
| 205 | − select ARCH_R8A77950 | |
| 206 | − select ARCH_R8A77951 | |
| 205 | + bool "Renesas R-Car H3 ES2.0+ SoC Platform" | |
| 207 | 206 | select ARCH_RCAR_GEN3 |
| 208 | 207 | select SYSC_R8A7795 |
| 209 | 208 | help |
| 210 | − This enables support for the Renesas R-Car H3 SoC. | |
| 209 | + This enables support for the Renesas R-Car H3 SoC (revisions 2.0 and | |
| 210 | + later). | |
| 211 | 211 | |
| 212 | 212 | config ARCH_R8A77960 |
| 213 | 213 | bool "Renesas R-Car M3-W SoC Platform" |
| @@ -1,5 +1,5 @@ | ||
| 1 | −/* SPDX-License-Identifier: GPL-2.0 | |
| 2 | − * | |
| 1 | +/* SPDX-License-Identifier: GPL-2.0 */ | |
| 2 | +/* | |
| 3 | 3 | * Renesas R-Car System Controller |
| 4 | 4 | * |
| 5 | 5 | * Copyright (C) 2016 Glider bvba |
| @@ -259,7 +259,7 @@ static const struct of_device_id renesas_socs[] __initconst = { · 258 unmodified lines | ||
| 259 | 259 | #ifdef CONFIG_ARCH_R8A7794 |
| 260 | 260 | { .compatible = "renesas,r8a7794", .data = &soc_rcar_e2 }, |
| 261 | 261 | #endif |
| 262 | −#ifdef CONFIG_ARCH_R8A7795 | |
| 262 | +#if defined(CONFIG_ARCH_R8A77950) || defined(CONFIG_ARCH_R8A77951) | |
| 263 | 263 | { .compatible = "renesas,r8a7795", .data = &soc_rcar_h3 }, |
| 264 | 264 | #endif |
| 265 | 265 | #ifdef CONFIG_ARCH_R8A77960 |
| @@ -44,7 +44,6 @@ static struct tee_context *teedev_open(struct tee_device *teedev) · 43 unmodified lines | ||
| 44 | 44 | |
| 45 | 45 | kref_init(&ctx->refcount); |
| 46 | 46 | ctx->teedev = teedev; |
| 47 | − INIT_LIST_HEAD(&ctx->list_shm); | |
| 48 | 47 | rc = teedev->desc->ops->open(ctx); |
| 49 | 48 | if (rc) |
| 50 | 49 | goto err; |
| @@ -37,7 +37,8 @@ struct tee_shm_pool { · 36 unmodified lines | ||
| 37 | 37 | * @num_users: number of active users of this device |
| 38 | 38 | * @c_no_user: completion used when unregistering the device |
| 39 | 39 | * @mutex: mutex protecting @num_users and @idr |
| 40 | − * @idr: register of shared memory object allocated on this device | |
| 40 | + * @idr: register of user space shared memory objects allocated or | |
| 41 | + * registered on this device | |
| 41 | 42 | * @pool: shared memory pool |
| 42 | 43 | */ |
| 43 | 44 | struct tee_device { |
| @@ -13,13 +13,13 @@ · 12 unmodified lines | ||
| 13 | 13 | |
| 14 | 14 | static void tee_shm_release(struct tee_shm *shm) |
| 15 | 15 | { |
| 16 | − struct tee_device *teedev = shm->teedev; | |
| 16 | + struct tee_device *teedev = shm->ctx->teedev; | |
| 17 | 17 | |
| 18 | − mutex_lock(&teedev->mutex); | |
| 19 | − idr_remove(&teedev->idr, shm->id); | |
| 20 | − if (shm->ctx) | |
| 21 | − list_del(&shm->link); | |
| 22 | − mutex_unlock(&teedev->mutex); | |
| 18 | + if (shm->flags & TEE_SHM_DMA_BUF) { | |
| 19 | + mutex_lock(&teedev->mutex); | |
| 20 | + idr_remove(&teedev->idr, shm->id); | |
| 21 | + mutex_unlock(&teedev->mutex); | |
| 22 | + } | |
| 23 | 23 | |
| 24 | 24 | if (shm->flags & TEE_SHM_POOL) { |
| 25 | 25 | struct tee_shm_pool_mgr *poolm; |
| @@ -44,8 +44,7 @@ static void tee_shm_release(struct tee_shm *shm) · 18 unmodified lines | ||
| 44 | 44 | kfree(shm->pages); |
| 45 | 45 | } |
| 46 | 46 | |
| 47 | − if (shm->ctx) | |
| 48 | − teedev_ctx_put(shm->ctx); | |
| 47 | + teedev_ctx_put(shm->ctx); | |
| 49 | 48 | |
| 50 | 49 | kfree(shm); |
| 51 | 50 | |
| @@ -77,7 +76,7 @@ static int tee_shm_op_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) · 25 unmodified lines | ||
| 77 | 76 | size_t size = vma->vm_end - vma->vm_start; |
| 78 | 77 | |
| 79 | 78 | /* Refuse sharing shared memory provided by application */ |
| 80 | − if (shm->flags & TEE_SHM_REGISTER) | |
| 79 | + if (shm->flags & TEE_SHM_USER_MAPPED) | |
| 81 | 80 | return -EINVAL; |
| 82 | 81 | |
| 83 | 82 | return remap_pfn_range(vma, vma->vm_start, shm->paddr >> PAGE_SHIFT, |
| @@ -91,20 +90,14 @@ static const struct dma_buf_ops tee_shm_dma_buf_ops = { · 7 unmodified lines | ||
| 91 | 90 | .mmap = tee_shm_op_mmap, |
| 92 | 91 | }; |
| 93 | 92 | |
| 94 | −static struct tee_shm *__tee_shm_alloc(struct tee_context *ctx, | |
| 95 | − struct tee_device *teedev, | |
| 96 | − size_t size, u32 flags) | |
| 93 | +struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags) | |
| 97 | 94 | { |
| 95 | + struct tee_device *teedev = ctx->teedev; | |
| 98 | 96 | struct tee_shm_pool_mgr *poolm = NULL; |
| 99 | 97 | struct tee_shm *shm; |
| 100 | 98 | void *ret; |
| 101 | 99 | int rc; |
| 102 | 100 | |
| 103 | − if (ctx && ctx->teedev != teedev) { | |
| 104 | − dev_err(teedev->dev.parent, "ctx and teedev mismatch\n"); | |
| 105 | − return ERR_PTR(-EINVAL); | |
| 106 | − } | |
| 107 | − | |
| 108 | 101 | if (!(flags & TEE_SHM_MAPPED)) { |
| 109 | 102 | dev_err(teedev->dev.parent, |
| 110 | 103 | "only mapped allocations supported\n"); |
| @@ -132,7 +125,6 @@ static struct tee_shm *__tee_shm_alloc(struct tee_context *ctx, · 21 unmodified lines | ||
| 132 | 125 | } |
| 133 | 126 | |
| 134 | 127 | shm->flags = flags | TEE_SHM_POOL; |
| 135 | − shm->teedev = teedev; | |
| 136 | 128 | shm->ctx = ctx; |
| 137 | 129 | if (flags & TEE_SHM_DMA_BUF) |
| 138 | 130 | poolm = teedev->pool->dma_buf_mgr; |
| @@ -145,17 +137,18 @@ static struct tee_shm *__tee_shm_alloc(struct tee_context *ctx, · 6 unmodified lines | ||
| 145 | 137 | goto err_kfree; |
| 146 | 138 | } |
| 147 | 139 | |
| 148 | − mutex_lock(&teedev->mutex); | |
| 149 | − shm->id = idr_alloc(&teedev->idr, shm, 1, 0, GFP_KERNEL); | |
| 150 | − mutex_unlock(&teedev->mutex); | |
| 151 | − if (shm->id < 0) { | |
| 152 | − ret = ERR_PTR(shm->id); | |
| 153 | − goto err_pool_free; | |
| 154 | − } | |
| 155 | 140 | |
| 156 | 141 | if (flags & TEE_SHM_DMA_BUF) { |
| 157 | 142 | DEFINE_DMA_BUF_EXPORT_INFO(exp_info); |
| 158 | 143 | |
| 144 | + mutex_lock(&teedev->mutex); | |
| 145 | + shm->id = idr_alloc(&teedev->idr, shm, 1, 0, GFP_KERNEL); | |
| 146 | + mutex_unlock(&teedev->mutex); | |
| 147 | + if (shm->id < 0) { | |
| 148 | + ret = ERR_PTR(shm->id); | |
| 149 | + goto err_pool_free; | |
| 150 | + } | |
| 151 | + | |
| 159 | 152 | exp_info.ops = &tee_shm_dma_buf_ops; |
| 160 | 153 | exp_info.size = shm->size; |
| 161 | 154 | exp_info.flags = O_RDWR; |
| @@ -168,18 +161,16 @@ static struct tee_shm *__tee_shm_alloc(struct tee_context *ctx, · 6 unmodified lines | ||
| 168 | 161 | } |
| 169 | 162 | } |
| 170 | 163 | |
| 171 | − if (ctx) { | |
| 164 | + if (ctx) | |
| 172 | 165 | teedev_ctx_get(ctx); |
| 173 | − mutex_lock(&teedev->mutex); | |
| 174 | − list_add_tail(&shm->link, &ctx->list_shm); | |
| 175 | − mutex_unlock(&teedev->mutex); | |
| 176 | − } | |
| 177 | 166 | |
| 178 | 167 | return shm; |
| 179 | 168 | err_rem: |
| 180 | − mutex_lock(&teedev->mutex); | |
| 181 | − idr_remove(&teedev->idr, shm->id); | |
| 182 | − mutex_unlock(&teedev->mutex); | |
| 169 | + if (flags & TEE_SHM_DMA_BUF) { | |
| 170 | + mutex_lock(&teedev->mutex); | |
| 171 | + idr_remove(&teedev->idr, shm->id); | |
| 172 | + mutex_unlock(&teedev->mutex); | |
| 173 | + } | |
| 183 | 174 | err_pool_free: |
| 184 | 175 | poolm->ops->free(poolm, shm); |
| 185 | 176 | err_kfree: |
| @@ -188,31 +179,8 @@ err_dev_put: · 2 unmodified lines | ||
| 188 | 179 | tee_device_put(teedev); |
| 189 | 180 | return ret; |
| 190 | 181 | } |
| 191 | − | |
| 192 | −/** | |
| 193 | − * tee_shm_alloc() - Allocate shared memory | |
| 194 | − * @ctx: Context that allocates the shared memory | |
| 195 | − * @size: Requested size of shared memory | |
| 196 | − * @flags: Flags setting properties for the requested shared memory. | |
| 197 | − * | |
| 198 | − * Memory allocated as global shared memory is automatically freed when the | |
| 199 | − * TEE file pointer is closed. The @flags field uses the bits defined by | |
| 200 | − * TEE_SHM_* in <linux/tee_drv.h>. TEE_SHM_MAPPED must currently always be | |
| 201 | − * set. If TEE_SHM_DMA_BUF global shared memory will be allocated and | |
| 202 | − * associated with a dma-buf handle, else driver private memory. | |
| 203 | − */ | |
| 204 | −struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags) | |
| 205 | −{ | |
| 206 | − return __tee_shm_alloc(ctx, ctx->teedev, size, flags); | |
| 207 | −} | |
| 208 | 182 | EXPORT_SYMBOL_GPL(tee_shm_alloc); |
| 209 | 183 | |
| 210 | −struct tee_shm *tee_shm_priv_alloc(struct tee_device *teedev, size_t size) | |
| 211 | −{ | |
| 212 | − return __tee_shm_alloc(NULL, teedev, size, TEE_SHM_MAPPED); | |
| 213 | −} | |
| 214 | −EXPORT_SYMBOL_GPL(tee_shm_priv_alloc); | |
| 215 | − | |
| 216 | 184 | struct tee_shm *tee_shm_register(struct tee_context *ctx, unsigned long addr, |
| 217 | 185 | size_t length, u32 flags) |
| 218 | 186 | { |
| @@ -245,7 +213,6 @@ struct tee_shm *tee_shm_register(struct tee_context *ctx, unsigned long addr, · 26 unmodified lines | ||
| 245 | 213 | } |
| 246 | 214 | |
| 247 | 215 | shm->flags = flags | TEE_SHM_REGISTER; |
| 248 | − shm->teedev = teedev; | |
| 249 | 216 | shm->ctx = ctx; |
| 250 | 217 | shm->id = -1; |
| 251 | 218 | addr = untagged_addr(addr); |
| @@ -301,10 +268,6 @@ struct tee_shm *tee_shm_register(struct tee_context *ctx, unsigned long addr, · 49 unmodified lines | ||
| 301 | 268 | } |
| 302 | 269 | } |
| 303 | 270 | |
| 304 | − mutex_lock(&teedev->mutex); | |
| 305 | − list_add_tail(&shm->link, &ctx->list_shm); | |
| 306 | − mutex_unlock(&teedev->mutex); | |
| 307 | − | |
| 308 | 271 | return shm; |
| 309 | 272 | err: |
| 310 | 273 | if (shm) { |
| @@ -18,6 +18,10 @@ · 17 unmodified lines | ||
| 18 | 18 | |
| 19 | 19 | #define SYSC_DRA7_MCAN_ENAWAKEUP (1 << 4) |
| 20 | 20 | |
| 21 | +/* PRUSS sysc found on AM33xx/AM43xx/AM57xx */ | |
| 22 | +#define SYSC_PRUSS_SUB_MWAIT (1 << 5) | |
| 23 | +#define SYSC_PRUSS_STANDBY_INIT (1 << 4) | |
| 24 | + | |
| 21 | 25 | /* SYSCONFIG STANDBYMODE/MIDLEMODE/SIDLEMODE supported by hardware */ |
| 22 | 26 | #define SYSC_IDLE_FORCE 0 |
| 23 | 27 | #define SYSC_IDLE_NO 1 |
| @@ -0,0 +1,32 @@ | ||
| 1 | +/* SPDX-License-Identifier: (GPL-2.0+ or MIT) */ | |
| 2 | +/* | |
| 3 | + * Copyright (c) 2019 Amlogic, Inc. | |
| 4 | + * Author: Jianxin Pan <jianxin.pan@amlogic.com> | |
| 5 | + */ | |
| 6 | + | |
| 7 | +#ifndef _DT_BINDINGS_MESON_A1_POWER_H | |
| 8 | +#define _DT_BINDINGS_MESON_A1_POWER_H | |
| 9 | + | |
| 10 | +#define PWRC_DSPA_ID 8 | |
| 11 | +#define PWRC_DSPB_ID 9 | |
| 12 | +#define PWRC_UART_ID 10 | |
| 13 | +#define PWRC_DMC_ID 11 | |
| 14 | +#define PWRC_I2C_ID 12 | |
| 15 | +#define PWRC_PSRAM_ID 13 | |
| 16 | +#define PWRC_ACODEC_ID 14 | |
| 17 | +#define PWRC_AUDIO_ID 15 | |
| 18 | +#define PWRC_OTP_ID 16 | |
| 19 | +#define PWRC_DMA_ID 17 | |
| 20 | +#define PWRC_SD_EMMC_ID 18 | |
| 21 | +#define PWRC_RAMA_ID 19 | |
| 22 | +#define PWRC_RAMB_ID 20 | |
| 23 | +#define PWRC_IR_ID 21 | |
| 24 | +#define PWRC_SPICC_ID 22 | |
| 25 | +#define PWRC_SPIFC_ID 23 | |
| 26 | +#define PWRC_USB_ID 24 | |
| 27 | +#define PWRC_NIC_ID 25 | |
| 28 | +#define PWRC_PDMIN_ID 26 | |
| 29 | +#define PWRC_RSA_ID 27 | |
| 30 | +#define PWRC_MAX_ID 28 | |
| 31 | + | |
| 32 | +#endif | |
| @@ -25,7 +25,6 @@ enum imx_sc_rpc_svc { · 24 unmodified lines | ||
| 25 | 25 | IMX_SC_RPC_SVC_PAD = 6, |
| 26 | 26 | IMX_SC_RPC_SVC_MISC = 7, |
| 27 | 27 | IMX_SC_RPC_SVC_IRQ = 8, |
| 28 | − IMX_SC_RPC_SVC_ABORT = 9 | |
| 29 | 28 | }; |
| 30 | 29 | |
| 31 | 30 | struct imx_sc_rpc_msg { |
| @@ -12,6 +12,8 @@ enum { · 11 unmodified lines | ||
| 12 | 12 | SM_EFUSE_WRITE, |
| 13 | 13 | SM_EFUSE_USER_MAX, |
| 14 | 14 | SM_GET_CHIP_ID, |
| 15 | + SM_A1_PWRC_SET, | |
| 16 | + SM_A1_PWRC_GET, | |
| 15 | 17 | }; |
| 16 | 18 | |
| 17 | 19 | struct meson_sm_firmware; |
| @@ -17,6 +17,7 @@ enum ti_sysc_module_type { · 16 unmodified lines | ||
| 17 | 17 | TI_SYSC_OMAP4_MCASP, |
| 18 | 18 | TI_SYSC_OMAP4_USB_HOST_FS, |
| 19 | 19 | TI_SYSC_DRA7_MCAN, |
| 20 | + TI_SYSC_PRUSS, | |
| 20 | 21 | }; |
| 21 | 22 | |
| 22 | 23 | struct ti_sysc_cookie { |
| @@ -49,6 +50,9 @@ struct sysc_regbits { · 26 unmodified lines | ||
| 49 | 50 | s8 emufree_shift; |
| 50 | 51 | }; |
| 51 | 52 | |
| 53 | +#define SYSC_MODULE_QUIRK_PRUSS BIT(24) | |
| 54 | +#define SYSC_MODULE_QUIRK_DSS_RESET BIT(23) | |
| 55 | +#define SYSC_MODULE_QUIRK_RTC_UNLOCK BIT(22) | |
| 52 | 56 | #define SYSC_QUIRK_CLKDM_NOAUTO BIT(21) |
| 53 | 57 | #define SYSC_QUIRK_FORCE_MSTANDBY BIT(20) |
| 54 | 58 | #define SYSC_MODULE_QUIRK_AESS BIT(19) |
| @@ -141,6 +145,7 @@ struct clk; · 86 unmodified lines | ||
| 141 | 145 | |
| 142 | 146 | struct ti_sysc_platform_data { |
| 143 | 147 | struct of_dev_auxdata *auxdata; |
| 148 | + bool (*soc_type_gp)(void); | |
| 144 | 149 | int (*init_clockdomain)(struct device *dev, struct clk *fck, |
| 145 | 150 | struct clk *ick, struct ti_sysc_cookie *cookie); |
| 146 | 151 | void (*clkdm_deny_idle)(struct device *dev, |
| @@ -85,6 +85,7 @@ struct apr_device { · 84 unmodified lines | ||
| 85 | 85 | uint16_t domain_id; |
| 86 | 86 | uint32_t version; |
| 87 | 87 | char name[APR_NAME_SIZE]; |
| 88 | + const char *service_path; | |
| 88 | 89 | spinlock_t lock; |
| 89 | 90 | struct list_head node; |
| 90 | 91 | }; |
| @@ -0,0 +1,29 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0 */ | |
| 2 | +#ifndef __QCOM_PDR_HELPER__ | |
| 3 | +#define __QCOM_PDR_HELPER__ | |
| 4 | + | |
| 5 | +#include <linux/soc/qcom/qmi.h> | |
| 6 | + | |
| 7 | +#define SERVREG_NAME_LENGTH 64 | |
| 8 | + | |
| 9 | +struct pdr_service; | |
| 10 | +struct pdr_handle; | |
| 11 | + | |
| 12 | +enum servreg_service_state { | |
| 13 | + SERVREG_LOCATOR_ERR = 0x1, | |
| 14 | + SERVREG_SERVICE_STATE_DOWN = 0x0FFFFFFF, | |
| 15 | + SERVREG_SERVICE_STATE_UP = 0x1FFFFFFF, | |
| 16 | + SERVREG_SERVICE_STATE_EARLY_DOWN = 0x2FFFFFFF, | |
| 17 | + SERVREG_SERVICE_STATE_UNINIT = 0x7FFFFFFF, | |
| 18 | +}; | |
| 19 | + | |
| 20 | +struct pdr_handle *pdr_handle_alloc(void (*status)(int state, | |
| 21 | + char *service_path, | |
| 22 | + void *priv), void *priv); | |
| 23 | +struct pdr_service *pdr_add_lookup(struct pdr_handle *pdr, | |
| 24 | + const char *service_name, | |
| 25 | + const char *service_path); | |
| 26 | +int pdr_restart_pd(struct pdr_handle *pdr, struct pdr_service *pds); | |
| 27 | +void pdr_handle_release(struct pdr_handle *pdr); | |
| 28 | + | |
| 29 | +#endif | |
| @@ -88,6 +88,7 @@ struct qmi_elem_info { · 87 unmodified lines | ||
| 88 | 88 | #define QMI_ERR_CLIENT_IDS_EXHAUSTED_V01 5 |
| 89 | 89 | #define QMI_ERR_INVALID_ID_V01 41 |
| 90 | 90 | #define QMI_ERR_ENCODING_V01 58 |
| 91 | +#define QMI_ERR_DISABLED_V01 69 | |
| 91 | 92 | #define QMI_ERR_INCOMPATIBLE_STATE_V01 90 |
| 92 | 93 | #define QMI_ERR_NOT_SUPPORTED_V01 94 |
| 93 | 94 | |
| @@ -49,7 +49,6 @@ struct tee_shm_pool; · 48 unmodified lines | ||
| 49 | 49 | */ |
| 50 | 50 | struct tee_context { |
| 51 | 51 | struct tee_device *teedev; |
| 52 | − struct list_head list_shm; | |
| 53 | 52 | void *data; |
| 54 | 53 | struct kref refcount; |
| 55 | 54 | bool releasing; |
| @@ -168,9 +167,7 @@ void tee_device_unregister(struct tee_device *teedev); · 112 unmodified lines | ||
| 168 | 167 | |
| 169 | 168 | /** |
| 170 | 169 | * struct tee_shm - shared memory object |
| 171 | − * @teedev: device used to allocate the object | |
| 172 | − * @ctx: context using the object, if NULL the context is gone | |
| 173 | − * @link link element | |
| 170 | + * @ctx: context using the object | |
| 174 | 171 | * @paddr: physical address of the shared memory |
| 175 | 172 | * @kaddr: virtual address of the shared memory |
| 176 | 173 | * @size: size of shared memory |
| @@ -185,9 +182,7 @@ void tee_device_unregister(struct tee_device *teedev); · 8 unmodified lines | ||
| 185 | 182 | * subsystem and from drivers that implements their own shm pool manager. |
| 186 | 183 | */ |
| 187 | 184 | struct tee_shm { |
| 188 | − struct tee_device *teedev; | |
| 189 | 185 | struct tee_context *ctx; |
| 190 | − struct list_head link; | |
| 191 | 186 | phys_addr_t paddr; |
| 192 | 187 | void *kaddr; |
| 193 | 188 | size_t size; |
| @@ -318,18 +313,6 @@ void *tee_get_drvdata(struct tee_device *teedev); · 124 unmodified lines | ||
| 318 | 313 | */ |
| 319 | 314 | struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags); |
| 320 | 315 | |
| 321 | −/** | |
| 322 | − * tee_shm_priv_alloc() - Allocate shared memory privately | |
| 323 | − * @dev: Device that allocates the shared memory | |
| 324 | − * @size: Requested size of shared memory | |
| 325 | − * | |
| 326 | − * Allocates shared memory buffer that is not associated with any client | |
| 327 | − * context. Such buffers are owned by TEE driver and used for internal calls. | |
| 328 | − * | |
| 329 | − * @returns a pointer to 'struct tee_shm' | |
| 330 | − */ | |
| 331 | −struct tee_shm *tee_shm_priv_alloc(struct tee_device *teedev, size_t size); | |
| 332 | − | |
| 333 | 316 | /** |
| 334 | 317 | * tee_shm_register() - Register shared memory buffer |
| 335 | 318 | * @ctx: Context that registers the shared memory |
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | /* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ |
| 2 | 2 | /* |
| 3 | 3 | * Copyright 2014-2016 Freescale Semiconductor Inc. |
| 4 | − * Copyright NXP | |
| 4 | + * Copyright 2017-2019 NXP | |
| 5 | 5 | * |
| 6 | 6 | */ |
| 7 | 7 | #ifndef __FSL_DPAA2_IO_H |
| @@ -109,6 +109,10 @@ int dpaa2_io_service_pull_channel(struct dpaa2_io *d, u32 channelid, · 101 unmodified lines | ||
| 109 | 109 | |
| 110 | 110 | int dpaa2_io_service_enqueue_fq(struct dpaa2_io *d, u32 fqid, |
| 111 | 111 | const struct dpaa2_fd *fd); |
| 112 | +int dpaa2_io_service_enqueue_multiple_fq(struct dpaa2_io *d, u32 fqid, | |
| 113 | + const struct dpaa2_fd *fd, int number_of_frame); | |
| 114 | +int dpaa2_io_service_enqueue_multiple_desc_fq(struct dpaa2_io *d, u32 *fqid, | |
| 115 | + const struct dpaa2_fd *fd, int number_of_frame); | |
| 112 | 116 | int dpaa2_io_service_enqueue_qd(struct dpaa2_io *d, u32 qdid, u8 prio, |
| 113 | 117 | u16 qdbin, const struct dpaa2_fd *fd); |
| 114 | 118 | int dpaa2_io_service_release(struct dpaa2_io *d, u16 bpid, |
| @@ -178,10 +178,10 @@ struct ucc_fast_info { · 177 unmodified lines | ||
| 178 | 178 | struct ucc_fast_private { |
| 179 | 179 | struct ucc_fast_info *uf_info; |
| 180 | 180 | struct ucc_fast __iomem *uf_regs; /* a pointer to the UCC regs. */ |
| 181 | − u32 __iomem *p_ucce; /* a pointer to the event register in memory. */ | |
| 182 | − u32 __iomem *p_uccm; /* a pointer to the mask register in memory. */ | |
| 181 | + __be32 __iomem *p_ucce; /* a pointer to the event register in memory. */ | |
| 182 | + __be32 __iomem *p_uccm; /* a pointer to the mask register in memory. */ | |
| 183 | 183 | #ifdef CONFIG_UGETH_TX_ON_DEMAND |
| 184 | − u16 __iomem *p_utodr; /* pointer to the transmit on demand register */ | |
| 184 | + __be16 __iomem *p_utodr;/* pointer to the transmit on demand register */ | |
| 185 | 185 | #endif |
| 186 | 186 | int enabled_tx; /* Whether channel is enabled for Tx (ENT) */ |
| 187 | 187 | int enabled_rx; /* Whether channel is enabled for Rx (ENR) */ |
| @@ -184,7 +184,7 @@ struct ucc_slow_info { · 183 unmodified lines | ||
| 184 | 184 | struct ucc_slow_private { |
| 185 | 185 | struct ucc_slow_info *us_info; |
| 186 | 186 | struct ucc_slow __iomem *us_regs; /* Ptr to memory map of UCC regs */ |
| 187 | − struct ucc_slow_pram *us_pram; /* a pointer to the parameter RAM */ | |
| 187 | + struct ucc_slow_pram __iomem *us_pram; /* a pointer to the parameter RAM */ | |
| 188 | 188 | s32 us_pram_offset; |
| 189 | 189 | int enabled_tx; /* Whether channel is enabled for Tx (ENT) */ |
| 190 | 190 | int enabled_rx; /* Whether channel is enabled for Rx (ENR) */ |
| @@ -196,13 +196,12 @@ struct ucc_slow_private { · 5 unmodified lines | ||
| 196 | 196 | and length for first BD in a frame */ |
| 197 | 197 | s32 tx_base_offset; /* first BD in Tx BD table offset (In MURAM) */ |
| 198 | 198 | s32 rx_base_offset; /* first BD in Rx BD table offset (In MURAM) */ |
| 199 | − struct qe_bd *confBd; /* next BD for confirm after Tx */ | |
| 200 | − struct qe_bd *tx_bd; /* next BD for new Tx request */ | |
| 201 | − struct qe_bd *rx_bd; /* next BD to collect after Rx */ | |
| 199 | + struct qe_bd __iomem *confBd; /* next BD for confirm after Tx */ | |
| 200 | + struct qe_bd __iomem *tx_bd; /* next BD for new Tx request */ | |
| 201 | + struct qe_bd __iomem *rx_bd; /* next BD to collect after Rx */ | |
| 202 | 202 | void *p_rx_frame; /* accumulating receive frame */ |
| 203 | − u16 *p_ucce; /* a pointer to the event register in memory. | |
| 204 | − */ | |
| 205 | − u16 *p_uccm; /* a pointer to the mask register in memory */ | |
| 203 | + __be16 __iomem *p_ucce; /* a pointer to the event register in memory */ | |
| 204 | + __be16 __iomem *p_uccm; /* a pointer to the mask register in memory */ | |
| 206 | 205 | u16 saved_uccm; /* a saved mask for the RX Interrupt bits */ |
| 207 | 206 | #ifdef STATISTICS |
| 208 | 207 | u32 tx_frames; /* Transmitted frames counters */ |
| @@ -20,8 +20,6 @@ int rpmh_write_async(const struct device *dev, enum rpmh_state state, · 19 unmodified lines | ||
| 20 | 20 | int rpmh_write_batch(const struct device *dev, enum rpmh_state state, |
| 21 | 21 | const struct tcs_cmd *cmd, u32 *n); |
| 22 | 22 | |
| 23 | −int rpmh_flush(const struct device *dev); | |
| 24 | − | |
| 25 | 23 | int rpmh_invalidate(const struct device *dev); |
| 26 | 24 | |
| 27 | 25 | #else |
| @@ -40,9 +38,6 @@ static inline int rpmh_write_batch(const struct device *dev, · 12 unmodified lines | ||
| 40 | 38 | const struct tcs_cmd *cmd, u32 *n) |
| 41 | 39 | { return -ENODEV; } |
| 42 | 40 | |
| 43 | −static inline int rpmh_flush(const struct device *dev) | |
| 44 | −{ return -ENODEV; } | |
| 45 | − | |
| 46 | 41 | static inline int rpmh_invalidate(const struct device *dev) |
| 47 | 42 | { return -ENODEV; } |
| 48 | 43 | |