diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-04-25 12:02:16 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-04-25 12:02:16 -0700 |
| commit | a907047732470f75f3b7c9a8ee09b16765b8364c (patch) | |
| tree | c14d8b6220f80f4c87e8dc0eb6d6fa2581e9a294 /drivers/soc/qcom | |
| parent | 18032df5ef5c0eec2adf120142bd95a3a8807866 (diff) | |
| parent | 4c87f3ff78b20da0dd118762fd66b67a98e36249 (diff) | |
| download | linux-a907047732470f75f3b7c9a8ee09b16765b8364c.tar.gz linux-a907047732470f75f3b7c9a8ee09b16765b8364c.tar.bz2 linux-a907047732470f75f3b7c9a8ee09b16765b8364c.zip | |
Merge tag 'soc-drivers-6.4' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc
Pull ARM SoC driver updates from Arnd Bergmann:
"The most notable updates this time are for Qualcomm Snapdragon
platforms. The Inline-Crypto-Engine gets a new DT binding and driver,
and a number of drivers now support additional Snapdragon variants, in
particular the rsc, scm, geni, bwm, glink and socinfo, while the llcc
(edac) and rpm drivers get notable functionality updates.
Updates on other platforms include:
- Various updates to the Mediatek mutex and mmsys drivers, including
support for the Helio X10 SoC
- Support for unidirectional mailbox channels in Arm SCMI firmware
- Support for per cpu asynchronous notification in OP-TEE firmware
- Minor updates for memory controller drivers.
- Minor updates for Renesas, TI, Amlogic, Apple, Broadcom, Tegra,
Allwinner, Versatile Express, Canaan, Microchip, Mediatek and i.MX
SoC drivers, mainly updating the use of MODULE_LICENSE() macros and
obsolete DT driver interfaces"
* tag 'soc-drivers-6.4' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (165 commits)
soc: ti: smartreflex: Simplify getting the opam_sr pointer
bus: vexpress-config: Add explicit of_platform.h include
soc: mediatek: Kconfig: Add MTK_CMDQ dependency to MTK_MMSYS
memory: mtk-smi: mt8365: Add SMI Support
dt-bindings: memory-controllers: mediatek,smi-larb: add mt8365
dt-bindings: memory-controllers: mediatek,smi-common: add mt8365
memory: tegra: read values from correct device
dt-bindings: crypto: Add Qualcomm Inline Crypto Engine
soc: qcom: Make the Qualcomm UFS/SDCC ICE a dedicated driver
dt-bindings: firmware: document Qualcomm QCM2290 SCM
soc: qcom: rpmh-rsc: Support RSC v3 minor versions
soc: qcom: smd-rpm: Use GFP_ATOMIC in write path
soc/tegra: fuse: Remove nvmem root only access
soc/tegra: cbb: tegra194: Use of_address_count() helper
soc/tegra: cbb: Remove MODULE_LICENSE in non-modules
ARM: tegra: Remove MODULE_LICENSE in non-modules
soc/tegra: flowctrl: Use devm_platform_get_and_ioremap_resource()
soc: tegra: cbb: Drop empty platform remove function
firmware: arm_scmi: Add support for unidirectional mailbox channels
dt-bindings: firmware: arm,scmi: Support mailboxes unidirectional channels
...
Diffstat (limited to 'drivers/soc/qcom')
| -rw-r--r-- | drivers/soc/qcom/Kconfig | 6 | ||||
| -rw-r--r-- | drivers/soc/qcom/Makefile | 1 | ||||
| -rw-r--r-- | drivers/soc/qcom/icc-bwmon.c | 231 | ||||
| -rw-r--r-- | drivers/soc/qcom/ice.c | 366 | ||||
| -rw-r--r-- | drivers/soc/qcom/llcc-qcom.c | 104 | ||||
| -rw-r--r-- | drivers/soc/qcom/pmic_glink.c | 65 | ||||
| -rw-r--r-- | drivers/soc/qcom/qcom_aoss.c | 2 | ||||
| -rw-r--r-- | drivers/soc/qcom/qcom_gsbi.c | 2 | ||||
| -rw-r--r-- | drivers/soc/qcom/rmtfs_mem.c | 2 | ||||
| -rw-r--r-- | drivers/soc/qcom/rpmh-rsc.c | 2 | ||||
| -rw-r--r-- | drivers/soc/qcom/rpmpd.c | 833 | ||||
| -rw-r--r-- | drivers/soc/qcom/smd-rpm.c | 2 | ||||
| -rw-r--r-- | drivers/soc/qcom/smem.c | 4 | ||||
| -rw-r--r-- | drivers/soc/qcom/smsm.c | 11 | ||||
| -rw-r--r-- | drivers/soc/qcom/socinfo.c | 16 |
15 files changed, 1298 insertions, 349 deletions
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index a8f283086a21..a491718f8064 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -72,7 +72,7 @@ config QCOM_LLCC config QCOM_KRYO_L2_ACCESSORS bool - depends on ARCH_QCOM && ARM64 || COMPILE_TEST + depends on (ARCH_QCOM || COMPILE_TEST) && ARM64 config QCOM_MDT_LOADER tristate @@ -275,4 +275,8 @@ config QCOM_ICC_BWMON the fixed bandwidth votes from cpufreq (CPU nodes) thus achieve high memory throughput even with lower CPU frequencies. +config QCOM_INLINE_CRYPTO_ENGINE + tristate + select QCOM_SCM + endmenu diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 6e88da899f60..0f43a88b4894 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -32,3 +32,4 @@ obj-$(CONFIG_QCOM_RPMHPD) += rpmhpd.o obj-$(CONFIG_QCOM_RPMPD) += rpmpd.o obj-$(CONFIG_QCOM_KRYO_L2_ACCESSORS) += kryo-l2-accessors.o obj-$(CONFIG_QCOM_ICC_BWMON) += icc-bwmon.o +obj-$(CONFIG_QCOM_INLINE_CRYPTO_ENGINE) += ice.o diff --git a/drivers/soc/qcom/icc-bwmon.c b/drivers/soc/qcom/icc-bwmon.c index d07be3700db6..fd58c5b69897 100644 --- a/drivers/soc/qcom/icc-bwmon.c +++ b/drivers/soc/qcom/icc-bwmon.c @@ -34,14 +34,27 @@ /* Internal sampling clock frequency */ #define HW_TIMER_HZ 19200000 -#define BWMON_V4_GLOBAL_IRQ_CLEAR 0x008 -#define BWMON_V4_GLOBAL_IRQ_ENABLE 0x00c +#define BWMON_V4_GLOBAL_IRQ_CLEAR 0x108 +#define BWMON_V4_GLOBAL_IRQ_ENABLE 0x10c /* * All values here and further are matching regmap fields, so without absolute * register offsets. */ #define BWMON_V4_GLOBAL_IRQ_ENABLE_ENABLE BIT(0) +/* + * Starting with SDM845, the BWMON4 register space has changed a bit: + * the global registers were jammed into the beginning of the monitor region. + * To keep the proper offsets, one would have to map <GLOBAL_BASE 0x200> and + * <GLOBAL_BASE+0x100 0x300>, which is straight up wrong. + * To facilitate for that, while allowing the older, arguably more proper + * implementations to work, offset the global registers by -0x100 to avoid + * having to map half of the global registers twice. + */ +#define BWMON_V4_845_OFFSET 0x100 +#define BWMON_V4_GLOBAL_IRQ_CLEAR_845 (BWMON_V4_GLOBAL_IRQ_CLEAR - BWMON_V4_845_OFFSET) +#define BWMON_V4_GLOBAL_IRQ_ENABLE_845 (BWMON_V4_GLOBAL_IRQ_ENABLE - BWMON_V4_845_OFFSET) + #define BWMON_V4_IRQ_STATUS 0x100 #define BWMON_V4_IRQ_CLEAR 0x108 @@ -118,9 +131,13 @@ #define BWMON_NEEDS_FORCE_CLEAR BIT(1) enum bwmon_fields { + /* Global region fields, keep them at the top */ F_GLOBAL_IRQ_CLEAR, F_GLOBAL_IRQ_ENABLE, - F_IRQ_STATUS, + F_NUM_GLOBAL_FIELDS, + + /* Monitor region fields */ + F_IRQ_STATUS = F_NUM_GLOBAL_FIELDS, F_IRQ_CLEAR, F_IRQ_ENABLE, F_ENABLE, @@ -157,6 +174,9 @@ struct icc_bwmon_data { const struct regmap_config *regmap_cfg; const struct reg_field *regmap_fields; + + const struct regmap_config *global_regmap_cfg; + const struct reg_field *global_regmap_fields; }; struct icc_bwmon { @@ -164,8 +184,8 @@ struct icc_bwmon { const struct icc_bwmon_data *data; int irq; - struct regmap *regmap; struct regmap_field *regs[F_NUM_FIELDS]; + struct regmap_field *global_regs[F_NUM_GLOBAL_FIELDS]; unsigned int max_bw_kbps; unsigned int min_bw_kbps; @@ -175,8 +195,8 @@ struct icc_bwmon { /* BWMON v4 */ static const struct reg_field msm8998_bwmon_reg_fields[] = { - [F_GLOBAL_IRQ_CLEAR] = REG_FIELD(BWMON_V4_GLOBAL_IRQ_CLEAR, 0, 0), - [F_GLOBAL_IRQ_ENABLE] = REG_FIELD(BWMON_V4_GLOBAL_IRQ_ENABLE, 0, 0), + [F_GLOBAL_IRQ_CLEAR] = {}, + [F_GLOBAL_IRQ_ENABLE] = {}, [F_IRQ_STATUS] = REG_FIELD(BWMON_V4_IRQ_STATUS, 4, 7), [F_IRQ_CLEAR] = REG_FIELD(BWMON_V4_IRQ_CLEAR, 4, 7), [F_IRQ_ENABLE] = REG_FIELD(BWMON_V4_IRQ_ENABLE, 4, 7), @@ -202,7 +222,6 @@ static const struct reg_field msm8998_bwmon_reg_fields[] = { }; static const struct regmap_range msm8998_bwmon_reg_noread_ranges[] = { - regmap_reg_range(BWMON_V4_GLOBAL_IRQ_CLEAR, BWMON_V4_GLOBAL_IRQ_CLEAR), regmap_reg_range(BWMON_V4_IRQ_CLEAR, BWMON_V4_IRQ_CLEAR), regmap_reg_range(BWMON_V4_CLEAR, BWMON_V4_CLEAR), }; @@ -222,16 +241,33 @@ static const struct regmap_access_table msm8998_bwmon_reg_volatile_table = { .n_yes_ranges = ARRAY_SIZE(msm8998_bwmon_reg_volatile_ranges), }; +static const struct reg_field msm8998_bwmon_global_reg_fields[] = { + [F_GLOBAL_IRQ_CLEAR] = REG_FIELD(BWMON_V4_GLOBAL_IRQ_CLEAR, 0, 0), + [F_GLOBAL_IRQ_ENABLE] = REG_FIELD(BWMON_V4_GLOBAL_IRQ_ENABLE, 0, 0), +}; + +static const struct regmap_range msm8998_bwmon_global_reg_noread_ranges[] = { + regmap_reg_range(BWMON_V4_GLOBAL_IRQ_CLEAR, BWMON_V4_GLOBAL_IRQ_CLEAR), +}; + +static const struct regmap_access_table msm8998_bwmon_global_reg_read_table = { + .no_ranges = msm8998_bwmon_global_reg_noread_ranges, + .n_no_ranges = ARRAY_SIZE(msm8998_bwmon_global_reg_noread_ranges), +}; + /* * Fill the cache for non-readable registers only as rest does not really * matter and can be read from the device. */ static const struct reg_default msm8998_bwmon_reg_defaults[] = { - { BWMON_V4_GLOBAL_IRQ_CLEAR, 0x0 }, { BWMON_V4_IRQ_CLEAR, 0x0 }, { BWMON_V4_CLEAR, 0x0 }, }; +static const struct reg_default msm8998_bwmon_global_reg_defaults[] = { + { BWMON_V4_GLOBAL_IRQ_CLEAR, 0x0 }, +}; + static const struct regmap_config msm8998_bwmon_regmap_cfg = { .reg_bits = 32, .reg_stride = 4, @@ -252,6 +288,93 @@ static const struct regmap_config msm8998_bwmon_regmap_cfg = { .cache_type = REGCACHE_RBTREE, }; +static const struct regmap_config msm8998_bwmon_global_regmap_cfg = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + /* + * No concurrent access expected - driver has one interrupt handler, + * regmap is not shared, no driver or user-space API. + */ + .disable_locking = true, + .rd_table = &msm8998_bwmon_global_reg_read_table, + .reg_defaults = msm8998_bwmon_global_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(msm8998_bwmon_global_reg_defaults), + /* + * Cache is necessary for using regmap fields with non-readable + * registers. + */ + .cache_type = REGCACHE_RBTREE, +}; + +static const struct reg_field sdm845_cpu_bwmon_reg_fields[] = { + [F_GLOBAL_IRQ_CLEAR] = REG_FIELD(BWMON_V4_GLOBAL_IRQ_CLEAR_845, 0, 0), + [F_GLOBAL_IRQ_ENABLE] = REG_FIELD(BWMON_V4_GLOBAL_IRQ_ENABLE_845, 0, 0), + [F_IRQ_STATUS] = REG_FIELD(BWMON_V4_IRQ_STATUS, 4, 7), + [F_IRQ_CLEAR] = REG_FIELD(BWMON_V4_IRQ_CLEAR, 4, 7), + [F_IRQ_ENABLE] = REG_FIELD(BWMON_V4_IRQ_ENABLE, 4, 7), + /* F_ENABLE covers entire register to disable other features */ + [F_ENABLE] = REG_FIELD(BWMON_V4_ENABLE, 0, 31), + [F_CLEAR] = REG_FIELD(BWMON_V4_CLEAR, 0, 1), + [F_SAMPLE_WINDOW] = REG_FIELD(BWMON_V4_SAMPLE_WINDOW, 0, 23), + [F_THRESHOLD_HIGH] = REG_FIELD(BWMON_V4_THRESHOLD_HIGH, 0, 11), + [F_THRESHOLD_MED] = REG_FIELD(BWMON_V4_THRESHOLD_MED, 0, 11), + [F_THRESHOLD_LOW] = REG_FIELD(BWMON_V4_THRESHOLD_LOW, 0, 11), + [F_ZONE_ACTIONS_ZONE0] = REG_FIELD(BWMON_V4_ZONE_ACTIONS, 0, 7), + [F_ZONE_ACTIONS_ZONE1] = REG_FIELD(BWMON_V4_ZONE_ACTIONS, 8, 15), + [F_ZONE_ACTIONS_ZONE2] = REG_FIELD(BWMON_V4_ZONE_ACTIONS, 16, 23), + [F_ZONE_ACTIONS_ZONE3] = REG_FIELD(BWMON_V4_ZONE_ACTIONS, 24, 31), + [F_THRESHOLD_COUNT_ZONE0] = REG_FIELD(BWMON_V4_THRESHOLD_COUNT, 0, 7), + [F_THRESHOLD_COUNT_ZONE1] = REG_FIELD(BWMON_V4_THRESHOLD_COUNT, 8, 15), + [F_THRESHOLD_COUNT_ZONE2] = REG_FIELD(BWMON_V4_THRESHOLD_COUNT, 16, 23), + [F_THRESHOLD_COUNT_ZONE3] = REG_FIELD(BWMON_V4_THRESHOLD_COUNT, 24, 31), + [F_ZONE0_MAX] = REG_FIELD(BWMON_V4_ZONE_MAX(0), 0, 11), + [F_ZONE1_MAX] = REG_FIELD(BWMON_V4_ZONE_MAX(1), 0, 11), + [F_ZONE2_MAX] = REG_FIELD(BWMON_V4_ZONE_MAX(2), 0, 11), + [F_ZONE3_MAX] = REG_FIELD(BWMON_V4_ZONE_MAX(3), 0, 11), +}; + +static const struct regmap_range sdm845_cpu_bwmon_reg_noread_ranges[] = { + regmap_reg_range(BWMON_V4_GLOBAL_IRQ_CLEAR_845, BWMON_V4_GLOBAL_IRQ_CLEAR_845), + regmap_reg_range(BWMON_V4_IRQ_CLEAR, BWMON_V4_IRQ_CLEAR), + regmap_reg_range(BWMON_V4_CLEAR, BWMON_V4_CLEAR), +}; + +static const struct regmap_access_table sdm845_cpu_bwmon_reg_read_table = { + .no_ranges = sdm845_cpu_bwmon_reg_noread_ranges, + .n_no_ranges = ARRAY_SIZE(sdm845_cpu_bwmon_reg_noread_ranges), +}; + +/* + * Fill the cache for non-readable registers only as rest does not really + * matter and can be read from the device. + */ +static const struct reg_default sdm845_cpu_bwmon_reg_defaults[] = { + { BWMON_V4_GLOBAL_IRQ_CLEAR_845, 0x0 }, + { BWMON_V4_IRQ_CLEAR, 0x0 }, + { BWMON_V4_CLEAR, 0x0 }, +}; + +static const struct regmap_config sdm845_cpu_bwmon_regmap_cfg = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + /* + * No concurrent access expected - driver has one interrupt handler, + * regmap is not shared, no driver or user-space API. + */ + .disable_locking = true, + .rd_table = &sdm845_cpu_bwmon_reg_read_table, + .volatile_table = &msm8998_bwmon_reg_volatile_table, + .reg_defaults = sdm845_cpu_bwmon_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(sdm845_cpu_bwmon_reg_defaults), + /* + * Cache is necessary for using regmap fields with non-readable + * registers. + */ + .cache_type = REGCACHE_RBTREE, +}; + /* BWMON v5 */ static const struct reg_field sdm845_llcc_bwmon_reg_fields[] = { [F_GLOBAL_IRQ_CLEAR] = {}, @@ -350,6 +473,13 @@ static void bwmon_clear_counters(struct icc_bwmon *bwmon, bool clear_all) static void bwmon_clear_irq(struct icc_bwmon *bwmon) { + struct regmap_field *global_irq_clr; + + if (bwmon->data->global_regmap_fields) + global_irq_clr = bwmon->global_regs[F_GLOBAL_IRQ_CLEAR]; + else + global_irq_clr = bwmon->regs[F_GLOBAL_IRQ_CLEAR]; + /* * Clear zone and global interrupts. The order and barriers are * important. Quoting downstream Qualcomm msm-4.9 tree: @@ -370,15 +500,22 @@ static void bwmon_clear_irq(struct icc_bwmon *bwmon) if (bwmon->data->quirks & BWMON_NEEDS_FORCE_CLEAR) regmap_field_force_write(bwmon->regs[F_IRQ_CLEAR], 0); if (bwmon->data->quirks & BWMON_HAS_GLOBAL_IRQ) - regmap_field_force_write(bwmon->regs[F_GLOBAL_IRQ_CLEAR], + regmap_field_force_write(global_irq_clr, BWMON_V4_GLOBAL_IRQ_ENABLE_ENABLE); } static void bwmon_disable(struct icc_bwmon *bwmon) { + struct regmap_field *global_irq_en; + + if (bwmon->data->global_regmap_fields) + global_irq_en = bwmon->global_regs[F_GLOBAL_IRQ_ENABLE]; + else + global_irq_en = bwmon->regs[F_GLOBAL_IRQ_ENABLE]; + /* Disable interrupts. Strict ordering, see bwmon_clear_irq(). */ if (bwmon->data->quirks & BWMON_HAS_GLOBAL_IRQ) - regmap_field_write(bwmon->regs[F_GLOBAL_IRQ_ENABLE], 0x0); + regmap_field_write(global_irq_en, 0x0); regmap_field_write(bwmon->regs[F_IRQ_ENABLE], 0x0); /* @@ -390,10 +527,18 @@ static void bwmon_disable(struct icc_bwmon *bwmon) static void bwmon_enable(struct icc_bwmon *bwmon, unsigned int irq_enable) { + struct regmap_field *global_irq_en; + + if (bwmon->data->global_regmap_fields) + global_irq_en = bwmon->global_regs[F_GLOBAL_IRQ_ENABLE]; + else + global_irq_en = bwmon->regs[F_GLOBAL_IRQ_ENABLE]; + /* Enable interrupts */ if (bwmon->data->quirks & BWMON_HAS_GLOBAL_IRQ) - regmap_field_write(bwmon->regs[F_GLOBAL_IRQ_ENABLE], + regmap_field_write(global_irq_en, BWMON_V4_GLOBAL_IRQ_ENABLE_ENABLE); + regmap_field_write(bwmon->regs[F_IRQ_ENABLE], irq_enable); /* Enable bwmon */ @@ -556,7 +701,9 @@ static int bwmon_init_regmap(struct platform_device *pdev, struct device *dev = &pdev->dev; void __iomem *base; struct regmap *map; + int ret; + /* Map the monitor base */ base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return dev_err_probe(dev, PTR_ERR(base), @@ -567,12 +714,35 @@ static int bwmon_init_regmap(struct platform_device *pdev, return dev_err_probe(dev, PTR_ERR(map), "failed to initialize regmap\n"); + BUILD_BUG_ON(ARRAY_SIZE(msm8998_bwmon_global_reg_fields) != F_NUM_GLOBAL_FIELDS); BUILD_BUG_ON(ARRAY_SIZE(msm8998_bwmon_reg_fields) != F_NUM_FIELDS); + BUILD_BUG_ON(ARRAY_SIZE(sdm845_cpu_bwmon_reg_fields) != F_NUM_FIELDS); BUILD_BUG_ON(ARRAY_SIZE(sdm845_llcc_bwmon_reg_fields) != F_NUM_FIELDS); - return devm_regmap_field_bulk_alloc(dev, map, bwmon->regs, + ret = devm_regmap_field_bulk_alloc(dev, map, bwmon->regs, bwmon->data->regmap_fields, F_NUM_FIELDS); + if (ret) + return ret; + + if (bwmon->data->global_regmap_cfg) { + /* Map the global base, if separate */ + base = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(base)) + return dev_err_probe(dev, PTR_ERR(base), + "failed to map bwmon global registers\n"); + + map = devm_regmap_init_mmio(dev, base, bwmon->data->global_regmap_cfg); + if (IS_ERR(map)) + return dev_err_probe(dev, PTR_ERR(map), + "failed to initialize global regmap\n"); + + ret = devm_regmap_field_bulk_alloc(dev, map, bwmon->global_regs, + bwmon->data->global_regmap_fields, + F_NUM_GLOBAL_FIELDS); + } + + return ret; } static int bwmon_probe(struct platform_device *pdev) @@ -645,6 +815,21 @@ static const struct icc_bwmon_data msm8998_bwmon_data = { .quirks = BWMON_HAS_GLOBAL_IRQ, .regmap_fields = msm8998_bwmon_reg_fields, .regmap_cfg = &msm8998_bwmon_regmap_cfg, + .global_regmap_fields = msm8998_bwmon_global_reg_fields, + .global_regmap_cfg = &msm8998_bwmon_global_regmap_cfg, +}; + +static const struct icc_bwmon_data sdm845_cpu_bwmon_data = { + .sample_ms = 4, + .count_unit_kb = 64, + .default_highbw_kbps = 4800 * 1024, /* 4.8 GBps */ + .default_medbw_kbps = 512 * 1024, /* 512 MBps */ + .default_lowbw_kbps = 0, + .zone1_thres_count = 16, + .zone3_thres_count = 1, + .quirks = BWMON_HAS_GLOBAL_IRQ, + .regmap_fields = sdm845_cpu_bwmon_reg_fields, + .regmap_cfg = &sdm845_cpu_bwmon_regmap_cfg, }; static const struct icc_bwmon_data sdm845_llcc_bwmon_data = { @@ -673,16 +858,18 @@ static const struct icc_bwmon_data sc7280_llcc_bwmon_data = { }; static const struct of_device_id bwmon_of_match[] = { - { - .compatible = "qcom,msm8998-bwmon", - .data = &msm8998_bwmon_data - }, { - .compatible = "qcom,sdm845-llcc-bwmon", - .data = &sdm845_llcc_bwmon_data - }, { - .compatible = "qcom,sc7280-llcc-bwmon", - .data = &sc7280_llcc_bwmon_data - }, + /* BWMONv4, separate monitor and global register spaces */ + { .compatible = "qcom,msm8998-bwmon", .data = &msm8998_bwmon_data }, + /* BWMONv4, unified register space */ + { .compatible = "qcom,sdm845-bwmon", .data = &sdm845_cpu_bwmon_data }, + /* BWMONv5 */ + { .compatible = "qcom,sdm845-llcc-bwmon", .data = &sdm845_llcc_bwmon_data }, + { .compatible = "qcom,sc7280-llcc-bwmon", .data = &sc7280_llcc_bwmon_data }, + + /* Compatibles kept for legacy reasons */ + { .compatible = "qcom,sc7280-cpu-bwmon", .data = &sdm845_cpu_bwmon_data }, + { .compatible = "qcom,sc8280xp-cpu-bwmon", .data = &sdm845_cpu_bwmon_data }, + { .compatible = "qcom,sm8550-cpu-bwmon", .data = &sdm845_cpu_bwmon_data }, {} }; MODULE_DEVICE_TABLE(of, bwmon_of_match); diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c new file mode 100644 index 000000000000..a6123ea96272 --- /dev/null +++ b/drivers/soc/qcom/ice.c @@ -0,0 +1,366 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Qualcomm ICE (Inline Crypto Engine) support. + * + * Copyright (c) 2013-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2019, Google LLC + * Copyright (c) 2023, Linaro Limited + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/iopoll.h> +#include <linux/of_platform.h> + +#include <linux/firmware/qcom/qcom_scm.h> + +#include <soc/qcom/ice.h> + +#define AES_256_XTS_KEY_SIZE 64 + +/* QCOM ICE registers */ +#define QCOM_ICE_REG_VERSION 0x0008 +#define QCOM_ICE_REG_FUSE_SETTING 0x0010 +#define QCOM_ICE_REG_BIST_STATUS 0x0070 +#define QCOM_ICE_REG_ADVANCED_CONTROL 0x1000 + +/* BIST ("built-in self-test") status flags */ +#define QCOM_ICE_BIST_STATUS_MASK GENMASK(31, 28) + +#define QCOM_ICE_FUSE_SETTING_MASK 0x1 +#define QCOM_ICE_FORCE_HW_KEY0_SETTING_MASK 0x2 +#define QCOM_ICE_FORCE_HW_KEY1_SETTING_MASK 0x4 + +#define qcom_ice_writel(engine, val, reg) \ + writel((val), (engine)->base + (reg)) + +#define qcom_ice_readl(engine, reg) \ + readl((engine)->base + (reg)) + +struct qcom_ice { + struct device *dev; + void __iomem *base; + struct device_link *link; + + struct clk *core_clk; +}; + +static bool qcom_ice_check_supported(struct qcom_ice *ice) +{ + u32 regval = qcom_ice_readl(ice, QCOM_ICE_REG_VERSION); + struct device *dev = ice->dev; + int major = FIELD_GET(GENMASK(31, 24), regval); + int minor = FIELD_GET(GENMASK(23, 16), regval); + int step = FIELD_GET(GENMASK(15, 0), regval); + + /* For now this driver only supports ICE version 3 and 4. */ + if (major != 3 && major != 4) { + dev_warn(dev, "Unsupported ICE version: v%d.%d.%d\n", + major, minor, step); + return false; + } + + dev_info(dev, "Found QC Inline Crypto Engine (ICE) v%d.%d.%d\n", + major, minor, step); + + /* If fuses are blown, ICE might not work in the standard way. */ + regval = qcom_ice_readl(ice, QCOM_ICE_REG_FUSE_SETTING); + if (regval & (QCOM_ICE_FUSE_SETTING_MASK | + QCOM_ICE_FORCE_HW_KEY0_SETTING_MASK | + QCOM_ICE_FORCE_HW_KEY1_SETTING_MASK)) { + dev_warn(dev, "Fuses are blown; ICE is unusable!\n"); + return false; + } + + return true; +} + +static void qcom_ice_low_power_mode_enable(struct qcom_ice *ice) +{ + u32 regval; + + regval = qcom_ice_readl(ice, QCOM_ICE_REG_ADVANCED_CONTROL); + + /* Enable low power mode sequence */ + regval |= 0x7000; + qcom_ice_writel(ice, regval, QCOM_ICE_REG_ADVANCED_CONTROL); +} + +static void qcom_ice_optimization_enable(struct qcom_ice *ice) +{ + u32 regval; + + /* ICE Optimizations Enable Sequence */ + regval = qcom_ice_readl(ice, QCOM_ICE_REG_ADVANCED_CONTROL); + regval |= 0xd807100; + /* ICE HPG requires delay before writing */ + udelay(5); + qcom_ice_writel(ice, regval, QCOM_ICE_REG_ADVANCED_CONTROL); + udelay(5); +} + +/* + * Wait until the ICE BIST (built-in self-test) has completed. + * + * This may be necessary before ICE can be used. + * Note that we don't really care whether the BIST passed or failed; + * we really just want to make sure that it isn't still running. This is + * because (a) the BIST is a FIPS compliance thing that never fails in + * practice, (b) ICE is documented to reject crypto requests if the BIST + * fails, so we needn't do it in software too, and (c) properly testing + * storage encryption requires testing the full storage stack anyway, + * and not relying on hardware-level self-tests. + */ +static int qcom_ice_wait_bist_status(struct qcom_ice *ice) +{ + u32 regval; + int err; + + err = readl_poll_timeout(ice->base + QCOM_ICE_REG_BIST_STATUS, + regval, !(regval & QCOM_ICE_BIST_STATUS_MASK), + 50, 5000); + if (err) + dev_err(ice->dev, "Timed out waiting for ICE self-test to complete\n"); + + return err; +} + +int qcom_ice_enable(struct qcom_ice *ice) +{ + qcom_ice_low_power_mode_enable(ice); + qcom_ice_optimization_enable(ice); + + return qcom_ice_wait_bist_status(ice); +} +EXPORT_SYMBOL_GPL(qcom_ice_enable); + +int qcom_ice_resume(struct qcom_ice *ice) +{ + struct device *dev = ice->dev; + int err; + + err = clk_prepare_enable(ice->core_clk); + if (err) { + dev_err(dev, "failed to enable core clock (%d)\n", + err); + return err; + } + + return qcom_ice_wait_bist_status(ice); +} +EXPORT_SYMBOL_GPL(qcom_ice_resume); + +int qcom_ice_suspend(struct qcom_ice *ice) +{ + clk_disable_unprepare(ice->core_clk); + + return 0; +} +EXPORT_SYMBOL_GPL(qcom_ice_suspend); + +int qcom_ice_program_key(struct qcom_ice *ice, + u8 algorithm_id, u8 key_size, + const u8 crypto_key[], u8 data_unit_size, + int slot) +{ + struct device *dev = ice->dev; + union { + u8 bytes[AES_256_XTS_KEY_SIZE]; + u32 words[AES_256_XTS_KEY_SIZE / sizeof(u32)]; + } key; + int i; + int err; + + /* Only AES-256-XTS has been tested so far. */ + if (algorithm_id != QCOM_ICE_CRYPTO_ALG_AES_XTS || + key_size != QCOM_ICE_CRYPTO_KEY_SIZE_256) { + dev_err_ratelimited(dev, + "Unhandled crypto capability; algorithm_id=%d, key_size=%d\n", + algorithm_id, key_size); + return -EINVAL; + } + + memcpy(key.bytes, crypto_key, AES_256_XTS_KEY_SIZE); + + /* The SCM call requires that the key words are encoded in big endian */ + for (i = 0; i < ARRAY_SIZE(key.words); i++) + __cpu_to_be32s(&key.words[i]); + + err = qcom_scm_ice_set_key(slot, key.bytes, AES_256_XTS_KEY_SIZE, + QCOM_SCM_ICE_CIPHER_AES_256_XTS, + data_unit_size); + + memzero_explicit(&key, sizeof(key)); + + return err; +} +EXPORT_SYMBOL_GPL(qcom_ice_program_key); + +int qcom_ice_evict_key(struct qcom_ice *ice, int slot) +{ + return qcom_scm_ice_invalidate_key(slot); +} +EXPORT_SYMBOL_GPL(qcom_ice_evict_key); + +static struct qcom_ice *qcom_ice_create(struct device *dev, + void __iomem *base) +{ + struct qcom_ice *engine; + + if (!qcom_scm_is_available()) + return ERR_PTR(-EPROBE_DEFER); + + if (!qcom_scm_ice_available()) { + dev_warn(dev, "ICE SCM interface not found\n"); + return NULL; + } + + engine = devm_kzalloc(dev, sizeof(*engine), GFP_KERNEL); + if (!engine) + return ERR_PTR(-ENOMEM); + + engine->dev = dev; + engine->base = base; + + /* + * Legacy DT binding uses different clk names for each consumer, + * so lets try those first. If none of those are a match, it means + * the we only have one clock and it is part of the dedicated DT node. + * Also, enable the clock before we check what HW version the driver + * supports. + */ + engine->core_clk = devm_clk_get_optional_enabled(dev, "ice_core_clk"); + if (!engine->core_clk) + engine->core_clk = devm_clk_get_optional_enabled(dev, "ice"); + if (!engine->core_clk) + engine->core_clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(engine->core_clk)) + return ERR_CAST(engine->core_clk); + + if (!qcom_ice_check_supported(engine)) + return ERR_PTR(-EOPNOTSUPP); + + dev_dbg(dev, "Registered Qualcomm Inline Crypto Engine\n"); + + return engine; +} + +/** + * of_qcom_ice_get() - get an ICE instance from a DT node + * @dev: device pointer for the consumer device + * + * This function will provide an ICE instance either by creating one for the + * consumer device if its DT node provides the 'ice' reg range and the 'ice' + * clock (for legacy DT style). On the other hand, if consumer provides a + * phandle via 'qcom,ice' property to an ICE DT, the ICE instance will already + * be created and so this function will return that instead. + * + * Return: ICE pointer on success, NULL if there is no ICE data provided by the + * consumer or ERR_PTR() on error. + */ +struct qcom_ice *of_qcom_ice_get(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct qcom_ice *ice; + struct device_node *node; + struct resource *res; + void __iomem *base; + + if (!dev || !dev->of_node) + return ERR_PTR(-ENODEV); + + /* + * In order to support legacy style devicetree bindings, we need + * to create the ICE instance using the consumer device and the reg + * range called 'ice' it provides. + */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ice"); + if (res) { + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return ERR_CAST(base); + + /* create ICE instance using consumer dev */ + return qcom_ice_create(&pdev->dev, base); + } + + /* + * If the consumer node does not provider an 'ice' reg range + * (legacy DT binding), then it must at least provide a phandle + * to the ICE devicetree node, otherwise ICE is not supported. + */ + node = of_parse_phandle(dev->of_node, "qcom,ice", 0); + if (!node) + return NULL; + + pdev = of_find_device_by_node(node); + if (!pdev) { + dev_err(dev, "Cannot find device node %s\n", node->name); + ice = ERR_PTR(-EPROBE_DEFER); + goto out; + } + + ice = platform_get_drvdata(pdev); + if (!ice) { + dev_err(dev, "Cannot get ice instance from %s\n", + dev_name(&pdev->dev)); + platform_device_put(pdev); + ice = ERR_PTR(-EPROBE_DEFER); + goto out; + } + + ice->link = device_link_add(dev, &pdev->dev, DL_FLAG_AUTOREMOVE_SUPPLIER); + if (!ice->link) { + dev_err(&pdev->dev, + "Failed to create device link to consumer %s\n", + dev_name(dev)); + platform_device_put(pdev); + ice = ERR_PTR(-EINVAL); + } + +out: + of_node_put(node); + + return ice; +} +EXPORT_SYMBOL_GPL(of_qcom_ice_get); + +static int qcom_ice_probe(struct platform_device *pdev) +{ + struct qcom_ice *engine; + void __iomem *base; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) { + dev_warn(&pdev->dev, "ICE registers not found\n"); + return PTR_ERR(base); + } + + engine = qcom_ice_create(&pdev->dev, base); + if (IS_ERR(engine)) + return PTR_ERR(engine); + + platform_set_drvdata(pdev, engine); + + return 0; +} + +static const struct of_device_id qcom_ice_of_match_table[] = { + { .compatible = "qcom,inline-crypto-engine" }, + { }, +}; +MODULE_DEVICE_TABLE(of, qcom_ice_of_match_table); + +static struct platform_driver qcom_ice_driver = { + .probe = qcom_ice_probe, + .driver = { + .name = "qcom-ice", + .of_match_table = qcom_ice_of_match_table, + }, +}; + +module_platform_driver(qcom_ice_driver); + +MODULE_DESCRIPTION("Qualcomm Inline Crypto Engine driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c index 26efe12012a0..67c19ed2219a 100644 --- a/drivers/soc/qcom/llcc-qcom.c +++ b/drivers/soc/qcom/llcc-qcom.c @@ -62,8 +62,6 @@ #define LLCC_TRP_WRSC_CACHEABLE_EN 0x21f2c #define LLCC_TRP_ALGO_CFG8 0x21f30 -#define BANK_OFFSET_STRIDE 0x80000 - #define LLCC_VERSION_2_0_0_0 0x02000000 #define LLCC_VERSION_2_1_0_0 0x02010000 #define LLCC_VERSION_4_1_0_0 0x04010000 @@ -122,10 +120,11 @@ struct llcc_slice_config { struct qcom_llcc_config { const struct llcc_slice_config *sct_data; - int size; - bool need_llcc_cfg; const u32 *reg_offset; const struct llcc_edac_reg_offset *edac_reg_offset; + int size; + bool need_llcc_cfg; + bool no_edac; }; enum llcc_reg_offset { @@ -227,6 +226,14 @@ static const struct llcc_slice_config sm6350_data[] = { { LLCC_MODPE, 29, 64, 1, 1, 0xFFF, 0x0, 0, 0, 0, 0, 1, 0 }, }; +static const struct llcc_slice_config sm7150_data[] = { + { LLCC_CPUSS, 1, 512, 1, 0, 0xF, 0x0, 0, 0, 0, 1, 1 }, + { LLCC_MDM, 8, 128, 2, 0, 0xF, 0x0, 0, 0, 0, 1, 0 }, + { LLCC_GPUHTW, 11, 256, 1, 1, 0xF, 0x0, 0, 0, 0, 1, 0 }, + { LLCC_GPU, 12, 256, 1, 1, 0xF, 0x0, 0, 0, 0, 1, 0 }, + { LLCC_NPU, 23, 512, 1, 0, 0xF, 0x0, 0, 0, 0, 1, 0 }, +}; + static const struct llcc_slice_config sm8150_data[] = { { LLCC_CPUSS, 1, 3072, 1, 1, 0xFFF, 0x0, 0, 0, 0, 1, 1 }, { LLCC_VIDSC0, 2, 512, 2, 1, 0xFFF, 0x0, 0, 0, 0, 1, 0 }, @@ -454,6 +461,7 @@ static const struct qcom_llcc_config sdm845_cfg = { .need_llcc_cfg = false, .reg_offset = llcc_v1_reg_offset, .edac_reg_offset = &llcc_v1_edac_reg_offset, + .no_edac = true, }; static const struct qcom_llcc_config sm6350_cfg = { @@ -464,6 +472,14 @@ static const struct qcom_llcc_config sm6350_cfg = { .edac_reg_offset = &llcc_v1_edac_reg_offset, }; +static const struct qcom_llcc_config sm7150_cfg = { + .sct_data = sm7150_data, + .size = ARRAY_SIZE(sm7150_data), + .need_llcc_cfg = true, + .reg_offset = llcc_v1_reg_offset, + .edac_reg_offset = &llcc_v1_edac_reg_offset, +}; + static const struct qcom_llcc_config sm8150_cfg = { .sct_data = sm8150_data, .size = ARRAY_SIZE(sm8150_data), @@ -898,8 +914,8 @@ static int qcom_llcc_remove(struct platform_device *pdev) return 0; } -static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev, - const char *name) +static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev, u8 index, + const char *name) { void __iomem *base; struct regmap_config llcc_regmap_config = { @@ -909,7 +925,7 @@ static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev, .fast_io = true, }; - base = devm_platform_ioremap_resource_byname(pdev, name); + base = devm_platform_ioremap_resource(pdev, index); if (IS_ERR(base)) return ERR_CAST(base); @@ -927,6 +943,7 @@ static int qcom_llcc_probe(struct platform_device *pdev) const struct llcc_slice_config *llcc_cfg; u32 sz; u32 version; + struct regmap *regmap; drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL); if (!drv_data) { @@ -934,21 +951,51 @@ static int qcom_llcc_probe(struct platform_device *pdev) goto err; } |
