From 292709b9cf3ba470af94b62c9bb60284cc581b79 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Sat, 7 May 2022 20:14:13 +0800 Subject: [PATCH 0001/1823] ASoC: fsl_micfil: explicitly clear software reset bit SRES is self-cleared bit, but REG_MICFIL_CTRL1 is defined as non volatile register, it still remain in regmap cache after set, then every update of REG_MICFIL_CTRL1, software reset happens. to avoid this, clear it explicitly. Signed-off-by: Shengjiu Wang Link: https://lore.kernel.org/r/1651925654-32060-1-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_micfil.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sound/soc/fsl/fsl_micfil.c b/sound/soc/fsl/fsl_micfil.c index 9f90989ac59a..cb84d95c3aac 100644 --- a/sound/soc/fsl/fsl_micfil.c +++ b/sound/soc/fsl/fsl_micfil.c @@ -191,6 +191,17 @@ static int fsl_micfil_reset(struct device *dev) return ret; } + /* + * SRES is self-cleared bit, but REG_MICFIL_CTRL1 is defined + * as non-volatile register, so SRES still remain in regmap + * cache after set, that every update of REG_MICFIL_CTRL1, + * software reset happens. so clear it explicitly. + */ + ret = regmap_clear_bits(micfil->regmap, REG_MICFIL_CTRL1, + MICFIL_CTRL1_SRES); + if (ret) + return ret; + return 0; } From b776c4a4618ec1b5219d494c423dc142f23c4e8f Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Sat, 7 May 2022 20:14:14 +0800 Subject: [PATCH 0002/1823] ASoC: fsl_micfil: explicitly clear CHnF flags There may be failure when start 1 channel recording after 8 channels recording. The reason is that the CHnF flags are not cleared successfully by software reset. This issue is triggerred by the change of clearing software reset bit. CHnF flags are write 1 clear bits. Clear them by force write. Signed-off-by: Shengjiu Wang Link: https://lore.kernel.org/r/1651925654-32060-2-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_micfil.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/fsl/fsl_micfil.c b/sound/soc/fsl/fsl_micfil.c index cb84d95c3aac..d1cd104f8584 100644 --- a/sound/soc/fsl/fsl_micfil.c +++ b/sound/soc/fsl/fsl_micfil.c @@ -202,6 +202,14 @@ static int fsl_micfil_reset(struct device *dev) if (ret) return ret; + /* + * Set SRES should clear CHnF flags, But even add delay here + * the CHnF may not be cleared sometimes, so clear CHnF explicitly. + */ + ret = regmap_write_bits(micfil->regmap, REG_MICFIL_STAT, 0xFF, 0xFF); + if (ret) + return ret; + return 0; } From 698813ba8c580efb356ace8dbf55f61dac6063a8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 11 May 2022 14:41:36 +0100 Subject: [PATCH 0003/1823] ASoC: ops: Fix bounds check for _sx controls For _sx controls the semantics of the max field is not the usual one, max is the number of steps rather than the maximum value. This means that our check in snd_soc_put_volsw_sx() needs to just check against the maximum value. Fixes: 4f1e50d6a9cf9c1b ("ASoC: ops: Reject out of bounds values in snd_soc_put_volsw_sx()") Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220511134137.169575-1-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/soc-ops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index e693070f51fe..1ac7e2ce31a1 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -435,7 +435,7 @@ int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol, val = ucontrol->value.integer.value[0]; if (mc->platform_max && val > mc->platform_max) return -EINVAL; - if (val > max - min) + if (val > max) return -EINVAL; val_mask = mask << shift; val = (val + min) & mask; From 97eea946b93961fffd29448dcda7398d0d51c4b2 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 11 May 2022 14:41:37 +0100 Subject: [PATCH 0004/1823] ASoC: ops: Check bounds for second channel in snd_soc_put_volsw_sx() The bounds checks in snd_soc_put_volsw_sx() are only being applied to the first channel, meaning it is possible to write out of bounds values to the second channel in stereo controls. Add appropriate checks. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220511134137.169575-2-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/soc-ops.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index 1ac7e2ce31a1..7cac26a64e0c 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -451,6 +451,12 @@ int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol, val_mask = mask << rshift; val2 = (ucontrol->value.integer.value[1] + min) & mask; + + if (mc->platform_max && val2 > mc->platform_max) + return -EINVAL; + if (val2 > max) + return -EINVAL; + val2 = val2 << rshift; err = snd_soc_component_update_bits(component, reg2, val_mask, From 19c5bda74dc45fee598a57600b550c9ea7662f10 Mon Sep 17 00:00:00 2001 From: Hui Tang Date: Thu, 12 May 2022 15:46:40 +0800 Subject: [PATCH 0005/1823] ASoC: tlv320adc3xxx: Fix build error for implicit function declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sound/soc/codecs/tlv320adc3xxx.c: In function ‘adc3xxx_i2c_probe’: sound/soc/codecs/tlv320adc3xxx.c:1359:21: error: implicit declaration of function ‘devm_gpiod_get’; did you mean ‘devm_gpio_free’? [-Werror=implicit-function-declaration] adc3xxx->rst_pin = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); ^~~~~~~~~~~~~~ devm_gpio_free CC [M] drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgt215.o LD [M] sound/soc/codecs/snd-soc-ak4671.o LD [M] sound/soc/codecs/snd-soc-arizona.o LD [M] sound/soc/codecs/snd-soc-cros-ec-codec.o LD [M] sound/soc/codecs/snd-soc-ak4641.o LD [M] sound/soc/codecs/snd-soc-alc5632.o sound/soc/codecs/tlv320adc3xxx.c:1359:50: error: ‘GPIOD_OUT_LOW’ undeclared (first use in this function); did you mean ‘GPIOF_INIT_LOW’? adc3xxx->rst_pin = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); ^~~~~~~~~~~~~ GPIOF_INIT_LOW sound/soc/codecs/tlv320adc3xxx.c:1359:50: note: each undeclared identifier is reported only once for each function it appears in LD [M] sound/soc/codecs/snd-soc-cs35l32.o sound/soc/codecs/tlv320adc3xxx.c:1408:2: error: implicit declaration of function ‘gpiod_set_value_cansleep’; did you mean ‘gpio_set_value_cansleep’? [-Werror=implicit-function-declaration] gpiod_set_value_cansleep(adc3xxx->rst_pin, 1); ^~~~~~~~~~~~~~~~~~~~~~~~ gpio_set_value_cansleep LD [M] sound/soc/codecs/snd-soc-cs35l41-lib.o LD [M] sound/soc/codecs/snd-soc-cs35l36.o LD [M] sound/soc/codecs/snd-soc-cs35l34.o LD [M] sound/soc/codecs/snd-soc-cs35l41.o CC [M] drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp89.o cc1: all warnings being treated as errors Fixes: e9a3b57efd28 ("ASoC: codec: tlv320adc3xxx: New codec driver") Signed-off-by: Hui Tang Link: https://lore.kernel.org/r/20220512074640.75550-3-tanghui20@huawei.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320adc3xxx.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/codecs/tlv320adc3xxx.c b/sound/soc/codecs/tlv320adc3xxx.c index ae18982ac310..104167c6fd93 100644 --- a/sound/soc/codecs/tlv320adc3xxx.c +++ b/sound/soc/codecs/tlv320adc3xxx.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -1025,7 +1026,9 @@ static const struct gpio_chip adc3xxx_gpio_chip = { static void adc3xxx_free_gpio(struct adc3xxx *adc3xxx) { +#ifdef CONFIG_GPIOLIB gpiochip_remove(&adc3xxx->gpio_chip); +#endif } static void adc3xxx_init_gpio(struct adc3xxx *adc3xxx) From 6ca53a75c425cb1cb0929ce07f58d9e41094500c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 9 Sep 2022 11:20:21 +0200 Subject: [PATCH 0006/1823] dt-bindings: hwlock: qcom-hwspinlock: add support for MMIO on older SoCs Older Qualcomm SoCs have TCSR mutex registers with 0x80 stride, instead of 0x1000. Add dedicated compatibles for such case. Unfortunately the binding started using a generic "qcom,tcsr-mutex" compatible without specifying the SoC part, thus it looks now quite inconsistent. Signed-off-by: Krzysztof Kozlowski Acked-by: Rob Herring Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220909092035.223915-2-krzysztof.kozlowski@linaro.org --- .../bindings/hwlock/qcom-hwspinlock.yaml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/hwlock/qcom-hwspinlock.yaml b/Documentation/devicetree/bindings/hwlock/qcom-hwspinlock.yaml index 1c7149f7d171..de98b961fb38 100644 --- a/Documentation/devicetree/bindings/hwlock/qcom-hwspinlock.yaml +++ b/Documentation/devicetree/bindings/hwlock/qcom-hwspinlock.yaml @@ -15,9 +15,18 @@ description: properties: compatible: - enum: - - qcom,sfpb-mutex - - qcom,tcsr-mutex + oneOf: + - enum: + - qcom,sfpb-mutex + - qcom,tcsr-mutex + - items: + - enum: + - qcom,apq8084-tcsr-mutex + - qcom,ipq6018-tcsr-mutex + - qcom,msm8226-tcsr-mutex + - qcom,msm8974-tcsr-mutex + - qcom,msm8994-tcsr-mutex + - const: qcom,tcsr-mutex reg: maxItems: 1 From 276a4f1a5fb118bfd6980d42732d530e43f2916a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 9 Sep 2022 11:20:22 +0200 Subject: [PATCH 0007/1823] dt-bindings: hwlock: qcom-hwspinlock: correct example indentation Use some consistent indentation (4-space) for DTS example. Signed-off-by: Krzysztof Kozlowski Acked-by: Rob Herring Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220909092035.223915-3-krzysztof.kozlowski@linaro.org --- .../devicetree/bindings/hwlock/qcom-hwspinlock.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/hwlock/qcom-hwspinlock.yaml b/Documentation/devicetree/bindings/hwlock/qcom-hwspinlock.yaml index de98b961fb38..1a3adf75934b 100644 --- a/Documentation/devicetree/bindings/hwlock/qcom-hwspinlock.yaml +++ b/Documentation/devicetree/bindings/hwlock/qcom-hwspinlock.yaml @@ -43,9 +43,9 @@ additionalProperties: false examples: - | - tcsr_mutex: hwlock@1f40000 { - compatible = "qcom,tcsr-mutex"; - reg = <0x01f40000 0x40000>; - #hwlock-cells = <1>; - }; + hwlock@1f40000 { + compatible = "qcom,tcsr-mutex"; + reg = <0x01f40000 0x40000>; + #hwlock-cells = <1>; + }; ... From 90cb380f9ceb811059340d06ff5fd0c0e93ecbe1 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 9 Sep 2022 11:20:23 +0200 Subject: [PATCH 0008/1823] hwspinlock: qcom: correct MMIO max register for newer SoCs Newer ARMv8 Qualcomm SoCs using 0x1000 register stride have maximum register 0x20000 (32 mutexes * 0x1000). Fixes: 7a1e6fb1c606 ("hwspinlock: qcom: Allow mmio usage in addition to syscon") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220909092035.223915-4-krzysztof.kozlowski@linaro.org --- drivers/hwspinlock/qcom_hwspinlock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwspinlock/qcom_hwspinlock.c b/drivers/hwspinlock/qcom_hwspinlock.c index 80ea45b3a815..9734e149d981 100644 --- a/drivers/hwspinlock/qcom_hwspinlock.c +++ b/drivers/hwspinlock/qcom_hwspinlock.c @@ -121,7 +121,7 @@ static const struct regmap_config tcsr_mutex_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, - .max_register = 0x40000, + .max_register = 0x20000, .fast_io = true, }; From 5d4753f741d824e04e7ba46f46ec016be120f383 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 9 Sep 2022 11:20:24 +0200 Subject: [PATCH 0009/1823] hwspinlock: qcom: add support for MMIO on older SoCs Older Qualcomm SoCs have TCSR mutex registers with 0x80 stride, instead of 0x1000. Add dedicated compatibles and regmap for such case. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220909092035.223915-5-krzysztof.kozlowski@linaro.org --- drivers/hwspinlock/qcom_hwspinlock.c | 42 +++++++++++++++++++++------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/drivers/hwspinlock/qcom_hwspinlock.c b/drivers/hwspinlock/qcom_hwspinlock.c index 9734e149d981..9cf186362ae2 100644 --- a/drivers/hwspinlock/qcom_hwspinlock.c +++ b/drivers/hwspinlock/qcom_hwspinlock.c @@ -22,6 +22,7 @@ struct qcom_hwspinlock_of_data { u32 offset; u32 stride; + const struct regmap_config *regmap_config; }; static int qcom_hwspinlock_trylock(struct hwspinlock *lock) @@ -73,15 +74,42 @@ static const struct qcom_hwspinlock_of_data of_sfpb_mutex = { .stride = 0x4, }; -/* All modern platform has offset 0 and stride of 4k */ +static const struct regmap_config tcsr_msm8226_mutex_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1000, + .fast_io = true, +}; + +static const struct qcom_hwspinlock_of_data of_msm8226_tcsr_mutex = { + .offset = 0, + .stride = 0x80, + .regmap_config = &tcsr_msm8226_mutex_config, +}; + +static const struct regmap_config tcsr_mutex_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x20000, + .fast_io = true, +}; + static const struct qcom_hwspinlock_of_data of_tcsr_mutex = { .offset = 0, .stride = 0x1000, + .regmap_config = &tcsr_mutex_config, }; static const struct of_device_id qcom_hwspinlock_of_match[] = { { .compatible = "qcom,sfpb-mutex", .data = &of_sfpb_mutex }, { .compatible = "qcom,tcsr-mutex", .data = &of_tcsr_mutex }, + { .compatible = "qcom,apq8084-tcsr-mutex", .data = &of_msm8226_tcsr_mutex }, + { .compatible = "qcom,ipq6018-tcsr-mutex", .data = &of_msm8226_tcsr_mutex }, + { .compatible = "qcom,msm8226-tcsr-mutex", .data = &of_msm8226_tcsr_mutex }, + { .compatible = "qcom,msm8974-tcsr-mutex", .data = &of_msm8226_tcsr_mutex }, + { .compatible = "qcom,msm8994-tcsr-mutex", .data = &of_msm8226_tcsr_mutex }, { } }; MODULE_DEVICE_TABLE(of, qcom_hwspinlock_of_match); @@ -117,14 +145,6 @@ static struct regmap *qcom_hwspinlock_probe_syscon(struct platform_device *pdev, return regmap; } -static const struct regmap_config tcsr_mutex_config = { - .reg_bits = 32, - .reg_stride = 4, - .val_bits = 32, - .max_register = 0x20000, - .fast_io = true, -}; - static struct regmap *qcom_hwspinlock_probe_mmio(struct platform_device *pdev, u32 *offset, u32 *stride) { @@ -133,6 +153,8 @@ static struct regmap *qcom_hwspinlock_probe_mmio(struct platform_device *pdev, void __iomem *base; data = of_device_get_match_data(dev); + if (!data->regmap_config) + return ERR_PTR(-EINVAL); *offset = data->offset; *stride = data->stride; @@ -141,7 +163,7 @@ static struct regmap *qcom_hwspinlock_probe_mmio(struct platform_device *pdev, if (IS_ERR(base)) return ERR_CAST(base); - return devm_regmap_init_mmio(dev, base, &tcsr_mutex_config); + return devm_regmap_init_mmio(dev, base, data->regmap_config); } static int qcom_hwspinlock_probe(struct platform_device *pdev) From 3a5913183aa1b14148c723bda030e6102ad73008 Mon Sep 17 00:00:00 2001 From: Eyal Birger Date: Sun, 9 Oct 2022 22:16:43 +0300 Subject: [PATCH 0010/1823] xfrm: fix "disable_policy" on ipv4 early demux The commit in the "Fixes" tag tried to avoid a case where policy check is ignored due to dst caching in next hops. However, when the traffic is locally consumed, the dst may be cached in a local TCP or UDP socket as part of early demux. In this case the "disable_policy" flag is not checked as ip_route_input_noref() was only called before caching, and thus, packets after the initial packet in a flow will be dropped if not matching policies. Fix by checking the "disable_policy" flag also when a valid dst is already available. Link: https://bugzilla.kernel.org/show_bug.cgi?id=216557 Reported-by: Monil Patel Fixes: e6175a2ed1f1 ("xfrm: fix "disable_policy" flag use when arriving from different devices") Signed-off-by: Eyal Birger ---- v2: use dev instead of skb->dev Signed-off-by: Steffen Klassert --- net/ipv4/ip_input.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index 1b512390b3cf..e880ce77322a 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -366,6 +366,11 @@ static int ip_rcv_finish_core(struct net *net, struct sock *sk, iph->tos, dev); if (unlikely(err)) goto drop_error; + } else { + struct in_device *in_dev = __in_dev_get_rcu(dev); + + if (in_dev && IN_DEV_ORCONF(in_dev, NOPOLICY)) + IPCB(skb)->flags |= IPSKB_NOPOLICY; } #ifdef CONFIG_IP_ROUTE_CLASSID From d83f7040e18489265b4b121f33f99b02e52dabda Mon Sep 17 00:00:00 2001 From: Eyal Birger Date: Tue, 11 Oct 2022 11:01:37 +0300 Subject: [PATCH 0011/1823] xfrm: lwtunnel: squelch kernel warning in case XFRM encap type is not available Ido reported that a kernel warning [1] can be triggered from user space when the kernel is compiled with CONFIG_MODULES=y and CONFIG_XFRM=n when adding an xfrm encap type route, e.g: $ ip route add 198.51.100.0/24 dev dummy1 encap xfrm if_id 1 Error: lwt encapsulation type not supported. The reason for the warning is that the LWT infrastructure has an autoloading feature which is meant only for encap types that don't use a net device, which is not the case in xfrm encap. Mute this warning for xfrm encap as there's no encap module to autoload in this case. [1] WARNING: CPU: 3 PID: 2746262 at net/core/lwtunnel.c:57 lwtunnel_valid_encap_type+0x4f/0x120 [...] Call Trace: rtm_to_fib_config+0x211/0x350 inet_rtm_newroute+0x3a/0xa0 rtnetlink_rcv_msg+0x154/0x3c0 netlink_rcv_skb+0x49/0xf0 netlink_unicast+0x22f/0x350 netlink_sendmsg+0x208/0x440 ____sys_sendmsg+0x21f/0x250 ___sys_sendmsg+0x83/0xd0 __sys_sendmsg+0x54/0xa0 do_syscall_64+0x35/0x80 entry_SYSCALL_64_after_hwframe+0x63/0xcd Reported-by: Ido Schimmel Fixes: 2c2493b9da91 ("xfrm: lwtunnel: add lwtunnel support for xfrm interfaces in collect_md mode") Signed-off-by: Eyal Birger Tested-by: Ido Schimmel Reviewed-by: Nikolay Aleksandrov Signed-off-by: Steffen Klassert --- net/core/lwtunnel.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/core/lwtunnel.c b/net/core/lwtunnel.c index 6fac2f0ef074..711cd3b4347a 100644 --- a/net/core/lwtunnel.c +++ b/net/core/lwtunnel.c @@ -48,9 +48,11 @@ static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type) return "RPL"; case LWTUNNEL_ENCAP_IOAM6: return "IOAM6"; + case LWTUNNEL_ENCAP_XFRM: + /* module autoload not supported for encap type */ + return NULL; case LWTUNNEL_ENCAP_IP6: case LWTUNNEL_ENCAP_IP: - case LWTUNNEL_ENCAP_XFRM: case LWTUNNEL_ENCAP_NONE: case __LWTUNNEL_ENCAP_MAX: /* should not have got here */ From ac5408991ea6b06e29129b4d4861097c4c3e0d59 Mon Sep 17 00:00:00 2001 From: Aman Dhoot Date: Sat, 15 Oct 2022 20:41:17 -0700 Subject: [PATCH 0012/1823] Input: synaptics - switch touchpad on HP Laptop 15-da3001TU to RMI mode The device works fine in native RMI mode, there is no reason to use legacy PS/2 mode with it. Signed-off-by: Aman Dhoot Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index fa021af8506e..b0f776448a1c 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -192,6 +192,7 @@ static const char * const smbus_pnp_ids[] = { "SYN3221", /* HP 15-ay000 */ "SYN323d", /* HP Spectre X360 13-w013dx */ "SYN3257", /* HP Envy 13-ad105ng */ + "SYN3286", /* HP Laptop 15-da3001TU */ NULL }; From a01aca4b05174b6dee2392ec44406f85e0f8bd46 Mon Sep 17 00:00:00 2001 From: Sheng-Liang Pan Date: Sun, 16 Oct 2022 21:47:46 -0700 Subject: [PATCH 0013/1823] dt-bindings: input: touchscreen: Add compatible for Goodix GT7986U chip Add a compatible for Goodix touch screen chip GT7986U which is is expected to be fully compatible with a driver written for GT7375P. Signed-off-by: Sheng-Liang Pan Reviewed-by: Douglas Anderson Acked-by: Rob Herring Link: https://lore.kernel.org/r/20221006185333.v7.3.I52e4b4b20e2eb0ae20f2a9bb198aa6410f04cf16@changeid Signed-off-by: Dmitry Torokhov --- Documentation/devicetree/bindings/input/goodix,gt7375p.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/input/goodix,gt7375p.yaml b/Documentation/devicetree/bindings/input/goodix,gt7375p.yaml index fe1c5016f7f3..1c191bc5a178 100644 --- a/Documentation/devicetree/bindings/input/goodix,gt7375p.yaml +++ b/Documentation/devicetree/bindings/input/goodix,gt7375p.yaml @@ -16,8 +16,11 @@ description: properties: compatible: - items: + oneOf: - const: goodix,gt7375p + - items: + - const: goodix,gt7986u + - const: goodix,gt7375p reg: enum: From 76845ba539c3693f572377b38563d19a87266a5b Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 14 Oct 2022 11:54:24 +0530 Subject: [PATCH 0014/1823] MAINTAINERS: Update Kishon's email address in GENERIC PHY FRAMEWORK Update Kishon's email address in GENERIC PHY FRAMEWORK maintainer entry. Cc: Kishon Vijay Abraham I Signed-off-by: Kishon Vijay Abraham I Link: https://lore.kernel.org/r/20221014062424.3327-1-kishon@ti.com Signed-off-by: Vinod Koul --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index cf0f18502372..2526755bf977 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8595,8 +8595,8 @@ F: include/asm-generic/ F: include/uapi/asm-generic/ GENERIC PHY FRAMEWORK -M: Kishon Vijay Abraham I M: Vinod Koul +M: Kishon Vijay Abraham I L: linux-phy@lists.infradead.org S: Supported Q: https://patchwork.kernel.org/project/linux-phy/list/ From ca1c73628f5bd0c1ef6e46073cc3be2450605b06 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 14 Oct 2022 12:25:06 +0300 Subject: [PATCH 0015/1823] phy: stm32: fix an error code in probe If "index > usbphyc->nphys" is true then this returns success but it should return -EINVAL. Fixes: 94c358da3a05 ("phy: stm32: add support for STM32 USB PHY Controller (USBPHYC)") Signed-off-by: Dan Carpenter Reviewed-by: Amelie Delaunay Link: https://lore.kernel.org/r/Y0kq8j6S+5nDdMpr@kili Signed-off-by: Vinod Koul --- drivers/phy/st/phy-stm32-usbphyc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/phy/st/phy-stm32-usbphyc.c b/drivers/phy/st/phy-stm32-usbphyc.c index a98c911cc37a..5bb9647b078f 100644 --- a/drivers/phy/st/phy-stm32-usbphyc.c +++ b/drivers/phy/st/phy-stm32-usbphyc.c @@ -710,6 +710,8 @@ static int stm32_usbphyc_probe(struct platform_device *pdev) ret = of_property_read_u32(child, "reg", &index); if (ret || index > usbphyc->nphys) { dev_err(&phy->dev, "invalid reg property: %d\n", ret); + if (!ret) + ret = -EINVAL; goto put_child; } From 2a4ea83bb8e54986703187edce59c9130a75eb26 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Mon, 10 Oct 2022 14:51:32 +0100 Subject: [PATCH 0016/1823] phy: tegra: xusb: Fix crash during pad power on/down Commit a88520bfc0ec ("usb: gadget: tegra: Reduce pad power") added calls to tegra_phy_xusb_utmi_pad_power_on/down in the Tegra XUDC driver to control the pad power. This change is causing a kernel panic when powering down the pads on entering suspend with the Jetson TX2 platform. The panic occurs because the 'xudc->curr_utmi_phy' is not configured on this platform and we do not check to see if the pointer is valid before attempting to deference the pointer. Fix this by checking to see if the 'phy' pointer passed to tegra_phy_xusb_utmi_pad_power_on/down is valid. Fixes: a88520bfc0ec ("usb: gadget: tegra: Reduce pad power") Signed-off-by: Jon Hunter Link: https://lore.kernel.org/r/20221010135132.30809-1-jonathanh@nvidia.com Signed-off-by: Vinod Koul --- drivers/phy/tegra/xusb.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c index 95091876c422..dce45fbbd699 100644 --- a/drivers/phy/tegra/xusb.c +++ b/drivers/phy/tegra/xusb.c @@ -1461,8 +1461,14 @@ EXPORT_SYMBOL_GPL(tegra_phy_xusb_utmi_port_reset); void tegra_phy_xusb_utmi_pad_power_on(struct phy *phy) { - struct tegra_xusb_lane *lane = phy_get_drvdata(phy); - struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra_xusb_lane *lane; + struct tegra_xusb_padctl *padctl; + + if (!phy) + return; + + lane = phy_get_drvdata(phy); + padctl = lane->pad->padctl; if (padctl->soc->ops->utmi_pad_power_on) padctl->soc->ops->utmi_pad_power_on(phy); @@ -1471,8 +1477,14 @@ EXPORT_SYMBOL_GPL(tegra_phy_xusb_utmi_pad_power_on); void tegra_phy_xusb_utmi_pad_power_down(struct phy *phy) { - struct tegra_xusb_lane *lane = phy_get_drvdata(phy); - struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra_xusb_lane *lane; + struct tegra_xusb_padctl *padctl; + + if (!phy) + return; + + lane = phy_get_drvdata(phy); + padctl = lane->pad->padctl; if (padctl->soc->ops->utmi_pad_power_down) padctl->soc->ops->utmi_pad_power_down(phy); From ef40e88d1be172ca05e6e8cef00284a1cf4f43d8 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Thu, 13 Oct 2022 14:33:35 -0700 Subject: [PATCH 0017/1823] arm64: dts: rockchip: Drop RK3399-Scarlet's repeated ec_ap_int_l definition This is repeated a few lines down. Signed-off-by: Brian Norris Link: https://lore.kernel.org/r/20221013213336.1779917-1-briannorris@chromium.org Signed-off-by: Heiko Stuebner --- arch/arm64/boot/dts/rockchip/rk3399-gru-scarlet.dtsi | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3399-gru-scarlet.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-gru-scarlet.dtsi index ed3348b558f8..a47d9f758611 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-gru-scarlet.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399-gru-scarlet.dtsi @@ -734,10 +734,6 @@ camera: &i2c7 { }; /* PINCTRL OVERRIDES */ -&ec_ap_int_l { - rockchip,pins = <1 RK_PC2 RK_FUNC_GPIO &pcfg_pull_up>; -}; - &ap_fw_wp { rockchip,pins = <0 RK_PB5 RK_FUNC_GPIO &pcfg_pull_none>; }; From adbab347ec8861aa80d850693df3cd005ec65a99 Mon Sep 17 00:00:00 2001 From: Furkan Kardame Date: Mon, 10 Oct 2022 22:01:40 +0300 Subject: [PATCH 0018/1823] arm64: dts: rockchip: Fix gmac failure of rgmii-id from rk3566-roc-pc Lan does not work on rgmii-id, most rk356x devices lan is being switched to rgmii. Signed-off-by: Furkan Kardame Link: https://lore.kernel.org/r/20221010190142.18340-2-f.kardame@manjaro.org Signed-off-by: Heiko Stuebner --- arch/arm64/boot/dts/rockchip/rk3566-roc-pc.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3566-roc-pc.dts b/arch/arm64/boot/dts/rockchip/rk3566-roc-pc.dts index dba648c2f57e..72c91d2fcd26 100644 --- a/arch/arm64/boot/dts/rockchip/rk3566-roc-pc.dts +++ b/arch/arm64/boot/dts/rockchip/rk3566-roc-pc.dts @@ -142,7 +142,7 @@ assigned-clocks = <&cru SCLK_GMAC1_RX_TX>, <&cru SCLK_GMAC1_RGMII_SPEED>, <&cru SCLK_GMAC1>; assigned-clock-parents = <&cru SCLK_GMAC1_RGMII_SPEED>, <&cru SCLK_GMAC1>, <&gmac1_clkin>; clock_in_out = "input"; - phy-mode = "rgmii-id"; + phy-mode = "rgmii"; phy-supply = <&vcc_3v3>; pinctrl-names = "default"; pinctrl-0 = <&gmac1m0_miim From 2440ad0d851e404adcd1b9ad758f28bd59365bae Mon Sep 17 00:00:00 2001 From: Furkan Kardame Date: Mon, 10 Oct 2022 22:01:41 +0300 Subject: [PATCH 0019/1823] arm64: dts: rockchip: Fix i2c3 pinctrl on rk3566-roc-pc As per device schematic i2c3 pinctrl is connected to m0 instead of m1 Signed-off-by: Furkan Kardame Link: https://lore.kernel.org/r/20221010190142.18340-3-f.kardame@manjaro.org Signed-off-by: Heiko Stuebner --- arch/arm64/boot/dts/rockchip/rk3566-roc-pc.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3566-roc-pc.dts b/arch/arm64/boot/dts/rockchip/rk3566-roc-pc.dts index 72c91d2fcd26..72809246c4c1 100644 --- a/arch/arm64/boot/dts/rockchip/rk3566-roc-pc.dts +++ b/arch/arm64/boot/dts/rockchip/rk3566-roc-pc.dts @@ -432,7 +432,7 @@ &i2c3 { pinctrl-names = "default"; - pinctrl-0 = <&i2c3m1_xfer>; + pinctrl-0 = <&i2c3m0_xfer>; status = "okay"; }; From b44bc59d0d279fa4f3dc11b895f2c8f77719885d Mon Sep 17 00:00:00 2001 From: Furkan Kardame Date: Mon, 10 Oct 2022 22:01:42 +0300 Subject: [PATCH 0020/1823] arm64: dts: rockchip: remove i2c5 from rk3566-roc-pc i2c5 is owned by hdmi port Signed-off-by: Furkan Kardame Link: https://lore.kernel.org/r/20221010190142.18340-4-f.kardame@manjaro.org Signed-off-by: Heiko Stuebner --- arch/arm64/boot/dts/rockchip/rk3566-roc-pc.dts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3566-roc-pc.dts b/arch/arm64/boot/dts/rockchip/rk3566-roc-pc.dts index 72809246c4c1..9fd262334d77 100644 --- a/arch/arm64/boot/dts/rockchip/rk3566-roc-pc.dts +++ b/arch/arm64/boot/dts/rockchip/rk3566-roc-pc.dts @@ -436,10 +436,6 @@ status = "okay"; }; -&i2c5 { - status = "okay"; -}; - &mdio1 { rgmii_phy1: ethernet-phy@0 { compatible = "ethernet-phy-ieee802.3-c22"; From 463be3cb357dab7d7e4d8dcc7c15c642e10c5bef Mon Sep 17 00:00:00 2001 From: Jensen Huang Date: Wed, 28 Sep 2022 17:11:29 +0800 Subject: [PATCH 0021/1823] arm64: dts: rockchip: add enable-strobe-pulldown to emmc phy on nanopi4 Internal pull-down for strobe line (GRF_EMMCPHY_CON2[9]) was disabled by commit 8b5c2b45b8f0, which causes I/O error in HS400 mode. Tested on NanoPC-T4. Fixes: 8b5c2b45b8f0 ("phy: rockchip: set pulldown for strobe line in dts") Signed-off-by: Jensen Huang Link: https://lore.kernel.org/r/20220928091129.20597-1-jensenhuang@friendlyarm.com Signed-off-by: Heiko Stuebner --- arch/arm64/boot/dts/rockchip/rk3399-nanopi4.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3399-nanopi4.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-nanopi4.dtsi index 278123b4f911..b6e082f1f6d9 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-nanopi4.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399-nanopi4.dtsi @@ -167,6 +167,7 @@ }; &emmc_phy { + rockchip,enable-strobe-pulldown; status = "okay"; }; From 2dcd7e0c821fe9b663f7d3382b6d2faa8edf2129 Mon Sep 17 00:00:00 2001 From: Lev Popov Date: Mon, 26 Sep 2022 14:53:48 +0200 Subject: [PATCH 0022/1823] arm64: dts: rockchip: fix quartz64-a bluetooth configuration For "Quartz64 Model A" add missing RTS line to the UART interface used by bluetooth and swap bluetooth host-wakeup and device-wakeup gpio pins to match the boards physical layout. This changes are necessary to make bluetooth provided by the wireless module work. Also set max-speed on the bluetooth device as it's not automatically detected. Fixes: b33a22a1e7c4 ("arm64: dts: rockchip: add basic dts for Pine64 Quartz64-A") Signed-off-by: Lev Popov Link: https://lore.kernel.org/r/20220926125350.64783-1-leo@nabam.net Signed-off-by: Heiko Stuebner --- arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts b/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts index a05460b92415..25a8c781f4e7 100644 --- a/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts +++ b/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts @@ -740,7 +740,7 @@ &uart1 { pinctrl-names = "default"; - pinctrl-0 = <&uart1m0_xfer &uart1m0_ctsn>; + pinctrl-0 = <&uart1m0_xfer &uart1m0_ctsn &uart1m0_rtsn>; status = "okay"; uart-has-rtscts; @@ -748,13 +748,14 @@ compatible = "brcm,bcm43438-bt"; clocks = <&rk817 1>; clock-names = "lpo"; - device-wakeup-gpios = <&gpio2 RK_PC1 GPIO_ACTIVE_HIGH>; - host-wakeup-gpios = <&gpio2 RK_PC0 GPIO_ACTIVE_HIGH>; + host-wakeup-gpios = <&gpio2 RK_PC1 GPIO_ACTIVE_HIGH>; + device-wakeup-gpios = <&gpio2 RK_PC0 GPIO_ACTIVE_HIGH>; shutdown-gpios = <&gpio2 RK_PB7 GPIO_ACTIVE_HIGH>; pinctrl-names = "default"; pinctrl-0 = <&bt_host_wake_l &bt_wake_l &bt_enable_h>; vbat-supply = <&vcc_sys>; vddio-supply = <&vcca1v8_pmu>; + max-speed = <3000000>; }; }; From 849c19d14940b87332d5d59c7fc581d73f2099fd Mon Sep 17 00:00:00 2001 From: FUKAUMI Naoki Date: Sat, 24 Sep 2022 11:28:12 +0000 Subject: [PATCH 0023/1823] arm64: dts: rockchip: keep I2S1 disabled for GPIO function on ROCK Pi 4 series I2S1 pins are exposed on 40-pin header on Radxa ROCK Pi 4 series. their default function is GPIO, so I2S1 need to be disabled. Signed-off-by: FUKAUMI Naoki Link: https://lore.kernel.org/r/20220924112812.1219-1-naoki@radxa.com Signed-off-by: Heiko Stuebner --- arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dtsi | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dtsi index 645ced6617a6..1f76d3501bda 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dtsi @@ -509,7 +509,6 @@ &i2s1 { rockchip,playback-channels = <2>; rockchip,capture-channels = <2>; - status = "okay"; }; &i2s2 { From 10f3f10f202109e3841c0c75add5a743ca197205 Mon Sep 17 00:00:00 2001 From: Palmer Dabbelt Date: Thu, 13 Oct 2022 14:46:39 -0700 Subject: [PATCH 0024/1823] MAINTAINERS: git://github -> https://github.com for broadcom MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Github deprecated the git:// links about a year ago, so let's move to the https:// URLs instead. Acked-by: William Zhang Reviewed-by: Philippe Mathieu-Daudé Reported-by: Conor Dooley Link: https://github.blog/2021-09-01-improving-git-protocol-security-github/ Signed-off-by: Palmer Dabbelt Signed-off-by: Florian Fainelli --- MAINTAINERS | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index cf0f18502372..83612d907bed 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3984,7 +3984,7 @@ M: Rafał Miłecki R: Broadcom internal kernel review list L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained -T: git git://github.com/broadcom/stblinux.git +T: git https://github.com/broadcom/stblinux.git F: Documentation/devicetree/bindings/arm/bcm/brcm,bcmbca.yaml F: arch/arm64/boot/dts/broadcom/bcmbca/* N: bcmbca @@ -4009,7 +4009,7 @@ R: Broadcom internal kernel review list L: linux-rpi-kernel@lists.infradead.org (moderated for non-subscribers) L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained -T: git git://github.com/broadcom/stblinux.git +T: git https://github.com/broadcom/stblinux.git F: Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml F: drivers/pci/controller/pcie-brcmstb.c F: drivers/staging/vc04_services @@ -4023,7 +4023,7 @@ M: Ray Jui M: Scott Branden R: Broadcom internal kernel review list S: Maintained -T: git git://github.com/broadcom/mach-bcm +T: git https://github.com/broadcom/mach-bcm F: arch/arm/mach-bcm/ N: bcm281* N: bcm113* @@ -4088,7 +4088,7 @@ M: Florian Fainelli R: Broadcom internal kernel review list L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained -T: git git://github.com/broadcom/stblinux.git +T: git https://github.com/broadcom/stblinux.git F: Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml F: arch/arm/boot/dts/bcm7*.dts* F: arch/arm/include/asm/hardware/cache-b15-rac.h @@ -4120,7 +4120,7 @@ M: Florian Fainelli R: Broadcom internal kernel review list L: linux-mips@vger.kernel.org S: Maintained -T: git git://github.com/broadcom/stblinux.git +T: git https://github.com/broadcom/stblinux.git F: arch/mips/bmips/* F: arch/mips/boot/dts/brcm/bcm*.dts* F: arch/mips/include/asm/mach-bmips/* @@ -4259,7 +4259,7 @@ M: Scott Branden R: Broadcom internal kernel review list L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained -T: git git://github.com/broadcom/stblinux.git +T: git https://github.com/broadcom/stblinux.git F: arch/arm64/boot/dts/broadcom/northstar2/* F: arch/arm64/boot/dts/broadcom/stingray/* F: drivers/clk/bcm/clk-ns* @@ -4329,7 +4329,7 @@ M: Florian Fainelli R: Broadcom internal kernel review list L: linux-pm@vger.kernel.org S: Maintained -T: git git://github.com/broadcom/stblinux.git +T: git https://github.com/broadcom/stblinux.git F: drivers/soc/bcm/bcm63xx/bcm-pmb.c F: include/dt-bindings/soc/bcm-pmb.h From 40b21d466a86bd5b10d24f59746ed41283a9b3f6 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Fri, 19 Aug 2022 00:06:27 +0200 Subject: [PATCH 0025/1823] arm64: dts: qcom: ipq8074: correct APCS register space size APCS DTS addition that was merged, was not supposed to get merged as it was part of patch series that was superseded by 2 more patch series that resolved issues with this one and greatly simplified things. Since it already got merged, start by correcting the register space size as APCS will not be providing regmap for PLL and it will conflict with the standalone A53 PLL node. Fixes: 50ed9fffec3a ("arm64: dts: qcom: ipq8074: add APCS node") Signed-off-by: Robert Marko Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220818220628.339366-8-robimarko@gmail.com --- arch/arm64/boot/dts/qcom/ipq8074.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/ipq8074.dtsi b/arch/arm64/boot/dts/qcom/ipq8074.dtsi index a47acf9bdf24..a721cdd80489 100644 --- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi +++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi @@ -668,7 +668,7 @@ apcs_glb: mailbox@b111000 { compatible = "qcom,ipq8074-apcs-apps-global"; - reg = <0x0b111000 0x6000>; + reg = <0x0b111000 0x1000>; #clock-cells = <1>; #mbox-cells = <1>; From 20772f506fa4aab4d03035807f30eecee856e274 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 21 Sep 2022 12:52:58 +0300 Subject: [PATCH 0026/1823] Revert "arm64: dts: qcom: msm8996: add missing TCSR syscon compatible" This reverts commit 8a99e0fc8bd3 ("arm64: dts: qcom: msm8996: add missing TCSR syscon compatible"). This commit marked the saw3 (syscon@9a10000) node as compatible with qcom,tcsr-msm8996. However the mentioned device is not not a TCSR (system registers, hardware mutex). It is a CPU power controller/regulator, which is currently being handled as a syscon. Fixes: 8a99e0fc8bd3 ("arm64: dts: qcom: msm8996: add missing TCSR syscon compatible") Cc: Krzysztof Kozlowski Signed-off-by: Dmitry Baryshkov Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220921095258.2332568-1-dmitry.baryshkov@linaro.org --- arch/arm64/boot/dts/qcom/msm8996.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/msm8996.dtsi b/arch/arm64/boot/dts/qcom/msm8996.dtsi index c0a2baffa49d..aba717644391 100644 --- a/arch/arm64/boot/dts/qcom/msm8996.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8996.dtsi @@ -3504,7 +3504,7 @@ }; saw3: syscon@9a10000 { - compatible = "qcom,tcsr-msm8996", "syscon"; + compatible = "syscon"; reg = <0x09a10000 0x1000>; }; From bd9f3dcf42d943b53190f99bcdbcfe98a56ac4cd Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 29 Aug 2022 09:49:47 -0700 Subject: [PATCH 0027/1823] arm64: dts: qcom: sa8155p-adp: Specify which LDO modes are allowed This board uses RPMH, specifies "regulator-allow-set-load" for LDOs, but doesn't specify any modes with "regulator-allowed-modes". Prior to commit efb0cb50c427 ("regulator: qcom-rpmh: Implement get_optimum_mode(), not set_load()") the above meant that we were able to set either LPM or HPM mode. After that commit (and fixes [1]) we'll be stuck at the initial mode. Discussion of this has resulted in the decision that the old dts files were wrong and should be fixed to fully restore old functionality. Let's re-enable the old functionality by fixing the dts. NOTE: while here, let's also remove the nonsensical "regulator-allow-set-load" on the fixed regulator "vreg_s4a_1p8". [1] https://lore.kernel.org/r/20220824142229.RFT.v2.2.I6f77860e5cd98bf5c67208fa9edda4a08847c304@changeid Fixes: 5b85e8f2225c ("arm64: dts: qcom: sa8155p-adp: Add base dts file") Signed-off-by: Douglas Anderson Reviewed-by: Andrew Halaney Reviewed-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220829094903.v2.1.Id59c32b560c4662d8b3697de2bd494d08d654806@changeid --- arch/arm64/boot/dts/qcom/sa8155p-adp.dts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sa8155p-adp.dts b/arch/arm64/boot/dts/qcom/sa8155p-adp.dts index 87ab0e1ecd16..4dee790f1049 100644 --- a/arch/arm64/boot/dts/qcom/sa8155p-adp.dts +++ b/arch/arm64/boot/dts/qcom/sa8155p-adp.dts @@ -43,7 +43,6 @@ regulator-always-on; regulator-boot-on; - regulator-allow-set-load; vin-supply = <&vreg_3p3>; }; @@ -137,6 +136,9 @@ regulator-max-microvolt = <880000>; regulator-initial-mode = ; regulator-allow-set-load; + regulator-allowed-modes = + ; }; vreg_l7a_1p8: ldo7 { @@ -152,6 +154,9 @@ regulator-max-microvolt = <2960000>; regulator-initial-mode = ; regulator-allow-set-load; + regulator-allowed-modes = + ; }; vreg_l11a_0p8: ldo11 { @@ -258,6 +263,9 @@ regulator-max-microvolt = <1200000>; regulator-initial-mode = ; regulator-allow-set-load; + regulator-allowed-modes = + ; }; vreg_l7c_1p8: ldo7 { @@ -273,6 +281,9 @@ regulator-max-microvolt = <1200000>; regulator-initial-mode = ; regulator-allow-set-load; + regulator-allowed-modes = + ; }; vreg_l10c_3p3: ldo10 { From 09a1710b3e12e7ac8d871506bc395a9e8a0f63d6 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 29 Aug 2022 09:49:48 -0700 Subject: [PATCH 0028/1823] arm64: dts: qcom: sa8295p-adp: Specify which LDO modes are allowed This board uses RPMH, specifies "regulator-allow-set-load" for LDOs, but doesn't specify any modes with "regulator-allowed-modes". Prior to commit efb0cb50c427 ("regulator: qcom-rpmh: Implement get_optimum_mode(), not set_load()") the above meant that we were able to set either LPM or HPM mode. After that commit (and fixes [1]) we'll be stuck at the initial mode. Discussion of this has resulted in the decision that the old dts files were wrong and should be fixed to fully restore old functionality. Let's re-enable the old functionality by fixing the dts. [1] https://lore.kernel.org/r/20220824142229.RFT.v2.2.I6f77860e5cd98bf5c67208fa9edda4a08847c304@changeid Fixes: 519183af39b2 ("arm64: dts: qcom: add SA8540P and ADP") Signed-off-by: Douglas Anderson Reviewed-by: Andrew Halaney Reviewed-by: Konrad Dybcio Reviewed-by: Johan Hovold Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220829094903.v2.2.I430a56702ab0af65244e62667bb7743107de0c96@changeid --- arch/arm64/boot/dts/qcom/sa8295p-adp.dts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/sa8295p-adp.dts b/arch/arm64/boot/dts/qcom/sa8295p-adp.dts index b608b82dff03..2c62ba6a49c5 100644 --- a/arch/arm64/boot/dts/qcom/sa8295p-adp.dts +++ b/arch/arm64/boot/dts/qcom/sa8295p-adp.dts @@ -83,6 +83,9 @@ regulator-max-microvolt = <1200000>; regulator-initial-mode = ; regulator-allow-set-load; + regulator-allowed-modes = + ; }; vreg_l4c: ldo4 { @@ -98,6 +101,9 @@ regulator-max-microvolt = <1200000>; regulator-initial-mode = ; regulator-allow-set-load; + regulator-allowed-modes = + ; }; vreg_l7c: ldo7 { @@ -113,6 +119,9 @@ regulator-max-microvolt = <2504000>; regulator-initial-mode = ; regulator-allow-set-load; + regulator-allowed-modes = + ; }; vreg_l17c: ldo17 { @@ -121,6 +130,9 @@ regulator-max-microvolt = <2504000>; regulator-initial-mode = ; regulator-allow-set-load; + regulator-allowed-modes = + ; }; }; From a4543e21ae363f4f094fb3c3503d77029e0c5d90 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 29 Aug 2022 09:49:49 -0700 Subject: [PATCH 0029/1823] arm64: dts: qcom: sc8280xp-crd: Specify which LDO modes are allowed This board uses RPMH, specifies "regulator-allow-set-load" for LDOs, but doesn't specify any modes with "regulator-allowed-modes". Prior to commit efb0cb50c427 ("regulator: qcom-rpmh: Implement get_optimum_mode(), not set_load()") the above meant that we were able to set either LPM or HPM mode. After that commit (and fixes [1]) we'll be stuck at the initial mode. Discussion of this has resulted in the decision that the old dts files were wrong and should be fixed to fully restore old functionality. Let's re-enable the old functionality by fixing the dts. [1] https://lore.kernel.org/r/20220824142229.RFT.v2.2.I6f77860e5cd98bf5c67208fa9edda4a08847c304@changeid Fixes: ccd3517faf18 ("arm64: dts: qcom: sc8280xp: Add reference device") Signed-off-by: Douglas Anderson Reviewed-by: Andrew Halaney Reviewed-by: Konrad Dybcio Reviewed-by: Johan Hovold Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220829094903.v2.3.Ie7d2c50d2b42ef2d364f3a0c8e300e5ce1875b79@changeid --- arch/arm64/boot/dts/qcom/sc8280xp-crd.dts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/sc8280xp-crd.dts b/arch/arm64/boot/dts/qcom/sc8280xp-crd.dts index fea7d8273ccd..5e30349efd20 100644 --- a/arch/arm64/boot/dts/qcom/sc8280xp-crd.dts +++ b/arch/arm64/boot/dts/qcom/sc8280xp-crd.dts @@ -124,6 +124,9 @@ regulator-max-microvolt = <2504000>; regulator-initial-mode = ; regulator-allow-set-load; + regulator-allowed-modes = + ; }; vreg_l13c: ldo13 { @@ -146,6 +149,9 @@ regulator-max-microvolt = <1200000>; regulator-initial-mode = ; regulator-allow-set-load; + regulator-allowed-modes = + ; }; vreg_l4d: ldo4 { From aa30e786202e4ed1df980442d305658441f65859 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 29 Aug 2022 09:49:50 -0700 Subject: [PATCH 0030/1823] arm64: dts: qcom: sm8150-xperia-kumano: Specify which LDO modes are allowed This board uses RPMH, specifies "regulator-allow-set-load" for LDOs, but doesn't specify any modes with "regulator-allowed-modes". Prior to commit efb0cb50c427 ("regulator: qcom-rpmh: Implement get_optimum_mode(), not set_load()") the above meant that we were able to set either LPM or HPM mode. After that commit (and fixes [1]) we'll be stuck at the initial mode. Discussion of this has resulted in the decision that the old dts files were wrong and should be fixed to fully restore old functionality. Let's re-enable the old functionality by fixing the dts. [1] https://lore.kernel.org/r/20220824142229.RFT.v2.2.I6f77860e5cd98bf5c67208fa9edda4a08847c304@changeid Fixes: d0a6ce59ea4e ("arm64: dts: qcom: sm8150: Add support for SONY Xperia 1 / 5 (Kumano platform)") Signed-off-by: Douglas Anderson Reviewed-by: Andrew Halaney Reviewed-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220829094903.v2.4.I51d60414a42ba9e3008e208d60a04c9ffc425fa7@changeid --- arch/arm64/boot/dts/qcom/sm8150-sony-xperia-kumano.dtsi | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/sm8150-sony-xperia-kumano.dtsi b/arch/arm64/boot/dts/qcom/sm8150-sony-xperia-kumano.dtsi index 014fe3a31548..fb6e5a140c9f 100644 --- a/arch/arm64/boot/dts/qcom/sm8150-sony-xperia-kumano.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150-sony-xperia-kumano.dtsi @@ -348,6 +348,9 @@ regulator-max-microvolt = <2960000>; regulator-initial-mode = ; regulator-allow-set-load; + regulator-allowed-modes = + ; }; vreg_l7c_3p0: ldo7 { @@ -367,6 +370,9 @@ regulator-max-microvolt = <2960000>; regulator-initial-mode = ; regulator-allow-set-load; + regulator-allowed-modes = + ; }; vreg_l10c_3p3: ldo10 { From b7870d460c05ce31e2311036d91de1e2e0b32cea Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 29 Aug 2022 09:49:51 -0700 Subject: [PATCH 0031/1823] arm64: dts: qcom: sm8250-xperia-edo: Specify which LDO modes are allowed This board uses RPMH, specifies "regulator-allow-set-load" for LDOs, but doesn't specify any modes with "regulator-allowed-modes". Prior to commit efb0cb50c427 ("regulator: qcom-rpmh: Implement get_optimum_mode(), not set_load()") the above meant that we were able to set either LPM or HPM mode. After that commit (and fixes [1]) we'll be stuck at the initial mode. Discussion of this has resulted in the decision that the old dts files were wrong and should be fixed to fully restore old functionality. Let's re-enable the old functionality by fixing the dts. [1] https://lore.kernel.org/r/20220824142229.RFT.v2.2.I6f77860e5cd98bf5c67208fa9edda4a08847c304@changeid Fixes: 69cdb97ef652 ("arm64: dts: qcom: sm8250: Add support for SONY Xperia 1 II / 5 II (Edo platform)") Signed-off-by: Douglas Anderson Reviewed-by: Andrew Halaney Reviewed-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220829094903.v2.5.Ie446d5183d8b1e9ec4e32228ca300e604e3315eb@changeid --- arch/arm64/boot/dts/qcom/sm8250-sony-xperia-edo.dtsi | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/sm8250-sony-xperia-edo.dtsi b/arch/arm64/boot/dts/qcom/sm8250-sony-xperia-edo.dtsi index 549e0a2aa9fe..5428aab3058d 100644 --- a/arch/arm64/boot/dts/qcom/sm8250-sony-xperia-edo.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8250-sony-xperia-edo.dtsi @@ -317,6 +317,9 @@ regulator-max-microvolt = <2960000>; regulator-initial-mode = ; regulator-allow-set-load; + regulator-allowed-modes = + ; }; vreg_l7c_2p85: ldo7 { @@ -339,6 +342,9 @@ regulator-max-microvolt = <2960000>; regulator-initial-mode = ; regulator-allow-set-load; + regulator-allowed-modes = + ; }; vreg_l10c_3p3: ldo10 { From 1ce8aaf6abdc35cde555924418b3d4516b4ec871 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 29 Aug 2022 09:49:52 -0700 Subject: [PATCH 0032/1823] arm64: dts: qcom: sm8350-hdk: Specify which LDO modes are allowed This board uses RPMH, specifies "regulator-allow-set-load" for LDOs, but doesn't specify any modes with "regulator-allowed-modes". Prior to commit efb0cb50c427 ("regulator: qcom-rpmh: Implement get_optimum_mode(), not set_load()") the above meant that we were able to set either LPM or HPM mode. After that commit (and fixes [1]) we'll be stuck at the initial mode. Discussion of this has resulted in the decision that the old dts files were wrong and should be fixed to fully restore old functionality. Let's re-enable the old functionality by fixing the dts. [1] https://lore.kernel.org/r/20220824142229.RFT.v2.2.I6f77860e5cd98bf5c67208fa9edda4a08847c304@changeid Fixes: 9208c19f2124 ("arm64: dts: qcom: Introduce SM8350 HDK") Signed-off-by: Douglas Anderson Reviewed-by: Andrew Halaney Reviewed-by: Vinod Koul Reviewed-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220829094903.v2.6.I6799be85cf36d3b494f803cba767a569080624f5@changeid --- arch/arm64/boot/dts/qcom/sm8350-hdk.dts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/sm8350-hdk.dts b/arch/arm64/boot/dts/qcom/sm8350-hdk.dts index 0fcf5bd88fc7..69ae6503c2f6 100644 --- a/arch/arm64/boot/dts/qcom/sm8350-hdk.dts +++ b/arch/arm64/boot/dts/qcom/sm8350-hdk.dts @@ -107,6 +107,9 @@ regulator-max-microvolt = <888000>; regulator-initial-mode = ; regulator-allow-set-load; + regulator-allowed-modes = + ; }; vreg_l6b_1p2: ldo6 { @@ -115,6 +118,9 @@ regulator-max-microvolt = <1208000>; regulator-initial-mode = ; regulator-allow-set-load; + regulator-allowed-modes = + ; }; vreg_l7b_2p96: ldo7 { @@ -123,6 +129,9 @@ regulator-max-microvolt = <2504000>; regulator-initial-mode = ; regulator-allow-set-load; + regulator-allowed-modes = + ; }; vreg_l9b_1p2: ldo9 { @@ -131,6 +140,9 @@ regulator-max-microvolt = <1200000>; regulator-initial-mode = ; regulator-allow-set-load; + regulator-allowed-modes = + ; }; }; From 8d6b458ce6e93286a607e54f787f7a86067f58bd Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 5 Oct 2022 16:33:05 +0200 Subject: [PATCH 0033/1823] arm64: dts: qcom: sc8280xp: fix ufs_card_phy ref clock The GCC_UFS_REF_CLKREF_CLK must be enabled or the second UFS controller fails to enumerate on sa8295p-adp. Note that the vendor kernel enables both GCC_UFS_REF_CLKREF_CLK and GCC_UFS_1_CARD_CLKREF_CLK and it is possible that the former should be modelled as a parent of the latter. The clock driver also has a GCC_UFS_CARD_CLKREF_CLK clock which the firmware appears to enable on the ADP. The usual lack of documentation for Qualcomm SoCs makes this a highly annoying guessing game, but as the second controller works on the ADP without either card reference clock enabled, only enable GCC_UFS_REF_CLKREF_CLK for now. Fixes: 152d1faf1e2f ("arm64: dts: qcom: add SC8280XP platform") Signed-off-by: Johan Hovold Reviewed-by: Konrad Dybcio Reviewed-by: Brian Masney Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20221005143305.388-1-johan+linaro@kernel.org --- arch/arm64/boot/dts/qcom/sc8280xp.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi index c32bcded2aef..4921bbaf5caf 100644 --- a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi +++ b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi @@ -959,7 +959,7 @@ ranges; clock-names = "ref", "ref_aux"; - clocks = <&gcc GCC_UFS_1_CARD_CLKREF_CLK>, + clocks = <&gcc GCC_UFS_REF_CLKREF_CLK>, <&gcc GCC_UFS_CARD_PHY_AUX_CLK>; resets = <&ufs_card_hc 0>; From f3aa975e230e060c07dcfdf3fe92b59809422c13 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Thu, 6 Oct 2022 10:55:29 -0400 Subject: [PATCH 0034/1823] arm64: dts: qcom: sc8280xp: correct ref clock for ufs_mem_phy The first UFS host controller fails to start on the SA8540P automotive board (QDrive3) due to the following errors: ufshcd-qcom 1d84000.ufs: ufshcd_query_flag: Sending flag query for idn 18 failed, err = 253 ufshcd-qcom 1d84000.ufs: ufshcd_query_flag: Sending flag query for idn 18 failed, err = 253 ufshcd-qcom 1d84000.ufs: ufshcd_query_flag: Sending flag query for idn 18 failed, err = 253 ufshcd-qcom 1d84000.ufs: ufshcd_query_flag_retry: query attribute, opcode 5, idn 18, failed with error 253 after 3 retries The system eventually fails to boot with the warning: gcc_ufs_phy_axi_clk status stuck at 'off' This issue can be worked around by adding clk_ignore_unused to the kernel command line since the system firmware sets up this clock for us. Let's fix this issue by updating the ref clock on ufs_mem_phy. Note that the downstream MSM 5.4 sources list this as ref_clk_parent. With this patch, the SA8540P is able to be booted without clk_ignore_unused. Signed-off-by: Brian Masney Fixes: 152d1faf1e2f ("arm64: dts: qcom: add SC8280XP platform") Tested-by: Johan Hovold Reviewed-by: Johan Hovold Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20221006145529.755521-1-bmasney@redhat.com --- arch/arm64/boot/dts/qcom/sc8280xp.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi index 4921bbaf5caf..f5824f44e15f 100644 --- a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi +++ b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi @@ -891,7 +891,7 @@ ranges; clock-names = "ref", "ref_aux"; - clocks = <&rpmhcc RPMH_CXO_CLK>, + clocks = <&gcc GCC_UFS_REF_CLKREF_CLK>, <&gcc GCC_UFS_PHY_PHY_AUX_CLK>; resets = <&ufs_mem_hc 0>; From 31b3b3059791be536e2ec0c6830767b596af340f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 19 Sep 2022 11:44:51 +0200 Subject: [PATCH 0035/1823] arm64: dts: qcom: sc8280xp: fix USB0 PHY PCS_MISC registers The USB0 SS PHY node had the PCS_MISC register block (0x1200) replaced with PCS_USB (0x1700). Fixes: 152d1faf1e2f ("arm64: dts: qcom: add SC8280XP platform") Signed-off-by: Johan Hovold Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220919094454.1574-2-johan+linaro@kernel.org --- arch/arm64/boot/dts/qcom/sc8280xp.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi index f5824f44e15f..cab9965fe0f8 100644 --- a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi +++ b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi @@ -1184,7 +1184,7 @@ <0 0x088ec400 0 0x1f0>, <0 0x088eba00 0 0x100>, <0 0x088ebc00 0 0x3ec>, - <0 0x088ec700 0 0x64>; + <0 0x088ec200 0 0x18>; #phy-cells = <0>; #clock-cells = <0>; clocks = <&gcc GCC_USB3_PRIM_PHY_PIPE_CLK>; From 81cad26c6c3984d01b0612069c70ffd820f62dfa Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 19 Sep 2022 11:44:52 +0200 Subject: [PATCH 0036/1823] arm64: dts: qcom: sc8280xp: fix USB1 PHY RX1 registers The USB1 SS PHY node had the RX1 register block (0x600) replaced with RX2 (0xc00). Fixes: 152d1faf1e2f ("arm64: dts: qcom: add SC8280XP platform") Signed-off-by: Johan Hovold Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220919094454.1574-3-johan+linaro@kernel.org --- arch/arm64/boot/dts/qcom/sc8280xp.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi index cab9965fe0f8..fce05d69ffbe 100644 --- a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi +++ b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi @@ -1242,7 +1242,7 @@ usb_1_ssphy: usb3-phy@8903400 { reg = <0 0x08903400 0 0x100>, - <0 0x08903c00 0 0x3ec>, + <0 0x08903600 0 0x3ec>, <0 0x08904400 0 0x1f0>, <0 0x08903a00 0 0x100>, <0 0x08903c00 0 0x3ec>, From 8723c3f290c7b798c0cbd89998576e6b365bda3a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 19 Sep 2022 11:44:53 +0200 Subject: [PATCH 0037/1823] arm64: dts: qcom: sc8280xp: fix USB PHY PCS registers With the current binding, the PCS register block (0x1400) needs to include the PCS_USB registers (0x1700). Fixes: 152d1faf1e2f ("arm64: dts: qcom: add SC8280XP platform") Signed-off-by: Johan Hovold Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220919094454.1574-4-johan+linaro@kernel.org --- arch/arm64/boot/dts/qcom/sc8280xp.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi index fce05d69ffbe..2cf2b3d83631 100644 --- a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi +++ b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi @@ -1181,7 +1181,7 @@ usb_0_ssphy: usb3-phy@88eb400 { reg = <0 0x088eb400 0 0x100>, <0 0x088eb600 0 0x3ec>, - <0 0x088ec400 0 0x1f0>, + <0 0x088ec400 0 0x364>, <0 0x088eba00 0 0x100>, <0 0x088ebc00 0 0x3ec>, <0 0x088ec200 0 0x18>; @@ -1243,7 +1243,7 @@ usb_1_ssphy: usb3-phy@8903400 { reg = <0 0x08903400 0 0x100>, <0 0x08903600 0 0x3ec>, - <0 0x08904400 0 0x1f0>, + <0 0x08904400 0 0x364>, <0 0x08903a00 0 0x100>, <0 0x08903c00 0 0x3ec>, <0 0x08904200 0 0x18>; From 7cdfb7a54ac88f7cb6d830ebb78bdbcbcb44bb4c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 19 Sep 2022 11:44:54 +0200 Subject: [PATCH 0038/1823] arm64: dts: qcom: sc8280xp: drop broken DP PHY nodes The DP PHY register layout of the current binding do not apply to the newer USB4/USB3/DP PHY which uses a different register layout entirely. Drop the DP PHY subnodes until the binding has been updated to prevent the driver from corrupting unrelated registers. Note that this is also needed in order to not break USB with an upcoming PHY driver change that checks for overlapping register regions. Fixes: 152d1faf1e2f ("arm64: dts: qcom: add SC8280XP platform") Signed-off-by: Johan Hovold Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220919094454.1574-5-johan+linaro@kernel.org --- arch/arm64/boot/dts/qcom/sc8280xp.dtsi | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi index 2cf2b3d83631..6ed4ab2c08b4 100644 --- a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi +++ b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi @@ -1191,16 +1191,6 @@ clock-names = "pipe0"; clock-output-names = "usb0_phy_pipe_clk_src"; }; - - usb_0_dpphy: dp-phy@88ed200 { - reg = <0 0x088ed200 0 0x200>, - <0 0x088ed400 0 0x200>, - <0 0x088eda00 0 0x200>, - <0 0x088ea600 0 0x200>, - <0 0x088ea800 0 0x200>; - #clock-cells = <1>; - #phy-cells = <0>; - }; }; usb_1_hsphy: phy@8902000 { @@ -1253,16 +1243,6 @@ clock-names = "pipe0"; clock-output-names = "usb1_phy_pipe_clk_src"; }; - - usb_1_dpphy: dp-phy@8904200 { - reg = <0 0x08904200 0 0x200>, - <0 0x08904400 0 0x200>, - <0 0x08904a00 0 0x200>, - <0 0x08904600 0 0x200>, - <0 0x08904800 0 0x200>; - #clock-cells = <1>; - #phy-cells = <0>; - }; }; system-cache-controller@9200000 { From 2ff4ba9e37024735f5cefc5ea2a73fc66addfe0e Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 29 Sep 2022 21:55:21 +0200 Subject: [PATCH 0039/1823] clk: rs9: Fix I2C accessors Add custom I2C accessors to this driver, since the regular I2C regmap ones do not generate the exact I2C transfers required by the chip. On I2C write, it is mandatory to send transfer length first, on read the chip returns the transfer length in first byte. Instead of always reading back 8 bytes, which is the default and also the size of the entire register file, set BCP register to 1 to read out 1 byte which is less wasteful. Fixes: 892e0ddea1aa ("clk: rs9: Add Renesas 9-series PCIe clock generator driver") Reported-by: Alexander Stein Signed-off-by: Marek Vasut Link: https://lore.kernel.org/r/20220929195521.284497-1-marex@denx.de Reviewed-by: Alexander Stein Signed-off-by: Stephen Boyd --- drivers/clk/clk-renesas-pcie.c | 65 ++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/drivers/clk/clk-renesas-pcie.c b/drivers/clk/clk-renesas-pcie.c index 4f5df1fc74b4..e6247141d0c0 100644 --- a/drivers/clk/clk-renesas-pcie.c +++ b/drivers/clk/clk-renesas-pcie.c @@ -90,13 +90,66 @@ static const struct regmap_access_table rs9_writeable_table = { .n_yes_ranges = ARRAY_SIZE(rs9_writeable_ranges), }; +static int rs9_regmap_i2c_write(void *context, + unsigned int reg, unsigned int val) +{ + struct i2c_client *i2c = context; + const u8 data[3] = { reg, 1, val }; + const int count = ARRAY_SIZE(data); + int ret; + + ret = i2c_master_send(i2c, data, count); + if (ret == count) + return 0; + else if (ret < 0) + return ret; + else + return -EIO; +} + +static int rs9_regmap_i2c_read(void *context, + unsigned int reg, unsigned int *val) +{ + struct i2c_client *i2c = context; + struct i2c_msg xfer[2]; + u8 txdata = reg; + u8 rxdata[2]; + int ret; + + xfer[0].addr = i2c->addr; + xfer[0].flags = 0; + xfer[0].len = 1; + xfer[0].buf = (void *)&txdata; + + xfer[1].addr = i2c->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = 2; + xfer[1].buf = (void *)rxdata; + + ret = i2c_transfer(i2c->adapter, xfer, 2); + if (ret < 0) + return ret; + if (ret != 2) + return -EIO; + + /* + * Byte 0 is transfer length, which is always 1 due + * to BCP register programming to 1 in rs9_probe(), + * ignore it and use data from Byte 1. + */ + *val = rxdata[1]; + return 0; +} + static const struct regmap_config rs9_regmap_config = { .reg_bits = 8, .val_bits = 8, - .cache_type = REGCACHE_FLAT, - .max_register = 0x8, + .cache_type = REGCACHE_NONE, + .max_register = RS9_REG_BCP, .rd_table = &rs9_readable_table, .wr_table = &rs9_writeable_table, + .reg_write = rs9_regmap_i2c_write, + .reg_read = rs9_regmap_i2c_read, }; static int rs9_get_output_config(struct rs9_driver_data *rs9, int idx) @@ -242,11 +295,17 @@ static int rs9_probe(struct i2c_client *client) return ret; } - rs9->regmap = devm_regmap_init_i2c(client, &rs9_regmap_config); + rs9->regmap = devm_regmap_init(&client->dev, NULL, + client, &rs9_regmap_config); if (IS_ERR(rs9->regmap)) return dev_err_probe(&client->dev, PTR_ERR(rs9->regmap), "Failed to allocate register map\n"); + /* Always read back 1 Byte via I2C */ + ret = regmap_write(rs9->regmap, RS9_REG_BCP, 1); + if (ret < 0) + return ret; + /* Register clock */ for (i = 0; i < rs9->chip_info->num_clks; i++) { snprintf(name, 5, "DIF%d", i); From 7e3e6e1b75c9643e25e8ca7d6caf1b1faf8f022e Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Wed, 5 Oct 2022 18:13:44 +0100 Subject: [PATCH 0040/1823] clk: sifive: select by default if SOC_SIFIVE With the aim of dropping direct selects of drivers from Kconfig.socs, default the SiFive clock drivers to the value of SOC_SIFIVE. Signed-off-by: Conor Dooley Link: https://lore.kernel.org/r/20221005171348.167476-2-conor@kernel.org Signed-off-by: Stephen Boyd --- drivers/clk/sifive/Kconfig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/clk/sifive/Kconfig b/drivers/clk/sifive/Kconfig index 9132c3c4aa86..b7fde0aadfcb 100644 --- a/drivers/clk/sifive/Kconfig +++ b/drivers/clk/sifive/Kconfig @@ -2,7 +2,8 @@ menuconfig CLK_SIFIVE bool "SiFive SoC driver support" - depends on RISCV || COMPILE_TEST + depends on SOC_SIFIVE || COMPILE_TEST + default SOC_SIFIVE help SoC drivers for SiFive Linux-capable SoCs. @@ -10,6 +11,7 @@ if CLK_SIFIVE config CLK_SIFIVE_PRCI bool "PRCI driver for SiFive SoCs" + default SOC_SIFIVE select RESET_CONTROLLER select RESET_SIMPLE select CLK_ANALOGBITS_WRPLL_CLN28HPC From 8fbf8636cd37b821ce3482748340008dbbe2dcb5 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Sun, 9 Oct 2022 10:50:56 +0800 Subject: [PATCH 0041/1823] clk: mediatek: clk-mt8195-topckgen: Fix error return code in clk_mt8195_topck_probe() If devm_clk_hw_register_mux() fails in clk_mt8195_topck_probe(), it should return error code. Fixes: deeb2af77cf6 ("clk: mediatek: clk-mt8195-topckgen: Register mfg_ck_fast_ref as generic mux") Signed-off-by: Yang Yingliang Link: https://lore.kernel.org/r/20221009025056.35311-1-yangyingliang@huawei.com Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Stephen Boyd --- drivers/clk/mediatek/clk-mt8195-topckgen.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/clk/mediatek/clk-mt8195-topckgen.c b/drivers/clk/mediatek/clk-mt8195-topckgen.c index 8cbab5ca2e58..1e016329c1d2 100644 --- a/drivers/clk/mediatek/clk-mt8195-topckgen.c +++ b/drivers/clk/mediatek/clk-mt8195-topckgen.c @@ -1270,8 +1270,10 @@ static int clk_mt8195_topck_probe(struct platform_device *pdev) hw = devm_clk_hw_register_mux(&pdev->dev, "mfg_ck_fast_ref", mfg_fast_parents, ARRAY_SIZE(mfg_fast_parents), CLK_SET_RATE_PARENT, (base + 0x250), 8, 1, 0, &mt8195_clk_lock); - if (IS_ERR(hw)) + if (IS_ERR(hw)) { + r = PTR_ERR(hw); goto unregister_muxes; + } top_clk_data->hws[CLK_TOP_MFG_CK_FAST_REF] = hw; r = clk_mt8195_reg_mfg_mux_notifier(&pdev->dev, From 8703d55bd5eac642275fe91b34ac62ad0ad312b5 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 15 Sep 2022 16:16:01 +0200 Subject: [PATCH 0042/1823] arm64: dts: qcom: sc8280xp: fix UFS PHY serdes size The size of the UFS PHY serdes register region is 0x1c8 and the corresponding 'reg' property should specifically not include the adjacent regions that are defined in the child node (e.g. tx and rx). Fixes: 152d1faf1e2f ("arm64: dts: qcom: add SC8280XP platform") Signed-off-by: Johan Hovold Tested-by: Andrew Halaney #Qdrive3/sa8540p-adp-ride Reviewed-by: Brian Masney Reviewed-by: Bjorn Andersson Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220915141601.18435-1-johan+linaro@kernel.org --- arch/arm64/boot/dts/qcom/sc8280xp.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi index 6ed4ab2c08b4..212d63d5cbf2 100644 --- a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi +++ b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi @@ -885,7 +885,7 @@ ufs_mem_phy: phy@1d87000 { compatible = "qcom,sc8280xp-qmp-ufs-phy"; - reg = <0 0x01d87000 0 0xe10>; + reg = <0 0x01d87000 0 0x1c8>; #address-cells = <2>; #size-cells = <2>; ranges; @@ -953,7 +953,7 @@ ufs_card_phy: phy@1da7000 { compatible = "qcom,sc8280xp-qmp-ufs-phy"; - reg = <0 0x01da7000 0 0xe10>; + reg = <0 0x01da7000 0 0x1c8>; #address-cells = <2>; #size-cells = <2>; ranges; From cb1d0aaa674e99957b85af570cb2730145af01df Mon Sep 17 00:00:00 2001 From: Satya Priya Date: Tue, 20 Sep 2022 17:18:15 +0530 Subject: [PATCH 0043/1823] arm64: dts: qcom: sc7280: Add the reset reg for lpass audiocc on SC7280 Add the reset register offset for clock gating. Fixes: 9499240d15f2 ("arm64: dts: qcom: sc7280: Add lpasscore & lpassaudio clock controllers") Signed-off-by: Satya Priya Reviewed-by: Neil Armstrong Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/1663674495-25748-1-git-send-email-quic_c_skakit@quicinc.com --- arch/arm64/boot/dts/qcom/sc7280.dtsi | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sc7280.dtsi b/arch/arm64/boot/dts/qcom/sc7280.dtsi index 212580316d3e..4cdc88d33944 100644 --- a/arch/arm64/boot/dts/qcom/sc7280.dtsi +++ b/arch/arm64/boot/dts/qcom/sc7280.dtsi @@ -2296,7 +2296,8 @@ lpass_audiocc: clock-controller@3300000 { compatible = "qcom,sc7280-lpassaudiocc"; - reg = <0 0x03300000 0 0x30000>; + reg = <0 0x03300000 0 0x30000>, + <0 0x032a9000 0 0x1000>; clocks = <&rpmhcc RPMH_CXO_CLK>, <&lpass_aon LPASS_AON_CC_MAIN_RCG_CLK_SRC>; clock-names = "bi_tcxo", "lpass_aon_cc_main_rcg_clk_src"; From c29f446108e10bb791220ffbba54d82722d60d45 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 20 Sep 2022 17:04:12 +0200 Subject: [PATCH 0044/1823] dt-bindings: hwlock: qcom-hwspinlock: add syscon to MSM8974 The TCSR_MUTEX region contains two set of registers: mutex and halt. Add syscon, so the TCSR mutex device (hwspinlock) can use MMIO based method and in the same time share regmap with other devices for the halt regs. Signed-off-by: Krzysztof Kozlowski Acked-by: Rob Herring Tested-by: Luca Weiss # fairphone-fp2 Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20220920150414.637634-2-krzysztof.kozlowski@linaro.org --- .../devicetree/bindings/hwlock/qcom-hwspinlock.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/hwlock/qcom-hwspinlock.yaml b/Documentation/devicetree/bindings/hwlock/qcom-hwspinlock.yaml index 1a3adf75934b..ee2726149cf3 100644 --- a/Documentation/devicetree/bindings/hwlock/qcom-hwspinlock.yaml +++ b/Documentation/devicetree/bindings/hwlock/qcom-hwspinlock.yaml @@ -24,9 +24,13 @@ properties: - qcom,apq8084-tcsr-mutex - qcom,ipq6018-tcsr-mutex - qcom,msm8226-tcsr-mutex - - qcom,msm8974-tcsr-mutex - qcom,msm8994-tcsr-mutex - const: qcom,tcsr-mutex + - items: + - enum: + - qcom,msm8974-tcsr-mutex + - const: qcom,tcsr-mutex + - const: syscon reg: maxItems: 1 From ba5284ebe497044f37c9bb9c7b1564932f4b6610 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 7 Oct 2022 15:10:00 +0200 Subject: [PATCH 0045/1823] clk: renesas: r8a779g0: Add SASYNCPER clocks On R-Car V4H, all PLLs except PLL5 support Spread Spectrum and/or Fractional Multiplication to reduce electromagnetic interference. Add the SASYNCPER and SASYNCPERD[124] clocks, which are used as clock sources for modules that must not be affected by Spread Spectrum and/or Fractional Multiplication. Signed-off-by: Geert Uytterhoeven Acked-by: Stephen Boyd Link: https://lore.kernel.org/r/d0f35c35e1f96c5a649ab477e7ba5d8025957cd0.1665147497.git.geert+renesas@glider.be --- drivers/clk/renesas/r8a779g0-cpg-mssr.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/clk/renesas/r8a779g0-cpg-mssr.c b/drivers/clk/renesas/r8a779g0-cpg-mssr.c index 9641122133b5..3e8c93facfa1 100644 --- a/drivers/clk/renesas/r8a779g0-cpg-mssr.c +++ b/drivers/clk/renesas/r8a779g0-cpg-mssr.c @@ -47,6 +47,7 @@ enum clk_ids { CLK_S0_VIO, CLK_S0_VC, CLK_S0_HSC, + CLK_SASYNCPER, CLK_SV_VIP, CLK_SV_IR, CLK_SDSRC, @@ -84,6 +85,7 @@ static const struct cpg_core_clk r8a779g0_core_clks[] __initconst = { DEF_FIXED(".s0_vio", CLK_S0_VIO, CLK_PLL1_DIV2, 2, 1), DEF_FIXED(".s0_vc", CLK_S0_VC, CLK_PLL1_DIV2, 2, 1), DEF_FIXED(".s0_hsc", CLK_S0_HSC, CLK_PLL1_DIV2, 2, 1), + DEF_FIXED(".sasyncper", CLK_SASYNCPER, CLK_PLL5_DIV4, 3, 1), DEF_FIXED(".sv_vip", CLK_SV_VIP, CLK_PLL1, 5, 1), DEF_FIXED(".sv_ir", CLK_SV_IR, CLK_PLL1, 5, 1), DEF_BASE(".sdsrc", CLK_SDSRC, CLK_TYPE_GEN4_SDSRC, CLK_PLL5), @@ -128,6 +130,9 @@ static const struct cpg_core_clk r8a779g0_core_clks[] __initconst = { DEF_FIXED("s0d4_hsc", R8A779G0_CLK_S0D4_HSC, CLK_S0_HSC, 4, 1), DEF_FIXED("cl16m_hsc", R8A779G0_CLK_CL16M_HSC, CLK_S0_HSC, 48, 1), DEF_FIXED("s0d2_cc", R8A779G0_CLK_S0D2_CC, CLK_S0, 2, 1), + DEF_FIXED("sasyncperd1",R8A779G0_CLK_SASYNCPERD1, CLK_SASYNCPER,1, 1), + DEF_FIXED("sasyncperd2",R8A779G0_CLK_SASYNCPERD2, CLK_SASYNCPER,2, 1), + DEF_FIXED("sasyncperd4",R8A779G0_CLK_SASYNCPERD4, CLK_SASYNCPER,4, 1), DEF_FIXED("svd1_ir", R8A779G0_CLK_SVD1_IR, CLK_SV_IR, 1, 1), DEF_FIXED("svd2_ir", R8A779G0_CLK_SVD2_IR, CLK_SV_IR, 2, 1), DEF_FIXED("svd1_vip", R8A779G0_CLK_SVD1_VIP, CLK_SV_VIP, 1, 1), From ef80c95c29dc67c3034f32d93c41e2ede398e387 Mon Sep 17 00:00:00 2001 From: David Virag Date: Thu, 13 Oct 2022 17:13:40 +0200 Subject: [PATCH 0046/1823] clk: samsung: exynos7885: Correct "div4" clock parents "div4" DIVs which divide PLLs by 4 are actually dividing "div2" DIVs by 2 to achieve a by 4 division, thus their parents are the respective "div2" DIVs. These DIVs were mistakenly set to have the PLLs as parents. This leads to the kernel thinking "div4"s and everything under them run at 2x the clock speed. Fix this. Fixes: 45bd8166a1d8 ("clk: samsung: Add initial Exynos7885 clock driver") Signed-off-by: David Virag Acked-by: Chanwoo Choi Link: https://lore.kernel.org/r/20221013151341.151208-1-virag.david003@gmail.com Signed-off-by: Krzysztof Kozlowski --- drivers/clk/samsung/clk-exynos7885.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/samsung/clk-exynos7885.c b/drivers/clk/samsung/clk-exynos7885.c index 62ce6814f141..0d2a950ed184 100644 --- a/drivers/clk/samsung/clk-exynos7885.c +++ b/drivers/clk/samsung/clk-exynos7885.c @@ -231,7 +231,7 @@ static const struct samsung_div_clock top_div_clks[] __initconst = { CLK_CON_DIV_PLL_SHARED0_DIV2, 0, 1), DIV(CLK_DOUT_SHARED0_DIV3, "dout_shared0_div3", "fout_shared0_pll", CLK_CON_DIV_PLL_SHARED0_DIV3, 0, 2), - DIV(CLK_DOUT_SHARED0_DIV4, "dout_shared0_div4", "fout_shared0_pll", + DIV(CLK_DOUT_SHARED0_DIV4, "dout_shared0_div4", "dout_shared0_div2", CLK_CON_DIV_PLL_SHARED0_DIV4, 0, 1), DIV(CLK_DOUT_SHARED0_DIV5, "dout_shared0_div5", "fout_shared0_pll", CLK_CON_DIV_PLL_SHARED0_DIV5, 0, 3), @@ -239,7 +239,7 @@ static const struct samsung_div_clock top_div_clks[] __initconst = { CLK_CON_DIV_PLL_SHARED1_DIV2, 0, 1), DIV(CLK_DOUT_SHARED1_DIV3, "dout_shared1_div3", "fout_shared1_pll", CLK_CON_DIV_PLL_SHARED1_DIV3, 0, 2), - DIV(CLK_DOUT_SHARED1_DIV4, "dout_shared1_div4", "fout_shared1_pll", + DIV(CLK_DOUT_SHARED1_DIV4, "dout_shared1_div4", "dout_shared1_div2", CLK_CON_DIV_PLL_SHARED1_DIV4, 0, 1), /* CORE */ From 97cf79677ecb50a38517253ae2fd705849a7e51a Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 16 Oct 2022 17:54:40 -0700 Subject: [PATCH 0047/1823] xfs: avoid a UAF when log intent item recovery fails KASAN reported a UAF bug when I was running xfs/235: BUG: KASAN: use-after-free in xlog_recover_process_intents+0xa77/0xae0 [xfs] Read of size 8 at addr ffff88804391b360 by task mount/5680 CPU: 2 PID: 5680 Comm: mount Not tainted 6.0.0-xfsx #6.0.0 77e7b52a4943a975441e5ac90a5ad7748b7867f6 Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.15.0-1 04/01/2014 Call Trace: dump_stack_lvl+0x34/0x44 print_report.cold+0x2cc/0x682 kasan_report+0xa3/0x120 xlog_recover_process_intents+0xa77/0xae0 [xfs fb841c7180aad3f8359438576e27867f5795667e] xlog_recover_finish+0x7d/0x970 [xfs fb841c7180aad3f8359438576e27867f5795667e] xfs_log_mount_finish+0x2d7/0x5d0 [xfs fb841c7180aad3f8359438576e27867f5795667e] xfs_mountfs+0x11d4/0x1d10 [xfs fb841c7180aad3f8359438576e27867f5795667e] xfs_fs_fill_super+0x13d5/0x1a80 [xfs fb841c7180aad3f8359438576e27867f5795667e] get_tree_bdev+0x3da/0x6e0 vfs_get_tree+0x7d/0x240 path_mount+0xdd3/0x17d0 __x64_sys_mount+0x1fa/0x270 do_syscall_64+0x2b/0x80 entry_SYSCALL_64_after_hwframe+0x46/0xb0 RIP: 0033:0x7ff5bc069eae Code: 48 8b 0d 85 1f 0f 00 f7 d8 64 89 01 48 83 c8 ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 49 89 ca b8 a5 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 52 1f 0f 00 f7 d8 64 89 01 48 RSP: 002b:00007ffe433fd448 EFLAGS: 00000246 ORIG_RAX: 00000000000000a5 RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007ff5bc069eae RDX: 00005575d7213290 RSI: 00005575d72132d0 RDI: 00005575d72132b0 RBP: 00005575d7212fd0 R08: 00005575d7213230 R09: 00005575d7213fe0 R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 R13: 00005575d7213290 R14: 00005575d72132b0 R15: 00005575d7212fd0 Allocated by task 5680: kasan_save_stack+0x1e/0x40 __kasan_slab_alloc+0x66/0x80 kmem_cache_alloc+0x152/0x320 xfs_rui_init+0x17a/0x1b0 [xfs] xlog_recover_rui_commit_pass2+0xb9/0x2e0 [xfs] xlog_recover_items_pass2+0xe9/0x220 [xfs] xlog_recover_commit_trans+0x673/0x900 [xfs] xlog_recovery_process_trans+0xbe/0x130 [xfs] xlog_recover_process_data+0x103/0x2a0 [xfs] xlog_do_recovery_pass+0x548/0xc60 [xfs] xlog_do_log_recovery+0x62/0xc0 [xfs] xlog_do_recover+0x73/0x480 [xfs] xlog_recover+0x229/0x460 [xfs] xfs_log_mount+0x284/0x640 [xfs] xfs_mountfs+0xf8b/0x1d10 [xfs] xfs_fs_fill_super+0x13d5/0x1a80 [xfs] get_tree_bdev+0x3da/0x6e0 vfs_get_tree+0x7d/0x240 path_mount+0xdd3/0x17d0 __x64_sys_mount+0x1fa/0x270 do_syscall_64+0x2b/0x80 entry_SYSCALL_64_after_hwframe+0x46/0xb0 Freed by task 5680: kasan_save_stack+0x1e/0x40 kasan_set_track+0x21/0x30 kasan_set_free_info+0x20/0x30 ____kasan_slab_free+0x144/0x1b0 slab_free_freelist_hook+0xab/0x180 kmem_cache_free+0x1f1/0x410 xfs_rud_item_release+0x33/0x80 [xfs] xfs_trans_free_items+0xc3/0x220 [xfs] xfs_trans_cancel+0x1fa/0x590 [xfs] xfs_rui_item_recover+0x913/0xd60 [xfs] xlog_recover_process_intents+0x24e/0xae0 [xfs] xlog_recover_finish+0x7d/0x970 [xfs] xfs_log_mount_finish+0x2d7/0x5d0 [xfs] xfs_mountfs+0x11d4/0x1d10 [xfs] xfs_fs_fill_super+0x13d5/0x1a80 [xfs] get_tree_bdev+0x3da/0x6e0 vfs_get_tree+0x7d/0x240 path_mount+0xdd3/0x17d0 __x64_sys_mount+0x1fa/0x270 do_syscall_64+0x2b/0x80 entry_SYSCALL_64_after_hwframe+0x46/0xb0 The buggy address belongs to the object at ffff88804391b300 which belongs to the cache xfs_rui_item of size 688 The buggy address is located 96 bytes inside of 688-byte region [ffff88804391b300, ffff88804391b5b0) The buggy address belongs to the physical page: page:ffffea00010e4600 refcount:1 mapcount:0 mapping:0000000000000000 index:0xffff888043919320 pfn:0x43918 head:ffffea00010e4600 order:2 compound_mapcount:0 compound_pincount:0 flags: 0x4fff80000010200(slab|head|node=1|zone=1|lastcpupid=0xfff) raw: 04fff80000010200 0000000000000000 dead000000000122 ffff88807f0eadc0 raw: ffff888043919320 0000000080140010 00000001ffffffff 0000000000000000 page dumped because: kasan: bad access detected Memory state around the buggy address: ffff88804391b200: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff88804391b280: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc >ffff88804391b300: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ^ ffff88804391b380: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ffff88804391b400: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ================================================================== The test fuzzes an rmap btree block and starts writer threads to induce a filesystem shutdown on the corrupt block. When the filesystem is remounted, recovery will try to replay the committed rmap intent item, but the corruption problem causes the recovery transaction to fail. Cancelling the transaction frees the RUD, which frees the RUI that we recovered. When we return to xlog_recover_process_intents, @lip is now a dangling pointer, and we cannot use it to find the iop_recover method for the tracepoint. Hence we must store the item ops before calling ->iop_recover if we want to give it to the tracepoint so that the trace data will tell us exactly which intent item failed. Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig --- fs/xfs/xfs_log_recover.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 17e923b9c5fa..322eb2ee6c55 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -2552,6 +2552,8 @@ xlog_recover_process_intents( for (lip = xfs_trans_ail_cursor_first(ailp, &cur, 0); lip != NULL; lip = xfs_trans_ail_cursor_next(ailp, &cur)) { + const struct xfs_item_ops *ops; + if (!xlog_item_is_intent(lip)) break; @@ -2567,13 +2569,17 @@ xlog_recover_process_intents( * deferred ops, you /must/ attach them to the capture list in * the recover routine or else those subsequent intents will be * replayed in the wrong order! + * + * The recovery function can free the log item, so we must not + * access lip after it returns. */ spin_unlock(&ailp->ail_lock); - error = lip->li_ops->iop_recover(lip, &capture_list); + ops = lip->li_ops; + error = ops->iop_recover(lip, &capture_list); spin_lock(&ailp->ail_lock); if (error) { trace_xlog_intent_recovery_failed(log->l_mp, error, - lip->li_ops->iop_recover); + ops->iop_recover); break; } } From a635beeacc6d56d2b71c39e6c0103f85b53d108e Mon Sep 17 00:00:00 2001 From: Zheng Yejian Date: Mon, 17 Oct 2022 10:38:06 +0000 Subject: [PATCH 0048/1823] tracing/histogram: Update document for KEYS_MAX size After commit 4f36c2d85ced ("tracing: Increase tracing map KEYS_MAX size"), 'keys' supports up to three fields. Signed-off-by: Zheng Yejian Cc: stable@vger.kernel.org Acked-by: Masami Hiramatsu (Google) Link: https://lore.kernel.org/r/20221017103806.2479139-1-zhengyejian1@huawei.com Signed-off-by: Jonathan Corbet --- Documentation/trace/histogram.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/trace/histogram.rst b/Documentation/trace/histogram.rst index c1b685a38f6b..87bd772836c0 100644 --- a/Documentation/trace/histogram.rst +++ b/Documentation/trace/histogram.rst @@ -39,7 +39,7 @@ Documentation written by Tom Zanussi will use the event's kernel stacktrace as the key. The keywords 'keys' or 'key' can be used to specify keys, and the keywords 'values', 'vals', or 'val' can be used to specify values. Compound - keys consisting of up to two fields can be specified by the 'keys' + keys consisting of up to three fields can be specified by the 'keys' keyword. Hashing a compound key produces a unique entry in the table for each unique combination of component keys, and can be useful for providing more fine-grained summaries of event data. From 4b549ccce941798703f159b227aa28c716aa78fa Mon Sep 17 00:00:00 2001 From: Christian Langrock Date: Mon, 17 Oct 2022 08:34:47 +0200 Subject: [PATCH 0049/1823] xfrm: replay: Fix ESN wrap around for GSO When using GSO it can happen that the wrong seq_hi is used for the last packets before the wrap around. This can lead to double usage of a sequence number. To avoid this, we should serialize this last GSO packet. Fixes: d7dbefc45cf5 ("xfrm: Add xfrm_replay_overflow functions for offloading") Co-developed-by: Steffen Klassert Signed-off-by: Christian Langrock Signed-off-by: Steffen Klassert --- net/ipv4/esp4_offload.c | 3 +++ net/ipv6/esp6_offload.c | 3 +++ net/xfrm/xfrm_device.c | 15 ++++++++++++++- net/xfrm/xfrm_replay.c | 2 +- 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/net/ipv4/esp4_offload.c b/net/ipv4/esp4_offload.c index 170152772d33..3969fa805679 100644 --- a/net/ipv4/esp4_offload.c +++ b/net/ipv4/esp4_offload.c @@ -314,6 +314,9 @@ static int esp_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features_ xo->seq.low += skb_shinfo(skb)->gso_segs; } + if (xo->seq.low < seq) + xo->seq.hi++; + esp.seqno = cpu_to_be64(seq + ((u64)xo->seq.hi << 32)); ip_hdr(skb)->tot_len = htons(skb->len); diff --git a/net/ipv6/esp6_offload.c b/net/ipv6/esp6_offload.c index 79d43548279c..242f4295940e 100644 --- a/net/ipv6/esp6_offload.c +++ b/net/ipv6/esp6_offload.c @@ -346,6 +346,9 @@ static int esp6_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features xo->seq.low += skb_shinfo(skb)->gso_segs; } + if (xo->seq.low < seq) + xo->seq.hi++; + esp.seqno = cpu_to_be64(xo->seq.low + ((u64)xo->seq.hi << 32)); len = skb->len - sizeof(struct ipv6hdr); diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c index 5f5aafd418af..21269e8f2db4 100644 --- a/net/xfrm/xfrm_device.c +++ b/net/xfrm/xfrm_device.c @@ -97,6 +97,18 @@ static void xfrm_outer_mode_prep(struct xfrm_state *x, struct sk_buff *skb) } } +static inline bool xmit_xfrm_check_overflow(struct sk_buff *skb) +{ + struct xfrm_offload *xo = xfrm_offload(skb); + __u32 seq = xo->seq.low; + + seq += skb_shinfo(skb)->gso_segs; + if (unlikely(seq < xo->seq.low)) + return true; + + return false; +} + struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t features, bool *again) { int err; @@ -134,7 +146,8 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur return skb; } - if (skb_is_gso(skb) && unlikely(x->xso.dev != dev)) { + if (skb_is_gso(skb) && (unlikely(x->xso.dev != dev) || + unlikely(xmit_xfrm_check_overflow(skb)))) { struct sk_buff *segs; /* Packet got rerouted, fixup features and segment it. */ diff --git a/net/xfrm/xfrm_replay.c b/net/xfrm/xfrm_replay.c index 9f4d42eb090f..ce56d659c55a 100644 --- a/net/xfrm/xfrm_replay.c +++ b/net/xfrm/xfrm_replay.c @@ -714,7 +714,7 @@ static int xfrm_replay_overflow_offload_esn(struct xfrm_state *x, struct sk_buff oseq += skb_shinfo(skb)->gso_segs; } - if (unlikely(oseq < replay_esn->oseq)) { + if (unlikely(xo->seq.low < replay_esn->oseq)) { XFRM_SKB_CB(skb)->seq.output.hi = ++oseq_hi; xo->seq.hi = oseq_hi; replay_esn->oseq_hi = oseq_hi; From eb83f502adb036cd56c27e13b9ca3b2aabfa790b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Bugge?= Date: Wed, 12 Oct 2022 16:15:42 +0200 Subject: [PATCH 0050/1823] RDMA/cma: Use output interface for net_dev check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 27cfde795a96 ("RDMA/cma: Fix arguments order in net device validation") swapped the src and dst addresses in the call to validate_net_dev(). As a consequence, the test in validate_ipv4_net_dev() to see if the net_dev is the right one, is incorrect for port 1 <-> 2 communication when the ports are on the same sub-net. This is fixed by denoting the flowi4_oif as the device instead of the incoming one. The bug has not been observed using IPv6 addresses. Fixes: 27cfde795a96 ("RDMA/cma: Fix arguments order in net device validation") Signed-off-by: Håkon Bugge Link: https://lore.kernel.org/r/20221012141542.16925-1-haakon.bugge@oracle.com Reviewed-by: Leon Romanovsky Signed-off-by: Leon Romanovsky --- drivers/infiniband/core/cma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index cc2222b85c88..26d1772179b8 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -1556,7 +1556,7 @@ static bool validate_ipv4_net_dev(struct net_device *net_dev, return false; memset(&fl4, 0, sizeof(fl4)); - fl4.flowi4_iif = net_dev->ifindex; + fl4.flowi4_oif = net_dev->ifindex; fl4.daddr = daddr; fl4.saddr = saddr; From 1afac08b39d85437187bb2a92d89a741b1078f55 Mon Sep 17 00:00:00 2001 From: Dean Luick Date: Tue, 18 Oct 2022 10:27:50 -0400 Subject: [PATCH 0051/1823] IB/hfi1: Correctly move list in sc_disable() Commit 13bac861952a ("IB/hfi1: Fix abba locking issue with sc_disable()") incorrectly tries to move a list from one list head to another. The result is a kernel crash. The crash is triggered when a link goes down and there are waiters for a send to complete. The following signature is seen: BUG: kernel NULL pointer dereference, address: 0000000000000030 [...] Call Trace: sc_disable+0x1ba/0x240 [hfi1] pio_freeze+0x3d/0x60 [hfi1] handle_freeze+0x27/0x1b0 [hfi1] process_one_work+0x1b0/0x380 ? process_one_work+0x380/0x380 worker_thread+0x30/0x360 ? process_one_work+0x380/0x380 kthread+0xd7/0x100 ? kthread_complete_and_exit+0x20/0x20 ret_from_fork+0x1f/0x30 The fix is to use the correct call to move the list. Fixes: 13bac861952a ("IB/hfi1: Fix abba locking issue with sc_disable()") Signed-off-by: Dean Luick Signed-off-by: Dennis Dalessandro Link: https://lore.kernel.org/r/166610327042.674422.6146908799669288976.stgit@awfm-02.cornelisnetworks.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/hfi1/pio.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/hfi1/pio.c b/drivers/infiniband/hw/hfi1/pio.c index 3d42bd2b36bd..51ae58c02b15 100644 --- a/drivers/infiniband/hw/hfi1/pio.c +++ b/drivers/infiniband/hw/hfi1/pio.c @@ -913,8 +913,7 @@ void sc_disable(struct send_context *sc) spin_unlock(&sc->release_lock); write_seqlock(&sc->waitlock); - if (!list_empty(&sc->piowait)) - list_move(&sc->piowait, &wake_list); + list_splice_init(&sc->piowait, &wake_list); write_sequnlock(&sc->waitlock); while (!list_empty(&wake_list)) { struct iowait *wait; From 1498a5a79980fb86458297df2b4aaa9cc56503b3 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 11 Oct 2022 10:07:44 -0400 Subject: [PATCH 0052/1823] dt-bindings: interconnect: qcom,msm8998-bwmon: Correct SC7280 CPU compatible Two different compatibles for SC7280 CPU BWMON instance were used in DTS and bindings. Correct the bindings to use the same one as in DTS, because it is more specific. Fixes: b7c84ae757c2 ("dt-bindings: interconnect: qcom,msm8998-bwmon: Add support for sc7280 BWMONs") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Rajendra Nayak Acked-by: Rob Herring Link: https://lore.kernel.org/r/20221011140744.29829-1-krzysztof.kozlowski@linaro.org Signed-off-by: Georgi Djakov --- .../devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml b/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml index 2684562df4d9..be29e0b80995 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml @@ -24,7 +24,7 @@ properties: oneOf: - items: - enum: - - qcom,sc7280-bwmon + - qcom,sc7280-cpu-bwmon - qcom,sdm845-bwmon - const: qcom,msm8998-bwmon - const: qcom,msm8998-bwmon # BWMON v4 From 5a637f83d96fc9f27e93385924e7253a5f5187bc Mon Sep 17 00:00:00 2001 From: Amelie Delaunay Date: Mon, 17 Oct 2022 15:14:13 +0200 Subject: [PATCH 0053/1823] dmaengine: stm32-mdma: memset stm32_mdma_chan_config struct before using it New bool m2m_hw has been added at the end of stm32_mdma_chan_config struct to support the STM32 DMA MDMA chaining. m2m_hw is set true in stm32_mdma_slave_config() if peripheral_size is set, but m2m_hw is never initialized false. To ensure this case, and any further new update of the structure, memset it to 0 before using it. Fixes: 696874322771 ("dmaengine: stm32-mdma: add support to be triggered by STM32 DMA") Signed-off-by: Amelie Delaunay Link: https://lore.kernel.org/r/20221017131413.202567-1-amelie.delaunay@foss.st.com Signed-off-by: Vinod Koul --- drivers/dma/stm32-mdma.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/dma/stm32-mdma.c b/drivers/dma/stm32-mdma.c index e28acbcb53f4..b9d4c843635f 100644 --- a/drivers/dma/stm32-mdma.c +++ b/drivers/dma/stm32-mdma.c @@ -1539,6 +1539,7 @@ static struct dma_chan *stm32_mdma_of_xlate(struct of_phandle_args *dma_spec, return NULL; } + memset(&config, 0, sizeof(config)); config.request = dma_spec->args[0]; config.priority_level = dma_spec->args[1]; config.transfer_config = dma_spec->args[2]; From 0ec8ce07394442d722806fe61b901a5b2b17249d Mon Sep 17 00:00:00 2001 From: Fenghua Yu Date: Fri, 14 Oct 2022 15:25:41 -0700 Subject: [PATCH 0054/1823] dmaengine: idxd: Do not enable user type Work Queue without Shared Virtual Addressing When the idxd_user_drv driver is bound to a Work Queue (WQ) device without IOMMU or with IOMMU Passthrough without Shared Virtual Addressing (SVA), the application gains direct access to physical memory via the device by programming physical address to a submitted descriptor. This allows direct userspace read and write access to arbitrary physical memory. This is inconsistent with the security goals of a good kernel API. Unlike vfio_pci driver, the IDXD char device driver does not provide any ways to pin user pages and translate the address from user VA to IOVA or PA without IOMMU SVA. Therefore the application has no way to instruct the device to perform DMA function. This makes the char device not usable for normal application usage. Since user type WQ without SVA cannot be used for normal application usage and presents the security issue, bind idxd_user_drv driver and enable user type WQ only when SVA is enabled (i.e. user PASID is enabled). Fixes: 448c3de8ac83 ("dmaengine: idxd: create user driver for wq 'device'") Cc: stable@vger.kernel.org Suggested-by: Arjan Van De Ven Signed-off-by: Fenghua Yu Reviewed-by: Dave Jiang Reviewed-by: Jerry Snitselaar Link: https://lore.kernel.org/r/20221014222541.3912195-1-fenghua.yu@intel.com Signed-off-by: Vinod Koul --- drivers/dma/idxd/cdev.c | 18 ++++++++++++++++++ include/uapi/linux/idxd.h | 1 + 2 files changed, 19 insertions(+) diff --git a/drivers/dma/idxd/cdev.c b/drivers/dma/idxd/cdev.c index c2808fd081d6..a9b96b18772f 100644 --- a/drivers/dma/idxd/cdev.c +++ b/drivers/dma/idxd/cdev.c @@ -312,6 +312,24 @@ static int idxd_user_drv_probe(struct idxd_dev *idxd_dev) if (idxd->state != IDXD_DEV_ENABLED) return -ENXIO; + /* + * User type WQ is enabled only when SVA is enabled for two reasons: + * - If no IOMMU or IOMMU Passthrough without SVA, userspace + * can directly access physical address through the WQ. + * - The IDXD cdev driver does not provide any ways to pin + * user pages and translate the address from user VA to IOVA or + * PA without IOMMU SVA. Therefore the application has no way + * to instruct the device to perform DMA function. This makes + * the cdev not usable for normal application usage. + */ + if (!device_user_pasid_enabled(idxd)) { + idxd->cmd_status = IDXD_SCMD_WQ_USER_NO_IOMMU; + dev_dbg(&idxd->pdev->dev, + "User type WQ cannot be enabled without SVA.\n"); + + return -EOPNOTSUPP; + } + mutex_lock(&wq->wq_lock); wq->type = IDXD_WQT_USER; rc = drv_enable_wq(wq); diff --git a/include/uapi/linux/idxd.h b/include/uapi/linux/idxd.h index 095299c75828..2b9e7feba3f3 100644 --- a/include/uapi/linux/idxd.h +++ b/include/uapi/linux/idxd.h @@ -29,6 +29,7 @@ enum idxd_scmd_stat { IDXD_SCMD_WQ_NO_SIZE = 0x800e0000, IDXD_SCMD_WQ_NO_PRIV = 0x800f0000, IDXD_SCMD_WQ_IRQ_ERR = 0x80100000, + IDXD_SCMD_WQ_USER_NO_IOMMU = 0x80110000, }; #define IDXD_SCMD_SOFTERR_MASK 0x80000000 From 097a4a1612389c31d2c4b95dfa816b91212d7f54 Mon Sep 17 00:00:00 2001 From: Jiangshan Yi Date: Sun, 9 Oct 2022 16:39:44 +0800 Subject: [PATCH 0055/1823] watchdog: sp805_wdt: fix spelling typo in comment Fix spelling typo in comment. Reported-by: k2ci Signed-off-by: Jiangshan Yi Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20221009083944.2988237-1-13667453960@163.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/sp805_wdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c index 78ba36689eec..2756ed54ca3d 100644 --- a/drivers/watchdog/sp805_wdt.c +++ b/drivers/watchdog/sp805_wdt.c @@ -88,7 +88,7 @@ static bool wdt_is_running(struct watchdog_device *wdd) return (wdtcontrol & ENABLE_MASK) == ENABLE_MASK; } -/* This routine finds load value that will reset system in required timout */ +/* This routine finds load value that will reset system in required timeout */ static int wdt_setload(struct watchdog_device *wdd, unsigned int timeout) { struct sp805_wdt *wdt = watchdog_get_drvdata(wdd); From 82ebbe65d781064cfb0a6a8af221a9cebcaaac9e Mon Sep 17 00:00:00 2001 From: Manank Patel Date: Thu, 13 Oct 2022 15:22:58 +0530 Subject: [PATCH 0056/1823] drivers: watchdog: exar_wdt.c fix use after free fix use after free by storing the result of PTR_ERR(n->pdev) to a local variable before returning. Signed-off-by: Manank Patel Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20221013095258.1424967-1-pmanank200502@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/exar_wdt.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/watchdog/exar_wdt.c b/drivers/watchdog/exar_wdt.c index 35058d8b21bc..7c61ff343271 100644 --- a/drivers/watchdog/exar_wdt.c +++ b/drivers/watchdog/exar_wdt.c @@ -355,8 +355,10 @@ static int __init exar_wdt_register(struct wdt_priv *priv, const int idx) &priv->wdt_res, 1, priv, sizeof(*priv)); if (IS_ERR(n->pdev)) { + int err = PTR_ERR(n->pdev); + kfree(n); - return PTR_ERR(n->pdev); + return err; } list_add_tail(&n->list, &pdev_list); From 091873e47ef700e935aa80079b63929af599a0b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= Date: Wed, 19 Oct 2022 22:05:36 +0200 Subject: [PATCH 0057/1823] selftests/landlock: Build without static libraries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The only (forced) static test binary doesn't depend on libcap. Because using -lcap on systems that don't have such static library would fail (e.g. on Arch Linux), let's be more specific and require only dynamic libcap linking. Fixes: a52540522c95 ("selftests/landlock: Fix out-of-tree builds") Cc: Anders Roxell Cc: Guillaume Tucker Cc: Mark Brown Cc: Shuah Khan Cc: stable@vger.kernel.org Signed-off-by: Mickaël Salaün Link: https://lore.kernel.org/r/20221019200536.2771316-1-mic@digikod.net --- tools/testing/selftests/landlock/Makefile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/landlock/Makefile b/tools/testing/selftests/landlock/Makefile index 6632bfff486b..348e2dbdb4e0 100644 --- a/tools/testing/selftests/landlock/Makefile +++ b/tools/testing/selftests/landlock/Makefile @@ -3,7 +3,6 @@ # First run: make -C ../../../.. headers_install CFLAGS += -Wall -O2 $(KHDR_INCLUDES) -LDLIBS += -lcap LOCAL_HDRS += common.h @@ -13,10 +12,12 @@ TEST_GEN_PROGS := $(src_test:.c=) TEST_GEN_PROGS_EXTENDED := true -# Static linking for short targets: +# Short targets: +$(TEST_GEN_PROGS): LDLIBS += -lcap $(TEST_GEN_PROGS_EXTENDED): LDFLAGS += -static include ../lib.mk -# Static linking for targets with $(OUTPUT)/ prefix: +# Targets with $(OUTPUT)/ prefix: +$(TEST_GEN_PROGS): LDLIBS += -lcap $(TEST_GEN_PROGS_EXTENDED): LDFLAGS += -static From 172c65e673cc3998059c9dfb3faf48e72e6fc236 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 15 Oct 2022 11:26:37 +0300 Subject: [PATCH 0058/1823] power: supply: rk817: check correct variable This code checks "->chg_ps" twice when it was supposed to check "->bat_ps". Fixes: 11cb8da0189b ("power: supply: Add charger driver for Rockchip RK817") Signed-off-by: Dan Carpenter Signed-off-by: Sebastian Reichel --- drivers/power/supply/rk817_charger.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/power/supply/rk817_charger.c b/drivers/power/supply/rk817_charger.c index 635f051b0821..4e9c7b191266 100644 --- a/drivers/power/supply/rk817_charger.c +++ b/drivers/power/supply/rk817_charger.c @@ -1116,14 +1116,12 @@ static int rk817_charger_probe(struct platform_device *pdev) charger->bat_ps = devm_power_supply_register(&pdev->dev, &rk817_bat_desc, &pscfg); - - charger->chg_ps = devm_power_supply_register(&pdev->dev, - &rk817_chg_desc, &pscfg); - - if (IS_ERR(charger->chg_ps)) + if (IS_ERR(charger->bat_ps)) return dev_err_probe(dev, -EINVAL, "Battery failed to probe\n"); + charger->chg_ps = devm_power_supply_register(&pdev->dev, + &rk817_chg_desc, &pscfg); if (IS_ERR(charger->chg_ps)) return dev_err_probe(dev, -EINVAL, "Charger failed to probe\n"); From 883babd43dcf8b44db36d015a08cbf0066a5270d Mon Sep 17 00:00:00 2001 From: Chris Morgan Date: Mon, 10 Oct 2022 09:13:29 -0500 Subject: [PATCH 0059/1823] power: supply: rk817: Change rk817_chg_cur_to_reg to int Change return value of rk817_chg_cur_to_reg from u8 to int. If the function fails to find a suitable value it returns a -EINVAL, but defined as a u8 it would not return correctly. Additionally, change defined variable that stores return value from u8 to int. Fixes: 11cb8da0189b ("power: supply: Add charger driver for Rockchip RK817") Signed-off-by: Chris Morgan Reported-by: Dan Carpenter Signed-off-by: Sebastian Reichel --- drivers/power/supply/rk817_charger.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/power/supply/rk817_charger.c b/drivers/power/supply/rk817_charger.c index 4e9c7b191266..f20a6ac584cc 100644 --- a/drivers/power/supply/rk817_charger.c +++ b/drivers/power/supply/rk817_charger.c @@ -121,7 +121,7 @@ struct rk817_charger { #define ADC_TO_CHARGE_UAH(adc_value, res_div) \ (adc_value / 3600 * 172 / res_div) -static u8 rk817_chg_cur_to_reg(u32 chg_cur_ma) +static int rk817_chg_cur_to_reg(u32 chg_cur_ma) { if (chg_cur_ma >= 3500) return CHG_3_5A; @@ -864,8 +864,8 @@ static int rk817_battery_init(struct rk817_charger *charger, { struct rk808 *rk808 = charger->rk808; u32 tmp, max_chg_vol_mv, max_chg_cur_ma; - u8 max_chg_vol_reg, chg_term_i_reg, max_chg_cur_reg; - int ret, chg_term_ma; + u8 max_chg_vol_reg, chg_term_i_reg; + int ret, chg_term_ma, max_chg_cur_reg; u8 bulk_reg[2]; /* Get initial plug state */ From 91e8b74fe6381e083f8aa55217bb0562785ab398 Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Wed, 19 Oct 2022 16:27:27 +0200 Subject: [PATCH 0060/1823] arm64: dts: rockchip: lower rk3399-puma-haikou SD controller clock frequency CRC errors (code -84 EILSEQ) have been observed for some SanDisk Ultra A1 cards when running at 50MHz. Waveform analysis suggest that the level shifters that are used on the RK3399-Q7 module for voltage translation between 3.0 and 3.3V don't handle clock rates at or above 48MHz properly. Back off to 40MHz for some safety margin. Cc: stable@vger.kernel.org Fixes: 60fd9f72ce8a ("arm64: dts: rockchip: add Haikou baseboard with RK3399-Q7 SoM") Signed-off-by: Jakob Unterwurzacher Signed-off-by: Quentin Schulz Link: https://lore.kernel.org/r/20221019-upstream-puma-sd-40mhz-v1-0-754a76421518@theobroma-systems.com Signed-off-by: Heiko Stuebner --- arch/arm64/boot/dts/rockchip/rk3399-puma-haikou.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3399-puma-haikou.dts b/arch/arm64/boot/dts/rockchip/rk3399-puma-haikou.dts index 04c752f49be9..115c14c0a3c6 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-puma-haikou.dts +++ b/arch/arm64/boot/dts/rockchip/rk3399-puma-haikou.dts @@ -207,7 +207,7 @@ cap-sd-highspeed; cd-gpios = <&gpio0 RK_PA7 GPIO_ACTIVE_LOW>; disable-wp; - max-frequency = <150000000>; + max-frequency = <40000000>; pinctrl-names = "default"; pinctrl-0 = <&sdmmc_clk &sdmmc_cmd &sdmmc_cd &sdmmc_bus4>; vmmc-supply = <&vcc3v3_baseboard>; From 9fa248c65bdbf5af0a2f74dd38575acfc8dfd2bf Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 20 Oct 2022 17:18:58 +0200 Subject: [PATCH 0061/1823] fuse: fix readdir cache race There's a race in fuse's readdir cache that can result in an uninitilized page being read. The page lock is supposed to prevent this from happening but in the following case it doesn't: Two fuse_add_dirent_to_cache() start out and get the same parameters (size=0,offset=0). One of them wins the race to create and lock the page, after which it fills in data, sets rdc.size and unlocks the page. In the meantime the page gets evicted from the cache before the other instance gets to run. That one also creates the page, but finds the size to be mismatched, bails out and leaves the uninitialized page in the cache. Fix by marking a filled page uptodate and ignoring non-uptodate pages. Reported-by: Frank Sorenson Fixes: 5d7bc7e8680c ("fuse: allow using readdir cache") Cc: # v4.20 Signed-off-by: Miklos Szeredi --- fs/fuse/readdir.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/fs/fuse/readdir.c b/fs/fuse/readdir.c index b4e565711045..e8deaacf1832 100644 --- a/fs/fuse/readdir.c +++ b/fs/fuse/readdir.c @@ -77,8 +77,10 @@ static void fuse_add_dirent_to_cache(struct file *file, goto unlock; addr = kmap_local_page(page); - if (!offset) + if (!offset) { clear_page(addr); + SetPageUptodate(page); + } memcpy(addr + offset, dirent, reclen); kunmap_local(addr); fi->rdc.size = (index << PAGE_SHIFT) + offset + reclen; @@ -516,6 +518,12 @@ retry_locked: page = find_get_page_flags(file->f_mapping, index, FGP_ACCESSED | FGP_LOCK); + /* Page gone missing, then re-added to cache, but not initialized? */ + if (page && !PageUptodate(page)) { + unlock_page(page); + put_page(page); + page = NULL; + } spin_lock(&fi->rdc.lock); if (!page) { /* From 54de93cd8740d52a83728802b4270f953d1a636f Mon Sep 17 00:00:00 2001 From: Petr Vorel Date: Thu, 20 Oct 2022 17:06:45 +0200 Subject: [PATCH 0062/1823] kernel/utsname_sysctl.c: Add missing enum uts_proc value bfca3dd3d068 added new struct ctl_table uts_kern_table[], but not new enum uts_proc value. It broke the notification mechanism between the sethostname syscall and the pollers of /proc/sys/kernel/hostname. The table uts_kern_table is addressed within uts_proc_notify by the enum value, that's why new enum value is needed. Fixes: bfca3dd3d068 ("kernel/utsname_sysctl.c: print kernel arch") Reported-by: Torsten Hilbrich Signed-off-by: Petr Vorel Link: https://lore.kernel.org/r/20221020150645.11719-1-pvorel@suse.cz Signed-off-by: Greg Kroah-Hartman --- include/linux/utsname.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/utsname.h b/include/linux/utsname.h index 2b1737c9b244..bf7613ba412b 100644 --- a/include/linux/utsname.h +++ b/include/linux/utsname.h @@ -10,6 +10,7 @@ #include enum uts_proc { + UTS_PROC_ARCH, UTS_PROC_OSTYPE, UTS_PROC_OSRELEASE, UTS_PROC_VERSION, From 13cf24e00665c9751951a422756d975812b71173 Mon Sep 17 00:00:00 2001 From: Guo Xuenan Date: Tue, 18 Oct 2022 14:32:35 -0700 Subject: [PATCH 0063/1823] xfs: fix exception caused by unexpected illegal bestcount in leaf dir For leaf dir, In most cases, there should be as many bestfree slots as the dir data blocks that can fit under i_size (except for [1]). Root cause is we don't examin the number bestfree slots, when the slots number less than dir data blocks, if we need to allocate new dir data block and update the bestfree array, we will use the dir block number as index to assign bestfree array, while we did not check the leaf buf boundary which may cause UAF or other memory access problem. This issue can also triggered with test cases xfs/473 from fstests. According to Dave Chinner & Darrick's suggestion, adding buffer verifier to detect this abnormal situation in time. Simplify the testcase for fstest xfs/554 [1] The error log is shown as follows: ================================================================== BUG: KASAN: use-after-free in xfs_dir2_leaf_addname+0x1995/0x1ac0 Write of size 2 at addr ffff88810168b000 by task touch/1552 CPU: 5 PID: 1552 Comm: touch Not tainted 6.0.0-rc3+ #101 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1ubuntu1.1 04/01/2014 Call Trace: dump_stack_lvl+0x4d/0x66 print_report.cold+0xf6/0x691 kasan_report+0xa8/0x120 xfs_dir2_leaf_addname+0x1995/0x1ac0 xfs_dir_createname+0x58c/0x7f0 xfs_create+0x7af/0x1010 xfs_generic_create+0x270/0x5e0 path_openat+0x270b/0x3450 do_filp_open+0x1cf/0x2b0 do_sys_openat2+0x46b/0x7a0 do_sys_open+0xb7/0x130 do_syscall_64+0x35/0x80 entry_SYSCALL_64_after_hwframe+0x63/0xcd RIP: 0033:0x7fe4d9e9312b Code: 25 00 00 41 00 3d 00 00 41 00 74 4b 64 8b 04 25 18 00 00 00 85 c0 75 67 44 89 e2 48 89 ee bf 9c ff ff ff b8 01 01 00 00 0f 05 <48> 3d 00 f0 ff ff 0f 87 91 00 00 00 48 8b 4c 24 28 64 48 33 0c 25 RSP: 002b:00007ffda4c16c20 EFLAGS: 00000246 ORIG_RAX: 0000000000000101 RAX: ffffffffffffffda RBX: 0000000000000001 RCX: 00007fe4d9e9312b RDX: 0000000000000941 RSI: 00007ffda4c17f33 RDI: 00000000ffffff9c RBP: 00007ffda4c17f33 R08: 0000000000000000 R09: 0000000000000000 R10: 00000000000001b6 R11: 0000000000000246 R12: 0000000000000941 R13: 00007fe4d9f631a4 R14: 00007ffda4c17f33 R15: 0000000000000000 The buggy address belongs to the physical page: page:ffffea000405a2c0 refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x10168b flags: 0x2fffff80000000(node=0|zone=2|lastcpupid=0x1fffff) raw: 002fffff80000000 ffffea0004057788 ffffea000402dbc8 0000000000000000 raw: 0000000000000000 0000000000170000 00000000ffffffff 0000000000000000 page dumped because: kasan: bad access detected Memory state around the buggy address: ffff88810168af00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ffff88810168af80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >ffff88810168b000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ^ ffff88810168b080: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ffff88810168b100: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ================================================================== Disabling lock debugging due to kernel taint 00000000: 58 44 44 33 5b 53 35 c2 00 00 00 00 00 00 00 78 XDD3[S5........x XFS (sdb): Internal error xfs_dir2_data_use_free at line 1200 of file fs/xfs/libxfs/xfs_dir2_data.c. Caller xfs_dir2_data_use_free+0x28a/0xeb0 CPU: 5 PID: 1552 Comm: touch Tainted: G B 6.0.0-rc3+ Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1ubuntu1.1 04/01/2014 Call Trace: dump_stack_lvl+0x4d/0x66 xfs_corruption_error+0x132/0x150 xfs_dir2_data_use_free+0x198/0xeb0 xfs_dir2_leaf_addname+0xa59/0x1ac0 xfs_dir_createname+0x58c/0x7f0 xfs_create+0x7af/0x1010 xfs_generic_create+0x270/0x5e0 path_openat+0x270b/0x3450 do_filp_open+0x1cf/0x2b0 do_sys_openat2+0x46b/0x7a0 do_sys_open+0xb7/0x130 do_syscall_64+0x35/0x80 entry_SYSCALL_64_after_hwframe+0x63/0xcd RIP: 0033:0x7fe4d9e9312b Code: 25 00 00 41 00 3d 00 00 41 00 74 4b 64 8b 04 25 18 00 00 00 85 c0 75 67 44 89 e2 48 89 ee bf 9c ff ff ff b8 01 01 00 00 0f 05 <48> 3d 00 f0 ff ff 0f 87 91 00 00 00 48 8b 4c 24 28 64 48 33 0c 25 RSP: 002b:00007ffda4c16c20 EFLAGS: 00000246 ORIG_RAX: 0000000000000101 RAX: ffffffffffffffda RBX: 0000000000000001 RCX: 00007fe4d9e9312b RDX: 0000000000000941 RSI: 00007ffda4c17f46 RDI: 00000000ffffff9c RBP: 00007ffda4c17f46 R08: 0000000000000000 R09: 0000000000000001 R10: 00000000000001b6 R11: 0000000000000246 R12: 0000000000000941 R13: 00007fe4d9f631a4 R14: 00007ffda4c17f46 R15: 0000000000000000 XFS (sdb): Corruption detected. Unmount and run xfs_repair [1] https://lore.kernel.org/all/20220928095355.2074025-1-guoxuenan@huawei.com/ Reviewed-by: Hou Tao Signed-off-by: Guo Xuenan Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/xfs/libxfs/xfs_dir2_leaf.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/fs/xfs/libxfs/xfs_dir2_leaf.c b/fs/xfs/libxfs/xfs_dir2_leaf.c index d9b66306a9a7..cb9e950a911d 100644 --- a/fs/xfs/libxfs/xfs_dir2_leaf.c +++ b/fs/xfs/libxfs/xfs_dir2_leaf.c @@ -146,6 +146,8 @@ xfs_dir3_leaf_check_int( xfs_dir2_leaf_tail_t *ltp; int stale; int i; + bool isleaf1 = (hdr->magic == XFS_DIR2_LEAF1_MAGIC || + hdr->magic == XFS_DIR3_LEAF1_MAGIC); ltp = xfs_dir2_leaf_tail_p(geo, leaf); @@ -158,8 +160,7 @@ xfs_dir3_leaf_check_int( return __this_address; /* Leaves and bests don't overlap in leaf format. */ - if ((hdr->magic == XFS_DIR2_LEAF1_MAGIC || - hdr->magic == XFS_DIR3_LEAF1_MAGIC) && + if (isleaf1 && (char *)&hdr->ents[hdr->count] > (char *)xfs_dir2_leaf_bests_p(ltp)) return __this_address; @@ -175,6 +176,10 @@ xfs_dir3_leaf_check_int( } if (hdr->ents[i].address == cpu_to_be32(XFS_DIR2_NULL_DATAPTR)) stale++; + if (isleaf1 && xfs_dir2_dataptr_to_db(geo, + be32_to_cpu(hdr->ents[i].address)) >= + be32_to_cpu(ltp->bestcount)) + return __this_address; } if (hdr->stale != stale) return __this_address; From fc93812c725068e6a491ce574f058a4530130c00 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 18 Oct 2022 14:37:39 -0700 Subject: [PATCH 0064/1823] xfs: remove redundant pointer lip The assignment to pointer lip is not really required, the pointer lip is redundant and can be removed. Cleans up clang-scan warning: warning: Although the value stored to 'lip' is used in the enclosing expression, the value is never actually read from 'lip' [deadcode.DeadStores] Signed-off-by: Colin Ian King Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_trans_ail.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/xfs/xfs_trans_ail.c b/fs/xfs/xfs_trans_ail.c index 16fbf2a1144c..f51df7d94ef7 100644 --- a/fs/xfs/xfs_trans_ail.c +++ b/fs/xfs/xfs_trans_ail.c @@ -730,11 +730,10 @@ void xfs_ail_push_all_sync( struct xfs_ail *ailp) { - struct xfs_log_item *lip; DEFINE_WAIT(wait); spin_lock(&ailp->ail_lock); - while ((lip = xfs_ail_max(ailp)) != NULL) { + while (xfs_ail_max(ailp) != NULL) { prepare_to_wait(&ailp->ail_empty, &wait, TASK_UNINTERRUPTIBLE); wake_up_process(ailp->ail_task); spin_unlock(&ailp->ail_lock); From cf4f4c12dea7a977a143c8fe5af1740b7f9876f8 Mon Sep 17 00:00:00 2001 From: Zeng Heng Date: Tue, 18 Oct 2022 14:38:14 -0700 Subject: [PATCH 0065/1823] xfs: fix memory leak in xfs_errortag_init When `xfs_sysfs_init` returns failed, `mp->m_errortag` needs to free. Otherwise kmemleak would report memory leak after mounting xfs image: unreferenced object 0xffff888101364900 (size 192): comm "mount", pid 13099, jiffies 4294915218 (age 335.207s) hex dump (first 32 bytes): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [<00000000f08ad25c>] __kmalloc+0x41/0x1b0 [<00000000dca9aeb6>] kmem_alloc+0xfd/0x430 [<0000000040361882>] xfs_errortag_init+0x20/0x110 [<00000000b384a0f6>] xfs_mountfs+0x6ea/0x1a30 [<000000003774395d>] xfs_fs_fill_super+0xe10/0x1a80 [<000000009cf07b6c>] get_tree_bdev+0x3e7/0x700 [<00000000046b5426>] vfs_get_tree+0x8e/0x2e0 [<00000000952ec082>] path_mount+0xf8c/0x1990 [<00000000beb1f838>] do_mount+0xee/0x110 [<000000000e9c41bb>] __x64_sys_mount+0x14b/0x1f0 [<00000000f7bb938e>] do_syscall_64+0x3b/0x90 [<000000003fcd67a9>] entry_SYSCALL_64_after_hwframe+0x63/0xcd Fixes: c68401011522 ("xfs: expose errortag knobs via sysfs") Signed-off-by: Zeng Heng Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_error.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/fs/xfs/xfs_error.c b/fs/xfs/xfs_error.c index 7db588ed0be5..c6b2aabd6f18 100644 --- a/fs/xfs/xfs_error.c +++ b/fs/xfs/xfs_error.c @@ -234,13 +234,18 @@ int xfs_errortag_init( struct xfs_mount *mp) { + int ret; + mp->m_errortag = kmem_zalloc(sizeof(unsigned int) * XFS_ERRTAG_MAX, KM_MAYFAIL); if (!mp->m_errortag) return -ENOMEM; - return xfs_sysfs_init(&mp->m_errortag_kobj, &xfs_errortag_ktype, - &mp->m_kobj, "errortag"); + ret = xfs_sysfs_init(&mp->m_errortag_kobj, &xfs_errortag_ktype, + &mp->m_kobj, "errortag"); + if (ret) + kmem_free(mp->m_errortag); + return ret; } void From d08af40340cad0e025d643c3982781a8f99d5032 Mon Sep 17 00:00:00 2001 From: Li Zetao Date: Tue, 18 Oct 2022 14:38:29 -0700 Subject: [PATCH 0066/1823] xfs: Fix unreferenced object reported by kmemleak in xfs_sysfs_init() kmemleak reported a sequence of memory leaks, and one of them indicated we failed to free a pointer: comm "mount", pid 19610, jiffies 4297086464 (age 60.635s) hex dump (first 8 bytes): 73 64 61 00 81 88 ff ff sda..... backtrace: [<00000000d77f3e04>] kstrdup_const+0x46/0x70 [<00000000e51fa804>] kobject_set_name_vargs+0x2f/0xb0 [<00000000247cd595>] kobject_init_and_add+0xb0/0x120 [<00000000f9139aaf>] xfs_mountfs+0x367/0xfc0 [<00000000250d3caf>] xfs_fs_fill_super+0xa16/0xdc0 [<000000008d873d38>] get_tree_bdev+0x256/0x390 [<000000004881f3fa>] vfs_get_tree+0x41/0xf0 [<000000008291ab52>] path_mount+0x9b3/0xdd0 [<0000000022ba8f2d>] __x64_sys_mount+0x190/0x1d0 As mentioned in kobject_init_and_add() comment, if this function returns an error, kobject_put() must be called to properly clean up the memory associated with the object. Apparently, xfs_sysfs_init() does not follow such a requirement. When kobject_init_and_add() returns an error, the space of kobj->kobject.name alloced by kstrdup_const() is unfree, which will cause the above stack. Fix it by adding kobject_put() when kobject_init_and_add returns an error. Fixes: a31b1d3d89e4 ("xfs: add xfs_mount sysfs kobject") Signed-off-by: Li Zetao Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/xfs/xfs_sysfs.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/xfs/xfs_sysfs.h b/fs/xfs/xfs_sysfs.h index 43585850f154..513095e353a5 100644 --- a/fs/xfs/xfs_sysfs.h +++ b/fs/xfs/xfs_sysfs.h @@ -33,10 +33,15 @@ xfs_sysfs_init( const char *name) { struct kobject *parent; + int err; parent = parent_kobj ? &parent_kobj->kobject : NULL; init_completion(&kobj->complete); - return kobject_init_and_add(&kobj->kobject, ktype, parent, "%s", name); + err = kobject_init_and_add(&kobj->kobject, ktype, parent, "%s", name); + if (err) + kobject_put(&kobj->kobject); + + return err; } static inline void From cf00b33058b196b4db928419dde68993b15a975b Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Mon, 15 Aug 2022 16:40:43 +0100 Subject: [PATCH 0067/1823] cxl/mbox: Add a check on input payload size A bug in the LSA code resulted in transfers slightly larger than the mailbox size. Let us make it easier to catch similar issues in future by adding a low level check. Signed-off-by: Jonathan Cameron Link: https://lore.kernel.org/r/20220815154044.24733-2-Jonathan.Cameron@huawei.com Signed-off-by: Dan Williams --- drivers/cxl/core/mbox.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c index 16176b9278b4..0c90f13870a4 100644 --- a/drivers/cxl/core/mbox.c +++ b/drivers/cxl/core/mbox.c @@ -174,7 +174,7 @@ int cxl_mbox_send_cmd(struct cxl_dev_state *cxlds, u16 opcode, void *in, }; int rc; - if (out_size > cxlds->payload_size) + if (in_size > cxlds->payload_size || out_size > cxlds->payload_size) return -E2BIG; rc = cxlds->mbox_send(cxlds, &mbox_cmd); From 2816e24b0510e0c185c0c46acff1ce7aa4c4443f Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Thu, 18 Aug 2022 17:42:10 +0100 Subject: [PATCH 0068/1823] cxl/region: Fix null pointer dereference due to pass through decoder commit Not all decoders have a commit callback. The CXL specification allows a host bridge with a single root port to have no explicit HDM decoders. Currently the region driver assumes there are none. As such the CXL core creates a special pass through decoder instance without a commit callback. Prior to this patch, the ->commit() callback was called unconditionally. Thus a configuration with 1 Host Bridge, 1 Root Port, 1 switch with multiple downstream ports below which there are multiple CXL type 3 devices results in a situation where committing the region causes a null pointer dereference. Reported-by: Bobo WL Fixes: 176baefb2eb5 ("cxl/hdm: Commit decoder state to hardware") Signed-off-by: Jonathan Cameron Reviewed-by: Vishal Verma Link: https://lore.kernel.org/r/20220818164210.2084-1-Jonathan.Cameron@huawei.com Signed-off-by: Dan Williams --- drivers/cxl/core/region.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index 401148016978..c49d9a5f1091 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -174,7 +174,8 @@ static int cxl_region_decode_commit(struct cxl_region *cxlr) iter = to_cxl_port(iter->dev.parent)) { cxl_rr = cxl_rr_load(iter, cxlr); cxld = cxl_rr->decoder; - rc = cxld->commit(cxld); + if (cxld->commit) + rc = cxld->commit(cxld); if (rc) break; } From f010c75c05299ecd65adfd31a7841eea3476ce1f Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Mon, 15 Aug 2022 16:40:44 +0100 Subject: [PATCH 0069/1823] cxl/pmem: Fix failure to account for 8 byte header for writes to the device LSA. Writes to the device must include an offset and size as defined in CXL 2.0 8.2.9.5.2.4 Set LSA (Opcode 4103h) Fixes tag is non obvious as this code has been through several reworks and variable names + wasn't in use until the addition of the region code. Due to a bug in QEMU CXL emulation this overrun resulted in QEMU crashing. Reported-by: Bobo WL Signed-off-by: Jonathan Cameron Fixes: 60b8f17215de ("cxl/pmem: Translate NVDIMM label commands to CXL label commands") Link: https://lore.kernel.org/r/20220815154044.24733-3-Jonathan.Cameron@huawei.com Signed-off-by: Dan Williams --- drivers/cxl/pmem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cxl/pmem.c b/drivers/cxl/pmem.c index 7dc0a2fa1a6b..115a7b79f343 100644 --- a/drivers/cxl/pmem.c +++ b/drivers/cxl/pmem.c @@ -107,7 +107,7 @@ static int cxl_pmem_get_config_size(struct cxl_dev_state *cxlds, *cmd = (struct nd_cmd_get_config_size) { .config_size = cxlds->lsa_size, - .max_xfer = cxlds->payload_size, + .max_xfer = cxlds->payload_size - sizeof(struct cxl_mbox_set_lsa), }; return 0; From e8a18e3f00f3ee8d07c17ab1ea3ad4df4a3b6fe0 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Fri, 21 Oct 2022 07:44:49 +0200 Subject: [PATCH 0070/1823] parisc: Make 8250_gsc driver dependend on CONFIG_PARISC Although the name of the driver 8250_gsc.c suggests that it handles only serial ports on the GSC bus, it does handle serial ports listed in the parisc machine inventory as well, e.g. the serial ports in a C8000 PCI-only workstation. Change the dependency to CONFIG_PARISC, so that the driver gets included in the kernel even if CONFIG_GSC isn't set. Reported-by: Mikulas Patocka Cc: Signed-off-by: Helge Deller --- drivers/tty/serial/8250/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index d0b49e15fbf5..7b3ffb7f50ab 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -118,7 +118,7 @@ config SERIAL_8250_CONSOLE config SERIAL_8250_GSC tristate - depends on SERIAL_8250 && GSC + depends on SERIAL_8250 && PARISC default SERIAL_8250 config SERIAL_8250_DMA From 9e4e2ce1a78ed92ed91135e90c85f27d75388129 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Fri, 21 Oct 2022 08:02:31 +0200 Subject: [PATCH 0071/1823] parisc/serial: Rename 8250_gsc.c to 8250_parisc.c The file name of this driver is misleading - it handles various serial ports on parisc machines, not just such on the GSC bus. Rename the file to make this clearer. Suggested-by: Mikulas Patocka Signed-off-by: Helge Deller --- drivers/tty/serial/8250/{8250_gsc.c => 8250_parisc.c} | 0 drivers/tty/serial/8250/Kconfig | 2 +- drivers/tty/serial/8250/Makefile | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename drivers/tty/serial/8250/{8250_gsc.c => 8250_parisc.c} (100%) diff --git a/drivers/tty/serial/8250/8250_gsc.c b/drivers/tty/serial/8250/8250_parisc.c similarity index 100% rename from drivers/tty/serial/8250/8250_gsc.c rename to drivers/tty/serial/8250/8250_parisc.c diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 7b3ffb7f50ab..b0f62345bc84 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -116,7 +116,7 @@ config SERIAL_8250_CONSOLE If unsure, say N. -config SERIAL_8250_GSC +config SERIAL_8250_PARISC tristate depends on SERIAL_8250 && PARISC default SERIAL_8250 diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index bee908f99ea0..1615bfdde2a0 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -12,7 +12,7 @@ obj-$(CONFIG_SERIAL_8250) += 8250.o 8250_base.o 8250_base-$(CONFIG_SERIAL_8250_DMA) += 8250_dma.o 8250_base-$(CONFIG_SERIAL_8250_DWLIB) += 8250_dwlib.o 8250_base-$(CONFIG_SERIAL_8250_FINTEK) += 8250_fintek.o -obj-$(CONFIG_SERIAL_8250_GSC) += 8250_gsc.o +obj-$(CONFIG_SERIAL_8250_PARISC) += 8250_parisc.o obj-$(CONFIG_SERIAL_8250_PCI) += 8250_pci.o obj-$(CONFIG_SERIAL_8250_EXAR) += 8250_exar.o obj-$(CONFIG_SERIAL_8250_HP300) += 8250_hp300.o From 66063033f77e10b985258126a97573f84bb8d3b4 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Wed, 19 Oct 2022 09:55:41 -0600 Subject: [PATCH 0072/1823] wifi: rt2x00: use explicitly signed or unsigned types On some platforms, `char` is unsigned, but this driver, for the most part, assumed it was signed. In other places, it uses `char` to mean an unsigned number, but only in cases when the values are small. And in still other places, `char` is used as a boolean. Put an end to this confusion by declaring explicit types, depending on the context. Cc: Andrew Morton Cc: Andy Shevchenko Cc: Stanislaw Gruszka Cc: Helmut Schaa Cc: Kalle Valo Signed-off-by: Jason A. Donenfeld Acked-by: Stanislaw Gruszka Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221019155541.3410813-1-Jason@zx2c4.com --- .../net/wireless/ralink/rt2x00/rt2400pci.c | 8 +-- .../net/wireless/ralink/rt2x00/rt2400pci.h | 2 +- .../net/wireless/ralink/rt2x00/rt2500pci.c | 8 +-- .../net/wireless/ralink/rt2x00/rt2500pci.h | 2 +- .../net/wireless/ralink/rt2x00/rt2500usb.c | 8 +-- .../net/wireless/ralink/rt2x00/rt2500usb.h | 2 +- .../net/wireless/ralink/rt2x00/rt2800lib.c | 60 +++++++++---------- .../net/wireless/ralink/rt2x00/rt2800lib.h | 8 +-- .../net/wireless/ralink/rt2x00/rt2x00usb.c | 6 +- drivers/net/wireless/ralink/rt2x00/rt61pci.c | 4 +- drivers/net/wireless/ralink/rt2x00/rt61pci.h | 2 +- drivers/net/wireless/ralink/rt2x00/rt73usb.c | 4 +- drivers/net/wireless/ralink/rt2x00/rt73usb.h | 2 +- 13 files changed, 58 insertions(+), 58 deletions(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2400pci.c b/drivers/net/wireless/ralink/rt2x00/rt2400pci.c index 273c5eac3362..ddfc16de1b26 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2400pci.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2400pci.c @@ -1023,9 +1023,9 @@ static int rt2400pci_set_state(struct rt2x00_dev *rt2x00dev, { u32 reg, reg2; unsigned int i; - char put_to_sleep; - char bbp_state; - char rf_state; + bool put_to_sleep; + u8 bbp_state; + u8 rf_state; put_to_sleep = (state != STATE_AWAKE); @@ -1561,7 +1561,7 @@ static int rt2400pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) { struct hw_mode_spec *spec = &rt2x00dev->spec; struct channel_info *info; - char *tx_power; + u8 *tx_power; unsigned int i; /* diff --git a/drivers/net/wireless/ralink/rt2x00/rt2400pci.h b/drivers/net/wireless/ralink/rt2x00/rt2400pci.h index b8187b6de143..979d5fd8babf 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2400pci.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2400pci.h @@ -939,7 +939,7 @@ #define DEFAULT_TXPOWER 39 #define __CLAMP_TX(__txpower) \ - clamp_t(char, (__txpower), MIN_TXPOWER, MAX_TXPOWER) + clamp_t(u8, (__txpower), MIN_TXPOWER, MAX_TXPOWER) #define TXPOWER_FROM_DEV(__txpower) \ ((__CLAMP_TX(__txpower) - MAX_TXPOWER) + MIN_TXPOWER) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2500pci.c b/drivers/net/wireless/ralink/rt2x00/rt2500pci.c index 8faa0a80e73a..cd6371e25062 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2500pci.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2500pci.c @@ -1176,9 +1176,9 @@ static int rt2500pci_set_state(struct rt2x00_dev *rt2x00dev, { u32 reg, reg2; unsigned int i; - char put_to_sleep; - char bbp_state; - char rf_state; + bool put_to_sleep; + u8 bbp_state; + u8 rf_state; put_to_sleep = (state != STATE_AWAKE); @@ -1856,7 +1856,7 @@ static int rt2500pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) { struct hw_mode_spec *spec = &rt2x00dev->spec; struct channel_info *info; - char *tx_power; + u8 *tx_power; unsigned int i; /* diff --git a/drivers/net/wireless/ralink/rt2x00/rt2500pci.h b/drivers/net/wireless/ralink/rt2x00/rt2500pci.h index 7e64aee2a172..ba362675c52c 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2500pci.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2500pci.h @@ -1219,6 +1219,6 @@ (((u8)(__txpower)) > MAX_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) #define TXPOWER_TO_DEV(__txpower) \ - clamp_t(char, __txpower, MIN_TXPOWER, MAX_TXPOWER) + clamp_t(u8, __txpower, MIN_TXPOWER, MAX_TXPOWER) #endif /* RT2500PCI_H */ diff --git a/drivers/net/wireless/ralink/rt2x00/rt2500usb.c b/drivers/net/wireless/ralink/rt2x00/rt2500usb.c index bb5ed6630645..4f3b0e6c6256 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2500usb.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2500usb.c @@ -984,9 +984,9 @@ static int rt2500usb_set_state(struct rt2x00_dev *rt2x00dev, u16 reg; u16 reg2; unsigned int i; - char put_to_sleep; - char bbp_state; - char rf_state; + bool put_to_sleep; + u8 bbp_state; + u8 rf_state; put_to_sleep = (state != STATE_AWAKE); @@ -1663,7 +1663,7 @@ static int rt2500usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) { struct hw_mode_spec *spec = &rt2x00dev->spec; struct channel_info *info; - char *tx_power; + u8 *tx_power; unsigned int i; /* diff --git a/drivers/net/wireless/ralink/rt2x00/rt2500usb.h b/drivers/net/wireless/ralink/rt2x00/rt2500usb.h index 0c070288a140..746f0e950b76 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2500usb.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2500usb.h @@ -839,6 +839,6 @@ (((u8)(__txpower)) > MAX_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) #define TXPOWER_TO_DEV(__txpower) \ - clamp_t(char, __txpower, MIN_TXPOWER, MAX_TXPOWER) + clamp_t(u8, __txpower, MIN_TXPOWER, MAX_TXPOWER) #endif /* RT2500USB_H */ diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c index cbbb1a4849cf..12b700c7b9c3 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -3372,10 +3372,10 @@ static void rt2800_config_channel_rf53xx(struct rt2x00_dev *rt2x00dev, if (rt2x00_has_cap_bt_coexist(rt2x00dev)) { if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) { /* r55/r59 value array of channel 1~14 */ - static const char r55_bt_rev[] = {0x83, 0x83, + static const u8 r55_bt_rev[] = {0x83, 0x83, 0x83, 0x73, 0x73, 0x63, 0x53, 0x53, 0x53, 0x43, 0x43, 0x43, 0x43, 0x43}; - static const char r59_bt_rev[] = {0x0e, 0x0e, + static const u8 r59_bt_rev[] = {0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0b, 0x0a, 0x09, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07}; @@ -3384,7 +3384,7 @@ static void rt2800_config_channel_rf53xx(struct rt2x00_dev *rt2x00dev, rt2800_rfcsr_write(rt2x00dev, 59, r59_bt_rev[idx]); } else { - static const char r59_bt[] = {0x8b, 0x8b, 0x8b, + static const u8 r59_bt[] = {0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8a, 0x89, 0x88, 0x88, 0x86, 0x85, 0x84}; @@ -3392,10 +3392,10 @@ static void rt2800_config_channel_rf53xx(struct rt2x00_dev *rt2x00dev, } } else { if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) { - static const char r55_nonbt_rev[] = {0x23, 0x23, + static const u8 r55_nonbt_rev[] = {0x23, 0x23, 0x23, 0x23, 0x13, 0x13, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03}; - static const char r59_nonbt_rev[] = {0x07, 0x07, + static const u8 r59_nonbt_rev[] = {0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x05, 0x04, 0x04}; @@ -3406,14 +3406,14 @@ static void rt2800_config_channel_rf53xx(struct rt2x00_dev *rt2x00dev, } else if (rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392) || rt2x00_rt(rt2x00dev, RT6352)) { - static const char r59_non_bt[] = {0x8f, 0x8f, + static const u8 r59_non_bt[] = {0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8d, 0x8a, 0x88, 0x88, 0x87, 0x87, 0x86}; rt2800_rfcsr_write(rt2x00dev, 59, r59_non_bt[idx]); } else if (rt2x00_rt(rt2x00dev, RT5350)) { - static const char r59_non_bt[] = {0x0b, 0x0b, + static const u8 r59_non_bt[] = {0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x09, 0x08, 0x07, 0x07, 0x06}; @@ -4035,23 +4035,23 @@ static void rt2800_iq_calibrate(struct rt2x00_dev *rt2x00dev, int channel) rt2800_bbp_write(rt2x00dev, 159, cal != 0xff ? cal : 0); } -static char rt2800_txpower_to_dev(struct rt2x00_dev *rt2x00dev, +static s8 rt2800_txpower_to_dev(struct rt2x00_dev *rt2x00dev, unsigned int channel, - char txpower) + s8 txpower) { if (rt2x00_rt(rt2x00dev, RT3593) || rt2x00_rt(rt2x00dev, RT3883)) txpower = rt2x00_get_field8(txpower, EEPROM_TXPOWER_ALC); if (channel <= 14) - return clamp_t(char, txpower, MIN_G_TXPOWER, MAX_G_TXPOWER); + return clamp_t(s8, txpower, MIN_G_TXPOWER, MAX_G_TXPOWER); if (rt2x00_rt(rt2x00dev, RT3593) || rt2x00_rt(rt2x00dev, RT3883)) - return clamp_t(char, txpower, MIN_A_TXPOWER_3593, + return clamp_t(s8, txpower, MIN_A_TXPOWER_3593, MAX_A_TXPOWER_3593); else - return clamp_t(char, txpower, MIN_A_TXPOWER, MAX_A_TXPOWER); + return clamp_t(s8, txpower, MIN_A_TXPOWER, MAX_A_TXPOWER); } static void rt3883_bbp_adjust(struct rt2x00_dev *rt2x00dev, @@ -8530,7 +8530,7 @@ static void rt2800_r_calibration(struct rt2x00_dev *rt2x00dev) u8 bytevalue = 0; int rcalcode; u8 r_cal_code = 0; - char d1 = 0, d2 = 0; + s8 d1 = 0, d2 = 0; u8 rfvalue; u32 MAC_RF_BYPASS0, MAC_RF_CONTROL0, MAC_PWR_PIN_CFG; u32 maccfg; @@ -8591,7 +8591,7 @@ static void rt2800_r_calibration(struct rt2x00_dev *rt2x00dev) if (bytevalue > 128) d1 = bytevalue - 256; else - d1 = (char)bytevalue; + d1 = (s8)bytevalue; rt2800_bbp_write(rt2x00dev, 22, 0x0); rt2800_rfcsr_write_bank(rt2x00dev, 0, 35, 0x01); @@ -8601,7 +8601,7 @@ static void rt2800_r_calibration(struct rt2x00_dev *rt2x00dev) if (bytevalue > 128) d2 = bytevalue - 256; else - d2 = (char)bytevalue; + d2 = (s8)bytevalue; rt2800_bbp_write(rt2x00dev, 22, 0x0); rcalcode = rt2800_calcrcalibrationcode(rt2x00dev, d1, d2); @@ -8703,7 +8703,7 @@ static void rt2800_rxdcoc_calibration(struct rt2x00_dev *rt2x00dev) static u32 rt2800_do_sqrt_accumulation(u32 si) { u32 root, root_pre, bit; - char i; + s8 i; bit = 1 << 15; root = 0; @@ -9330,11 +9330,11 @@ static void rt2800_loft_search(struct rt2x00_dev *rt2x00dev, u8 ch_idx, u8 alc_idx, u8 dc_result[][RF_ALC_NUM][2]) { u32 p0 = 0, p1 = 0, pf = 0; - char idx0 = 0, idx1 = 0; + s8 idx0 = 0, idx1 = 0; u8 idxf[] = {0x00, 0x00}; u8 ibit = 0x20; u8 iorq; - char bidx; + s8 bidx; rt2800_bbp_write(rt2x00dev, 158, 0xb0); rt2800_bbp_write(rt2x00dev, 159, 0x80); @@ -9384,17 +9384,17 @@ static void rt2800_loft_search(struct rt2x00_dev *rt2x00dev, u8 ch_idx, static void rt2800_iq_search(struct rt2x00_dev *rt2x00dev, u8 ch_idx, u8 *ges, u8 *pes) { u32 p0 = 0, p1 = 0, pf = 0; - char perr = 0, gerr = 0, iq_err = 0; - char pef = 0, gef = 0; - char psta, pend; - char gsta, gend; + s8 perr = 0, gerr = 0, iq_err = 0; + s8 pef = 0, gef = 0; + s8 psta, pend; + s8 gsta, gend; u8 ibit = 0x20; u8 first_search = 0x00, touch_neg_max = 0x00; - char idx0 = 0, idx1 = 0; + s8 idx0 = 0, idx1 = 0; u8 gop; u8 bbp = 0; - char bidx; + s8 bidx; for (bidx = 5; bidx >= 1; bidx--) { for (gop = 0; gop < 2; gop++) { @@ -10043,11 +10043,11 @@ static int rt2800_rf_lp_config(struct rt2x00_dev *rt2x00dev, bool btxcal) return 0; } -static char rt2800_lp_tx_filter_bw_cal(struct rt2x00_dev *rt2x00dev) +static s8 rt2800_lp_tx_filter_bw_cal(struct rt2x00_dev *rt2x00dev) { unsigned int cnt; u8 bbp_val; - char cal_val; + s8 cal_val; rt2800_bbp_dcoc_write(rt2x00dev, 0, 0x82); @@ -10079,7 +10079,7 @@ static void rt2800_bw_filter_calibration(struct rt2x00_dev *rt2x00dev, u8 rx_filter_target_20m = 0x27, rx_filter_target_40m = 0x31; int loop = 0, is_ht40, cnt; u8 bbp_val, rf_val; - char cal_r32_init, cal_r32_val, cal_diff; + s8 cal_r32_init, cal_r32_val, cal_diff; u8 saverfb5r00, saverfb5r01, saverfb5r03, saverfb5r04, saverfb5r05; u8 saverfb5r06, saverfb5r07; u8 saverfb5r08, saverfb5r17, saverfb5r18, saverfb5r19, saverfb5r20; @@ -11550,9 +11550,9 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) { struct hw_mode_spec *spec = &rt2x00dev->spec; struct channel_info *info; - char *default_power1; - char *default_power2; - char *default_power3; + s8 *default_power1; + s8 *default_power2; + s8 *default_power3; unsigned int i, tx_chains, rx_chains; u32 reg; diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h index 3cbef77b4bd3..194de676df8f 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h @@ -32,10 +32,10 @@ struct rf_reg_pair { struct rt2800_drv_data { u8 calibration_bw20; u8 calibration_bw40; - char rx_calibration_bw20; - char rx_calibration_bw40; - char tx_calibration_bw20; - char tx_calibration_bw40; + s8 rx_calibration_bw20; + s8 rx_calibration_bw40; + s8 tx_calibration_bw20; + s8 tx_calibration_bw40; u8 bbp25; u8 bbp26; u8 txmixer_gain_24g; diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c index 0827bc860bf8..8fd22c69855f 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c @@ -117,12 +117,12 @@ int rt2x00usb_vendor_request_buff(struct rt2x00_dev *rt2x00dev, const u16 buffer_length) { int status = 0; - unsigned char *tb; + u8 *tb; u16 off, len, bsize; mutex_lock(&rt2x00dev->csr_mutex); - tb = (char *)buffer; + tb = (u8 *)buffer; off = offset; len = buffer_length; while (len && !status) { @@ -215,7 +215,7 @@ void rt2x00usb_register_read_async(struct rt2x00_dev *rt2x00dev, rd->cr.wLength = cpu_to_le16(sizeof(u32)); usb_fill_control_urb(urb, usb_dev, usb_rcvctrlpipe(usb_dev, 0), - (unsigned char *)(&rd->cr), &rd->reg, sizeof(rd->reg), + (u8 *)(&rd->cr), &rd->reg, sizeof(rd->reg), rt2x00usb_register_read_async_cb, rd); usb_anchor_urb(urb, rt2x00dev->anchor); if (usb_submit_urb(urb, GFP_ATOMIC) < 0) { diff --git a/drivers/net/wireless/ralink/rt2x00/rt61pci.c b/drivers/net/wireless/ralink/rt2x00/rt61pci.c index d92f9eb07dc9..81db7f57c7e4 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt61pci.c +++ b/drivers/net/wireless/ralink/rt2x00/rt61pci.c @@ -1709,7 +1709,7 @@ static int rt61pci_set_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) { u32 reg, reg2; unsigned int i; - char put_to_sleep; + bool put_to_sleep; put_to_sleep = (state != STATE_AWAKE); @@ -2656,7 +2656,7 @@ static int rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) { struct hw_mode_spec *spec = &rt2x00dev->spec; struct channel_info *info; - char *tx_power; + u8 *tx_power; unsigned int i; /* diff --git a/drivers/net/wireless/ralink/rt2x00/rt61pci.h b/drivers/net/wireless/ralink/rt2x00/rt61pci.h index 5f208ad509bd..d72d0ffd1127 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt61pci.h +++ b/drivers/net/wireless/ralink/rt2x00/rt61pci.h @@ -1484,6 +1484,6 @@ struct hw_pairwise_ta_entry { (((u8)(__txpower)) > MAX_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) #define TXPOWER_TO_DEV(__txpower) \ - clamp_t(char, __txpower, MIN_TXPOWER, MAX_TXPOWER) + clamp_t(u8, __txpower, MIN_TXPOWER, MAX_TXPOWER) #endif /* RT61PCI_H */ diff --git a/drivers/net/wireless/ralink/rt2x00/rt73usb.c b/drivers/net/wireless/ralink/rt2x00/rt73usb.c index e3269fd7c59e..861035444374 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt73usb.c +++ b/drivers/net/wireless/ralink/rt2x00/rt73usb.c @@ -1378,7 +1378,7 @@ static int rt73usb_set_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) { u32 reg, reg2; unsigned int i; - char put_to_sleep; + bool put_to_sleep; put_to_sleep = (state != STATE_AWAKE); @@ -2090,7 +2090,7 @@ static int rt73usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) { struct hw_mode_spec *spec = &rt2x00dev->spec; struct channel_info *info; - char *tx_power; + u8 *tx_power; unsigned int i; /* diff --git a/drivers/net/wireless/ralink/rt2x00/rt73usb.h b/drivers/net/wireless/ralink/rt2x00/rt73usb.h index 1b56d285c34b..bb0a68516c08 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt73usb.h +++ b/drivers/net/wireless/ralink/rt2x00/rt73usb.h @@ -1063,6 +1063,6 @@ struct hw_pairwise_ta_entry { (((u8)(__txpower)) > MAX_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) #define TXPOWER_TO_DEV(__txpower) \ - clamp_t(char, __txpower, MIN_TXPOWER, MAX_TXPOWER) + clamp_t(u8, __txpower, MIN_TXPOWER, MAX_TXPOWER) #endif /* RT73USB_H */ From 50f19697dd768d8b072cf7f12c0c99c7d31b67d8 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Fri, 21 Oct 2022 09:15:20 +0200 Subject: [PATCH 0073/1823] parisc: Use signed char for hardware path in pdc.h Clean up the struct for hardware_path and drop the struct device_path with a proper assignment of bc[] and mod members as signed chars. This patch prepares for the kbuild change from Jason A. Donenfeld to treat char as always unsigned. Signed-off-by: Helge Deller Cc: Jason A. Donenfeld --- arch/parisc/include/uapi/asm/pdc.h | 36 +++++++++++------------------- drivers/parisc/pdc_stable.c | 34 ++++++++++++++-------------- 2 files changed, 30 insertions(+), 40 deletions(-) diff --git a/arch/parisc/include/uapi/asm/pdc.h b/arch/parisc/include/uapi/asm/pdc.h index e794e143ec5f..7a90070136e8 100644 --- a/arch/parisc/include/uapi/asm/pdc.h +++ b/arch/parisc/include/uapi/asm/pdc.h @@ -363,20 +363,25 @@ #if !defined(__ASSEMBLY__) -/* flags of the device_path */ +/* flags for hardware_path */ #define PF_AUTOBOOT 0x80 #define PF_AUTOSEARCH 0x40 #define PF_TIMER 0x0F -struct device_path { /* page 1-69 */ - unsigned char flags; /* flags see above! */ - unsigned char bc[6]; /* bus converter routing info */ - unsigned char mod; - unsigned int layers[6];/* device-specific layer-info */ -} __attribute__((aligned(8))) ; +struct hardware_path { + unsigned char flags; /* see bit definitions below */ + signed char bc[6]; /* Bus Converter routing info to a specific */ + /* I/O adaptor (< 0 means none, > 63 resvd) */ + signed char mod; /* fixed field of specified module */ +}; + +struct pdc_module_path { /* page 1-69 */ + struct hardware_path path; + unsigned int layers[6]; /* device-specific info (ctlr #, unit # ...) */ +} __attribute__((aligned(8))); struct pz_device { - struct device_path dp; /* see above */ + struct pdc_module_path dp; /* see above */ /* struct iomod *hpa; */ unsigned int hpa; /* HPA base address */ /* char *spa; */ @@ -611,21 +616,6 @@ struct pdc_initiator { /* PDC_INITIATOR */ int mode; }; -struct hardware_path { - char flags; /* see bit definitions below */ - char bc[6]; /* Bus Converter routing info to a specific */ - /* I/O adaptor (< 0 means none, > 63 resvd) */ - char mod; /* fixed field of specified module */ -}; - -/* - * Device path specifications used by PDC. - */ -struct pdc_module_path { - struct hardware_path path; - unsigned int layers[6]; /* device-specific info (ctlr #, unit # ...) */ -}; - /* Only used on some pre-PA2.0 boxes */ struct pdc_memory_map { /* PDC_MEMORY_MAP */ unsigned long hpa; /* mod's register set address */ diff --git a/drivers/parisc/pdc_stable.c b/drivers/parisc/pdc_stable.c index d9e51036a4fa..d6af5726ddf3 100644 --- a/drivers/parisc/pdc_stable.c +++ b/drivers/parisc/pdc_stable.c @@ -14,7 +14,7 @@ * all) PA-RISC machines should have them. Anyway, for safety reasons, the * following code can deal with just 96 bytes of Stable Storage, and all * sizes between 96 and 192 bytes (provided they are multiple of struct - * device_path size, eg: 128, 160 and 192) to provide full information. + * pdc_module_path size, eg: 128, 160 and 192) to provide full information. * One last word: there's one path we can always count on: the primary path. * Anything above 224 bytes is used for 'osdep2' OS-dependent storage area. * @@ -88,7 +88,7 @@ struct pdcspath_entry { short ready; /* entry record is valid if != 0 */ unsigned long addr; /* entry address in stable storage */ char *name; /* entry name */ - struct device_path devpath; /* device path in parisc representation */ + struct pdc_module_path devpath; /* device path in parisc representation */ struct device *dev; /* corresponding device */ struct kobject kobj; }; @@ -138,7 +138,7 @@ struct pdcspath_attribute paths_attr_##_name = { \ static int pdcspath_fetch(struct pdcspath_entry *entry) { - struct device_path *devpath; + struct pdc_module_path *devpath; if (!entry) return -EINVAL; @@ -153,7 +153,7 @@ pdcspath_fetch(struct pdcspath_entry *entry) return -EIO; /* Find the matching device. - NOTE: hardware_path overlays with device_path, so the nice cast can + NOTE: hardware_path overlays with pdc_module_path, so the nice cast can be used */ entry->dev = hwpath_to_device((struct hardware_path *)devpath); @@ -179,7 +179,7 @@ pdcspath_fetch(struct pdcspath_entry *entry) static void pdcspath_store(struct pdcspath_entry *entry) { - struct device_path *devpath; + struct pdc_module_path *devpath; BUG_ON(!entry); @@ -221,7 +221,7 @@ static ssize_t pdcspath_hwpath_read(struct pdcspath_entry *entry, char *buf) { char *out = buf; - struct device_path *devpath; + struct pdc_module_path *devpath; short i; if (!entry || !buf) @@ -236,11 +236,11 @@ pdcspath_hwpath_read(struct pdcspath_entry *entry, char *buf) return -ENODATA; for (i = 0; i < 6; i++) { - if (devpath->bc[i] >= 128) + if (devpath->path.bc[i] < 0) continue; - out += sprintf(out, "%u/", (unsigned char)devpath->bc[i]); + out += sprintf(out, "%d/", devpath->path.bc[i]); } - out += sprintf(out, "%u\n", (unsigned char)devpath->mod); + out += sprintf(out, "%u\n", (unsigned char)devpath->path.mod); return out - buf; } @@ -296,12 +296,12 @@ pdcspath_hwpath_write(struct pdcspath_entry *entry, const char *buf, size_t coun for (i=5; ((temp = strrchr(in, '/'))) && (temp-in > 0) && (likely(i)); i--) { hwpath.bc[i] = simple_strtoul(temp+1, NULL, 10); in[temp-in] = '\0'; - DPRINTK("%s: bc[%d]: %d\n", __func__, i, hwpath.bc[i]); + DPRINTK("%s: bc[%d]: %d\n", __func__, i, hwpath.path.bc[i]); } /* Store the final field */ hwpath.bc[i] = simple_strtoul(in, NULL, 10); - DPRINTK("%s: bc[%d]: %d\n", __func__, i, hwpath.bc[i]); + DPRINTK("%s: bc[%d]: %d\n", __func__, i, hwpath.path.bc[i]); /* Now we check that the user isn't trying to lure us */ if (!(dev = hwpath_to_device((struct hardware_path *)&hwpath))) { @@ -342,7 +342,7 @@ static ssize_t pdcspath_layer_read(struct pdcspath_entry *entry, char *buf) { char *out = buf; - struct device_path *devpath; + struct pdc_module_path *devpath; short i; if (!entry || !buf) @@ -547,7 +547,7 @@ static ssize_t pdcs_auto_read(struct kobject *kobj, pathentry = &pdcspath_entry_primary; read_lock(&pathentry->rw_lock); - out += sprintf(out, "%s\n", (pathentry->devpath.flags & knob) ? + out += sprintf(out, "%s\n", (pathentry->devpath.path.flags & knob) ? "On" : "Off"); read_unlock(&pathentry->rw_lock); @@ -594,8 +594,8 @@ static ssize_t pdcs_timer_read(struct kobject *kobj, /* print the timer value in seconds */ read_lock(&pathentry->rw_lock); - out += sprintf(out, "%u\n", (pathentry->devpath.flags & PF_TIMER) ? - (1 << (pathentry->devpath.flags & PF_TIMER)) : 0); + out += sprintf(out, "%u\n", (pathentry->devpath.path.flags & PF_TIMER) ? + (1 << (pathentry->devpath.path.flags & PF_TIMER)) : 0); read_unlock(&pathentry->rw_lock); return out - buf; @@ -764,7 +764,7 @@ static ssize_t pdcs_auto_write(struct kobject *kobj, /* Be nice to the existing flag record */ read_lock(&pathentry->rw_lock); - flags = pathentry->devpath.flags; + flags = pathentry->devpath.path.flags; read_unlock(&pathentry->rw_lock); DPRINTK("%s: flags before: 0x%X\n", __func__, flags); @@ -785,7 +785,7 @@ static ssize_t pdcs_auto_write(struct kobject *kobj, write_lock(&pathentry->rw_lock); /* Change the path entry flags first */ - pathentry->devpath.flags = flags; + pathentry->devpath.path.flags = flags; /* Now, dive in. Write back to the hardware */ pdcspath_store(pathentry); From 03c0ad4b06c3566de624b4f4b78ac1a5d1e4c8e7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 13 Oct 2022 19:41:51 +0200 Subject: [PATCH 0074/1823] wifi: cfg80211: silence a sparse RCU warning All we're going to do with this pointer is assign it to another __rcu pointer, but sparse can't see that, so use rcu_access_pointer() to silence the warning here. Fixes: c90b93b5b782 ("wifi: cfg80211: update hidden BSSes to avoid WARN_ON") Signed-off-by: Johannes Berg --- net/wireless/scan.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 806a5f1330ff..da752b0cc752 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -1674,7 +1674,9 @@ cfg80211_update_known_bss(struct cfg80211_registered_device *rdev, if (old == rcu_access_pointer(known->pub.ies)) rcu_assign_pointer(known->pub.ies, new->pub.beacon_ies); - cfg80211_update_hidden_bsses(known, new->pub.beacon_ies, old); + cfg80211_update_hidden_bsses(known, + rcu_access_pointer(new->pub.beacon_ies), + old); if (old) kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head); From 50b2e8711462409cd368c41067405aa446dfa2af Mon Sep 17 00:00:00 2001 From: taozhang Date: Sat, 15 Oct 2022 17:38:31 +0800 Subject: [PATCH 0075/1823] wifi: mac80211: fix memory free error when registering wiphy fail ieee80211_register_hw free the allocated cipher suites when registering wiphy fail, and ieee80211_free_hw will re-free it. set wiphy_ciphers_allocated to false after freeing allocated cipher suites. Signed-off-by: taozhang Signed-off-by: Johannes Berg --- net/mac80211/main.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 46f3eddc2388..02b5abc7326b 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1439,8 +1439,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) ieee80211_led_exit(local); destroy_workqueue(local->workqueue); fail_workqueue: - if (local->wiphy_ciphers_allocated) + if (local->wiphy_ciphers_allocated) { kfree(local->hw.wiphy->cipher_suites); + local->wiphy_ciphers_allocated = false; + } kfree(local->int_scan_req); return result; } @@ -1508,8 +1510,10 @@ void ieee80211_free_hw(struct ieee80211_hw *hw) mutex_destroy(&local->iflist_mtx); mutex_destroy(&local->mtx); - if (local->wiphy_ciphers_allocated) + if (local->wiphy_ciphers_allocated) { kfree(local->hw.wiphy->cipher_suites); + local->wiphy_ciphers_allocated = false; + } idr_for_each(&local->ack_status_frames, ieee80211_free_ack_frame, NULL); From 57b962e627ec0ae53d4d16d7bd1033e27e67677a Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 20 Oct 2022 13:40:40 +0200 Subject: [PATCH 0076/1823] wifi: cfg80211: fix memory leak in query_regdb_file() In the function query_regdb_file() the alpha2 parameter is duplicated using kmemdup() and subsequently freed in regdb_fw_cb(). However, request_firmware_nowait() can fail without calling regdb_fw_cb() and thus leak memory. Fixes: 007f6c5e6eb4 ("cfg80211: support loading regulatory database as firmware file") Signed-off-by: Arend van Spriel Signed-off-by: Johannes Berg --- net/wireless/reg.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index d5c7a5aa6853..c3d950d29432 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1084,6 +1084,8 @@ MODULE_FIRMWARE("regulatory.db"); static int query_regdb_file(const char *alpha2) { + int err; + ASSERT_RTNL(); if (regdb) @@ -1093,9 +1095,13 @@ static int query_regdb_file(const char *alpha2) if (!alpha2) return -ENOMEM; - return request_firmware_nowait(THIS_MODULE, true, "regulatory.db", - ®_pdev->dev, GFP_KERNEL, - (void *)alpha2, regdb_fw_cb); + err = request_firmware_nowait(THIS_MODULE, true, "regulatory.db", + ®_pdev->dev, GFP_KERNEL, + (void *)alpha2, regdb_fw_cb); + if (err) + kfree(alpha2); + + return err; } int reg_reload_regdb(void) From 18429c51c7ff6e6bfd627316c54670230967a7e5 Mon Sep 17 00:00:00 2001 From: Paul Zhang Date: Tue, 11 Oct 2022 21:04:28 +0800 Subject: [PATCH 0077/1823] wifi: cfg80211: Fix bitrates overflow issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When invoking function cfg80211_calculate_bitrate_eht about (320 MHz, EHT-MCS 13, EHT-NSS 2, EHT-GI 0), which means the parameters as flags: 0x80, bw: 7, mcs: 13, eht_gi: 0, nss: 2, this formula (result * rate->nss) will overflow and causes the returned bitrate to be 3959 when it should be 57646. Here is the explanation: u64 tmp; u32 result; … /* tmp = result = 4 * rates_996[0] * = 4 * 480388888 = 0x72889c60 */ tmp = result; /* tmp = 0x72889c60 * 6144 = 0xabccea90000 */ tmp *= SCALE; /* tmp = 0xabccea90000 / mcs_divisors[13] * = 0xabccea90000 / 5120 = 0x8970bba6 */ do_div(tmp, mcs_divisors[rate->mcs]); /* result = 0x8970bba6 */ result = tmp; /* normally (result * rate->nss) = 0x8970bba6 * 2 = 0x112e1774c, * but since result is u32, (result * rate->nss) = 0x12e1774c, * overflow happens and it loses the highest bit. * Then result = 0x12e1774c / 8 = 39595753, */ result = (result * rate->nss) / 8; Signed-off-by: Paul Zhang Signed-off-by: Johannes Berg --- net/wireless/util.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/wireless/util.c b/net/wireless/util.c index 1f285b515028..39680e7bad45 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1557,10 +1557,12 @@ static u32 cfg80211_calculate_bitrate_eht(struct rate_info *rate) tmp = result; tmp *= SCALE; do_div(tmp, mcs_divisors[rate->mcs]); - result = tmp; /* and take NSS */ - result = (result * rate->nss) / 8; + tmp *= rate->nss; + do_div(tmp, 8); + + result = tmp; return result / 10000; } From 69188df5f6e4cecc6b76b958979ba363cd5240e8 Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Fri, 14 Oct 2022 16:54:39 +0200 Subject: [PATCH 0078/1823] wifi: mac80211_hwsim: fix debugfs attribute ps with rc table support Fixes a warning that occurs when rc table support is enabled (IEEE80211_HW_SUPPORTS_RC_TABLE) in mac80211_hwsim and the PS mode is changed via the exported debugfs attribute. When the PS mode is changed, a packet is broadcasted via hwsim_send_nullfunc by creating and transmitting a plain skb with only header initialized. The ieee80211 rate array in the control buffer is zero-initialized. When ratetbl support is enabled, ieee80211_get_tx_rates is called for the skb with sta parameter set to NULL and thus no ratetbl can be used. The final rate array then looks like [-1,0; 0,0; 0,0; 0,0] which causes the warning in ieee80211_get_tx_rate. The issue is fixed by setting the count of the first rate with idx '0' to 1 and hence ieee80211_get_tx_rates won't overwrite it with idx '-1'. Signed-off-by: Jonas Jelonek Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index a40636c90ec3..0d81098c7b45 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -910,6 +910,7 @@ static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac, struct hwsim_vif_priv *vp = (void *)vif->drv_priv; struct sk_buff *skb; struct ieee80211_hdr *hdr; + struct ieee80211_tx_info *cb; if (!vp->assoc) return; @@ -931,6 +932,10 @@ static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac, memcpy(hdr->addr2, mac, ETH_ALEN); memcpy(hdr->addr3, vp->bssid, ETH_ALEN); + cb = IEEE80211_SKB_CB(skb); + cb->control.rates[0].count = 1; + cb->control.rates[1].idx = -1; + rcu_read_lock(); mac80211_hwsim_tx_frame(data->hw, skb, rcu_dereference(vif->bss_conf.chanctx_conf)->def.chan); From 24f0692bfd41fd207d99c993a5785c3426762046 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 20 Oct 2022 16:54:55 -0700 Subject: [PATCH 0079/1823] ACPI: NUMA: Add CXL CFMWS 'nodes' to the possible nodes set The ACPI CEDT.CFMWS indicates a range of possible address where new CXL regions can appear. Each range is associated with a QTG id (QoS Throttling Group id). For each range + QTG pair that is not covered by a proximity domain in the SRAT, Linux creates a new NUMA node. However, the commit that added the new ranges missed updating the node_possible mask which causes memory_group_register() to fail. Add the new nodes to the nodes_possible mask. Cc: Fixes: fd49f99c1809 ("ACPI: NUMA: Add a node and memblk for each CFMWS not in SRAT") Cc: Alison Schofield Cc: Rafael J. Wysocki Reported-by: Vishal Verma Tested-by: Vishal Verma Acked-by: Rafael J. Wysocki Reviewed-by: Vishal Verma Link: https://lore.kernel.org/r/166631003537.1167078.9373680312035292395.stgit@dwillia2-xfh.jf.intel.com Signed-off-by: Dan Williams --- drivers/acpi/numa/srat.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/acpi/numa/srat.c b/drivers/acpi/numa/srat.c index 3b818ab186be..1f4fc5f8a819 100644 --- a/drivers/acpi/numa/srat.c +++ b/drivers/acpi/numa/srat.c @@ -327,6 +327,7 @@ static int __init acpi_parse_cfmws(union acpi_subtable_headers *header, pr_warn("ACPI NUMA: Failed to add memblk for CFMWS node %d [mem %#llx-%#llx]\n", node, start, end); } + node_set(node, numa_nodes_parsed); /* Set the next available fake_pxm value */ (*fake_pxm)++; From 0469e56a14bf8cfb80507e51b7aeec0332cdbc13 Mon Sep 17 00:00:00 2001 From: Jim Mattson Date: Fri, 30 Sep 2022 00:51:58 +0200 Subject: [PATCH 0080/1823] KVM: x86: Mask off reserved bits in CPUID.80000001H KVM_GET_SUPPORTED_CPUID should only enumerate features that KVM actually supports. CPUID.80000001:EBX[27:16] are reserved bits and should be masked off. Fixes: 0771671749b5 ("KVM: Enhance guest cpuid management") Signed-off-by: Jim Mattson Cc: stable@vger.kernel.org Signed-off-by: Paolo Bonzini --- arch/x86/kvm/cpuid.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index 7065462378e2..834feeb0a828 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -1133,6 +1133,7 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function) entry->eax = max(entry->eax, 0x80000021); break; case 0x80000001: + entry->ebx &= ~GENMASK(27, 16); cpuid_entry_override(entry, CPUID_8000_0001_EDX); cpuid_entry_override(entry, CPUID_8000_0001_ECX); break; From eeb69eab57c6604ac90b3fd8e5ac43f24a5535b1 Mon Sep 17 00:00:00 2001 From: Jim Mattson Date: Thu, 29 Sep 2022 15:51:59 -0700 Subject: [PATCH 0081/1823] KVM: x86: Mask off reserved bits in CPUID.80000006H KVM_GET_SUPPORTED_CPUID should only enumerate features that KVM actually supports. CPUID.80000006H:EDX[17:16] are reserved bits and should be masked off. Fixes: 43d05de2bee7 ("KVM: pass through CPUID(0x80000006)") Signed-off-by: Jim Mattson Message-Id: <20220929225203.2234702-2-jmattson@google.com> Cc: stable@vger.kernel.org Signed-off-by: Paolo Bonzini --- arch/x86/kvm/cpuid.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index 834feeb0a828..8325a01cb1f1 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -1138,7 +1138,8 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function) cpuid_entry_override(entry, CPUID_8000_0001_ECX); break; case 0x80000006: - /* L2 cache and TLB: pass through host info. */ + /* Drop reserved bits, pass host L2 cache and TLB info. */ + entry->edx &= ~GENMASK(17, 16); break; case 0x80000007: /* Advanced power management */ /* invariant TSC is CPUID.80000007H:EDX[8] */ From 7030d8530e533844e2f4b0e7476498afcd324634 Mon Sep 17 00:00:00 2001 From: Jim Mattson Date: Thu, 29 Sep 2022 15:52:00 -0700 Subject: [PATCH 0082/1823] KVM: x86: Mask off reserved bits in CPUID.80000008H KVM_GET_SUPPORTED_CPUID should only enumerate features that KVM actually supports. The following ranges of CPUID.80000008H are reserved and should be masked off: ECX[31:18] ECX[11:8] In addition, the PerfTscSize field at ECX[17:16] should also be zero because KVM does not set the PERFTSC bit at CPUID.80000001H.ECX[27]. Fixes: 24c82e576b78 ("KVM: Sanitize cpuid") Signed-off-by: Jim Mattson Message-Id: <20220929225203.2234702-3-jmattson@google.com> Cc: stable@vger.kernel.org Signed-off-by: Paolo Bonzini --- arch/x86/kvm/cpuid.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index 8325a01cb1f1..489c028859e1 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -1169,6 +1169,7 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function) g_phys_as = phys_as; entry->eax = g_phys_as | (virt_as << 8); + entry->ecx &= ~(GENMASK(31, 16) | GENMASK(11, 8)); entry->edx = 0; cpuid_entry_override(entry, CPUID_8000_0008_EBX); break; From 079f6889818dd07903fb36c252532ab47ebb6d48 Mon Sep 17 00:00:00 2001 From: Jim Mattson Date: Thu, 29 Sep 2022 15:52:01 -0700 Subject: [PATCH 0083/1823] KVM: x86: Mask off reserved bits in CPUID.8000001AH KVM_GET_SUPPORTED_CPUID should only enumerate features that KVM actually supports. In the case of CPUID.8000001AH, only three bits are currently defined. The 125 reserved bits should be masked off. Fixes: 24c82e576b78 ("KVM: Sanitize cpuid") Signed-off-by: Jim Mattson Message-Id: <20220929225203.2234702-4-jmattson@google.com> Cc: stable@vger.kernel.org Signed-off-by: Paolo Bonzini --- arch/x86/kvm/cpuid.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index 489c028859e1..a0292ba650df 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -1189,6 +1189,9 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function) entry->ecx = entry->edx = 0; break; case 0x8000001a: + entry->eax &= GENMASK(2, 0); + entry->ebx = entry->ecx = entry->edx = 0; + break; case 0x8000001e: break; case 0x8000001F: From f23f1a1e8437e38014fe34a2f12e37e861e5bcc7 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 21 Sep 2022 03:10:08 +0200 Subject: [PATCH 0084/1823] arm64: dts: imx8mm: Enable CPLD_Dn pull down resistor on MX8Menlo Enable CPLD_Dn pull down resistor instead of pull up to avoid intefering with CPLD power off functionality. Fixes: 510c527b4ff57 ("arm64: dts: imx8mm: Add i.MX8M Mini Toradex Verdin based Menlo board") Signed-off-by: Marek Vasut Reviewed-by: Fabio Estevam Signed-off-by: Shawn Guo --- .../arm64/boot/dts/freescale/imx8mm-mx8menlo.dts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mm-mx8menlo.dts b/arch/arm64/boot/dts/freescale/imx8mm-mx8menlo.dts index 32f6f2f50c10..43e89859c044 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm-mx8menlo.dts +++ b/arch/arm64/boot/dts/freescale/imx8mm-mx8menlo.dts @@ -250,21 +250,21 @@ /* SODIMM 96 */ MX8MM_IOMUXC_SAI1_RXD2_GPIO4_IO4 0x1c4 /* CPLD_D[7] */ - MX8MM_IOMUXC_SAI1_RXD3_GPIO4_IO5 0x1c4 + MX8MM_IOMUXC_SAI1_RXD3_GPIO4_IO5 0x184 /* CPLD_D[6] */ - MX8MM_IOMUXC_SAI1_RXFS_GPIO4_IO0 0x1c4 + MX8MM_IOMUXC_SAI1_RXFS_GPIO4_IO0 0x184 /* CPLD_D[5] */ - MX8MM_IOMUXC_SAI1_TXC_GPIO4_IO11 0x1c4 + MX8MM_IOMUXC_SAI1_TXC_GPIO4_IO11 0x184 /* CPLD_D[4] */ - MX8MM_IOMUXC_SAI1_TXD0_GPIO4_IO12 0x1c4 + MX8MM_IOMUXC_SAI1_TXD0_GPIO4_IO12 0x184 /* CPLD_D[3] */ - MX8MM_IOMUXC_SAI1_TXD1_GPIO4_IO13 0x1c4 + MX8MM_IOMUXC_SAI1_TXD1_GPIO4_IO13 0x184 /* CPLD_D[2] */ - MX8MM_IOMUXC_SAI1_TXD2_GPIO4_IO14 0x1c4 + MX8MM_IOMUXC_SAI1_TXD2_GPIO4_IO14 0x184 /* CPLD_D[1] */ - MX8MM_IOMUXC_SAI1_TXD3_GPIO4_IO15 0x1c4 + MX8MM_IOMUXC_SAI1_TXD3_GPIO4_IO15 0x184 /* CPLD_D[0] */ - MX8MM_IOMUXC_SAI1_TXD4_GPIO4_IO16 0x1c4 + MX8MM_IOMUXC_SAI1_TXD4_GPIO4_IO16 0x184 /* KBD_intK */ MX8MM_IOMUXC_SAI2_MCLK_GPIO4_IO27 0x1c4 /* DISP_reset */ From f4cd18c5b2000df0c382f6530eeca9141ea41faf Mon Sep 17 00:00:00 2001 From: Jerry Snitselaar Date: Sat, 22 Oct 2022 08:23:52 -0700 Subject: [PATCH 0085/1823] efi/tpm: Pass correct address to memblock_reserve memblock_reserve() expects a physical address, but the address being passed for the TPM final events log is what was returned from early_memremap(). This results in something like the following: [ 0.000000] memblock_reserve: [0xffffffffff2c0000-0xffffffffff2c00e4] efi_tpm_eventlog_init+0x324/0x370 Pass the address from efi like what is done for the TPM events log. Fixes: c46f3405692d ("tpm: Reserve the TPM final events table") Cc: Matthew Garrett Cc: Jarkko Sakkinen Cc: Bartosz Szczepanek Cc: Ard Biesheuvel Signed-off-by: Jerry Snitselaar Acked-by: Jarkko Sakkinen Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/tpm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/efi/tpm.c b/drivers/firmware/efi/tpm.c index 8f665678e9e3..e8d69bd548f3 100644 --- a/drivers/firmware/efi/tpm.c +++ b/drivers/firmware/efi/tpm.c @@ -97,7 +97,7 @@ int __init efi_tpm_eventlog_init(void) goto out_calc; } - memblock_reserve((unsigned long)final_tbl, + memblock_reserve(efi.tpm_final_log, tbl_size + sizeof(*final_tbl)); efi_tpm_final_log_size = tbl_size; From 161a438d730dade2ba2b1bf8785f0759aba4ca5f Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 20 Oct 2022 10:39:08 +0200 Subject: [PATCH 0086/1823] efi: random: reduce seed size to 32 bytes We no longer need at least 64 bytes of random seed to permit the early crng init to complete. The RNG is now based on Blake2s, so reduce the EFI seed size to the Blake2s hash size, which is sufficient for our purposes. While at it, drop the READ_ONCE(), which was supposed to prevent size from being evaluated after seed was unmapped. However, this cannot actually happen, so READ_ONCE() is unnecessary here. Cc: # v4.14+ Signed-off-by: Ard Biesheuvel Reviewed-by: Jason A. Donenfeld Acked-by: Ilias Apalodimas --- drivers/firmware/efi/efi.c | 2 +- include/linux/efi.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 3ecdc43a3f2b..a46df5d1d094 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -611,7 +611,7 @@ int __init efi_config_parse_tables(const efi_config_table_t *config_tables, seed = early_memremap(efi_rng_seed, sizeof(*seed)); if (seed != NULL) { - size = READ_ONCE(seed->size); + size = min(seed->size, EFI_RANDOM_SEED_SIZE); early_memunmap(seed, sizeof(*seed)); } else { pr_err("Could not map UEFI random seed!\n"); diff --git a/include/linux/efi.h b/include/linux/efi.h index 80f3c1c7827d..929d559ad41d 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -1222,7 +1222,7 @@ efi_status_t efi_random_get_seed(void); arch_efi_call_virt_teardown(); \ }) -#define EFI_RANDOM_SEED_SIZE 64U +#define EFI_RANDOM_SEED_SIZE 32U // BLAKE2S_HASH_SIZE struct linux_efi_random_seed { u32 size; From 7d866e38c7e9ece8a096d0d098fa9d92b9d4f97e Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 20 Oct 2022 10:39:09 +0200 Subject: [PATCH 0087/1823] efi: random: Use 'ACPI reclaim' memory for random seed EFI runtime services data is guaranteed to be preserved by the OS, making it a suitable candidate for the EFI random seed table, which may be passed to kexec kernels as well (after refreshing the seed), and so we need to ensure that the memory is preserved without support from the OS itself. However, runtime services data is intended for allocations that are relevant to the implementations of the runtime services themselves, and so they are unmapped from the kernel linear map, and mapped into the EFI page tables that are active while runtime service invocations are in progress. None of this is needed for the RNG seed. So let's switch to EFI 'ACPI reclaim' memory: in spite of the name, there is nothing exclusively ACPI about it, it is simply a type of allocation that carries firmware provided data which may or may not be relevant to the OS, and it is left up to the OS to decide whether to reclaim it after having consumed its contents. Given that in Linux, we never reclaim these allocations, it is a good choice for the EFI RNG seed, as the allocation is guaranteed to survive kexec reboots. One additional reason for changing this now is to align it with the upcoming recommendation for EFI bootloader provided RNG seeds, which must not use EFI runtime services code/data allocations. Cc: # v4.14+ Signed-off-by: Ard Biesheuvel Reviewed-by: Ilias Apalodimas --- drivers/firmware/efi/libstub/random.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/efi/libstub/random.c b/drivers/firmware/efi/libstub/random.c index 24aa37535372..33ab56769595 100644 --- a/drivers/firmware/efi/libstub/random.c +++ b/drivers/firmware/efi/libstub/random.c @@ -75,7 +75,12 @@ efi_status_t efi_random_get_seed(void) if (status != EFI_SUCCESS) return status; - status = efi_bs_call(allocate_pool, EFI_RUNTIME_SERVICES_DATA, + /* + * Use EFI_ACPI_RECLAIM_MEMORY here so that it is guaranteed that the + * allocation will survive a kexec reboot (although we refresh the seed + * beforehand) + */ + status = efi_bs_call(allocate_pool, EFI_ACPI_RECLAIM_MEMORY, sizeof(*seed) + EFI_RANDOM_SEED_SIZE, (void **)&seed); if (status != EFI_SUCCESS) From b75927cff13e0b3b652a12da7eb9a012911799e8 Mon Sep 17 00:00:00 2001 From: Michael Margolin Date: Thu, 20 Oct 2022 18:19:49 +0300 Subject: [PATCH 0088/1823] RDMA/efa: Add EFA 0xefa2 PCI ID Add support for 0xefa2 devices. Reviewed-by: Firas Jahjah Reviewed-by: Yossi Leybovich Signed-off-by: Michael Margolin Link: https://lore.kernel.org/r/20221020151949.1768-1-mrgolin@amazon.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/efa/efa_main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/efa/efa_main.c b/drivers/infiniband/hw/efa/efa_main.c index 94b94cca4870..15ee92081118 100644 --- a/drivers/infiniband/hw/efa/efa_main.c +++ b/drivers/infiniband/hw/efa/efa_main.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause /* - * Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All rights reserved. + * Copyright 2018-2022 Amazon.com, Inc. or its affiliates. All rights reserved. */ #include @@ -14,10 +14,12 @@ #define PCI_DEV_ID_EFA0_VF 0xefa0 #define PCI_DEV_ID_EFA1_VF 0xefa1 +#define PCI_DEV_ID_EFA2_VF 0xefa2 static const struct pci_device_id efa_pci_tbl[] = { { PCI_VDEVICE(AMAZON, PCI_DEV_ID_EFA0_VF) }, { PCI_VDEVICE(AMAZON, PCI_DEV_ID_EFA1_VF) }, + { PCI_VDEVICE(AMAZON, PCI_DEV_ID_EFA2_VF) }, { } }; From 195583504be28df5d608a4677dd796117aea875f Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 18 Oct 2022 20:35:13 +0200 Subject: [PATCH 0089/1823] spi: stm32: Print summary 'callbacks suppressed' message The original fix "spi: stm32: Rate-limit the 'Communication suspended' message" still leads to "stm32h7_spi_irq_thread: 1696 callbacks suppressed" spew in the kernel log. Since this 'Communication suspended' message is a debug print, add RATELIMIT_MSG_ON_RELEASE flag to inhibit the "callbacks suspended" part during normal operation and only print summary at the end. Fixes: ea8be08cc9358 ("spi: stm32: Rate-limit the 'Communication suspended' message") Signed-off-by: Marek Vasut Link: https://lore.kernel.org/r/20221018183513.206706-1-marex@denx.de Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index 6fe617b445a5..3c2fa2e2f94a 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -886,6 +886,7 @@ static irqreturn_t stm32h7_spi_irq_thread(int irq, void *dev_id) static DEFINE_RATELIMIT_STATE(rs, DEFAULT_RATELIMIT_INTERVAL * 10, 1); + ratelimit_set_flags(&rs, RATELIMIT_MSG_ON_RELEASE); if (__ratelimit(&rs)) dev_dbg_ratelimited(spi->dev, "Communication suspended\n"); if (!spi->cur_usedma && (spi->rx_buf && (spi->rx_len > 0))) From 9e272ed69ad6f6952fafd0599d6993575512408e Mon Sep 17 00:00:00 2001 From: Yangyang Li Date: Mon, 24 Oct 2022 16:38:13 +0800 Subject: [PATCH 0090/1823] RDMA/hns: Disable local invalidate operation When function reset and local invalidate are mixed, HNS RoCEE may hang. Before introducing the cause of the problem, two hardware internal concepts need to be introduced: 1. Execution queue: The queue of hardware execution instructions, function reset and local invalidate are queued for execution in this queue. 2.Local queue: A queue that stores local operation instructions. The instructions in the local queue will be sent to the execution queue for execution. The instructions in the local queue will not be removed until the execution is completed. The reason for the problem is as follows: 1. There is a function reset instruction in the execution queue, which is currently being executed. A necessary condition for the successful execution of function reset is: the hardware pipeline needs to empty the instructions that were not completed before; 2. A local invalidate instruction at the head of the local queue is sent to the execution queue. Now there are two instructions in the execution queue, the first is the function reset instruction, and the second is the local invalidate instruction, which will be executed in se quence; 3. The user has issued many local invalidate operations, causing the local queue to be filled up. 4. The user still has a new local operation command and is queuing to enter the local queue. But the local queue is full and cannot receive new instructions, this instruction is temporarily stored at the hardware pipeline. 5. The function reset has been waiting for the instruction before the hardware pipeline stage is drained. The hardware pipeline stage also caches a local invalidate instruction, so the function reset cannot be completed, and the instructions after it cannot be executed. These factors together cause the execution logic deadlock of the hardware, and the consequence is that RoCEE will not have any response. Considering that the local operation command may potentially cause RoCEE to hang, this feature is no longer supported. Fixes: e93df0108579 ("RDMA/hns: Support local invalidate for hip08 in kernel space") Signed-off-by: Yangyang Li Signed-off-by: Wenpeng Liang Signed-off-by: Haoyue Xu Link: https://lore.kernel.org/r/20221024083814.1089722-2-xuhaoyue1@hisilicon.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 11 ----------- drivers/infiniband/hw/hns/hns_roce_hw_v2.h | 2 -- 2 files changed, 13 deletions(-) diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index 1ead35fb031b..7f5a4769cee0 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -118,7 +118,6 @@ static const u32 hns_roce_op_code[] = { HR_OPC_MAP(ATOMIC_CMP_AND_SWP, ATOM_CMP_AND_SWAP), HR_OPC_MAP(ATOMIC_FETCH_AND_ADD, ATOM_FETCH_AND_ADD), HR_OPC_MAP(SEND_WITH_INV, SEND_WITH_INV), - HR_OPC_MAP(LOCAL_INV, LOCAL_INV), HR_OPC_MAP(MASKED_ATOMIC_CMP_AND_SWP, ATOM_MSK_CMP_AND_SWAP), HR_OPC_MAP(MASKED_ATOMIC_FETCH_AND_ADD, ATOM_MSK_FETCH_AND_ADD), HR_OPC_MAP(REG_MR, FAST_REG_PMR), @@ -559,9 +558,6 @@ static int set_rc_opcode(struct hns_roce_dev *hr_dev, else ret = -EOPNOTSUPP; break; - case IB_WR_LOCAL_INV: - hr_reg_enable(rc_sq_wqe, RC_SEND_WQE_SO); - fallthrough; case IB_WR_SEND_WITH_INV: rc_sq_wqe->inv_key = cpu_to_le32(wr->ex.invalidate_rkey); break; @@ -3222,7 +3218,6 @@ static int hns_roce_v2_write_mtpt(struct hns_roce_dev *hr_dev, hr_reg_write(mpt_entry, MPT_ST, V2_MPT_ST_VALID); hr_reg_write(mpt_entry, MPT_PD, mr->pd); - hr_reg_enable(mpt_entry, MPT_L_INV_EN); hr_reg_write_bool(mpt_entry, MPT_BIND_EN, mr->access & IB_ACCESS_MW_BIND); @@ -3313,7 +3308,6 @@ static int hns_roce_v2_frmr_write_mtpt(struct hns_roce_dev *hr_dev, hr_reg_enable(mpt_entry, MPT_RA_EN); hr_reg_enable(mpt_entry, MPT_R_INV_EN); - hr_reg_enable(mpt_entry, MPT_L_INV_EN); hr_reg_enable(mpt_entry, MPT_FRE); hr_reg_clear(mpt_entry, MPT_MR_MW); @@ -3345,7 +3339,6 @@ static int hns_roce_v2_mw_write_mtpt(void *mb_buf, struct hns_roce_mw *mw) hr_reg_write(mpt_entry, MPT_PD, mw->pdn); hr_reg_enable(mpt_entry, MPT_R_INV_EN); - hr_reg_enable(mpt_entry, MPT_L_INV_EN); hr_reg_enable(mpt_entry, MPT_LW_EN); hr_reg_enable(mpt_entry, MPT_MR_MW); @@ -3794,7 +3787,6 @@ static const u32 wc_send_op_map[] = { HR_WC_OP_MAP(RDMA_READ, RDMA_READ), HR_WC_OP_MAP(RDMA_WRITE, RDMA_WRITE), HR_WC_OP_MAP(RDMA_WRITE_WITH_IMM, RDMA_WRITE), - HR_WC_OP_MAP(LOCAL_INV, LOCAL_INV), HR_WC_OP_MAP(ATOM_CMP_AND_SWAP, COMP_SWAP), HR_WC_OP_MAP(ATOM_FETCH_AND_ADD, FETCH_ADD), HR_WC_OP_MAP(ATOM_MSK_CMP_AND_SWAP, MASKED_COMP_SWAP), @@ -3844,9 +3836,6 @@ static void fill_send_wc(struct ib_wc *wc, struct hns_roce_v2_cqe *cqe) case HNS_ROCE_V2_WQE_OP_RDMA_WRITE_WITH_IMM: wc->wc_flags |= IB_WC_WITH_IMM; break; - case HNS_ROCE_V2_WQE_OP_LOCAL_INV: - wc->wc_flags |= IB_WC_WITH_INVALIDATE; - break; case HNS_ROCE_V2_WQE_OP_ATOM_CMP_AND_SWAP: case HNS_ROCE_V2_WQE_OP_ATOM_FETCH_AND_ADD: case HNS_ROCE_V2_WQE_OP_ATOM_MSK_CMP_AND_SWAP: diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h index b11579027e82..c7bf2d52c1cd 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h @@ -179,7 +179,6 @@ enum { HNS_ROCE_V2_WQE_OP_ATOM_MSK_CMP_AND_SWAP = 0x8, HNS_ROCE_V2_WQE_OP_ATOM_MSK_FETCH_AND_ADD = 0x9, HNS_ROCE_V2_WQE_OP_FAST_REG_PMR = 0xa, - HNS_ROCE_V2_WQE_OP_LOCAL_INV = 0xb, HNS_ROCE_V2_WQE_OP_BIND_MW = 0xc, HNS_ROCE_V2_WQE_OP_MASK = 0x1f, }; @@ -915,7 +914,6 @@ struct hns_roce_v2_rc_send_wqe { #define RC_SEND_WQE_OWNER RC_SEND_WQE_FIELD_LOC(7, 7) #define RC_SEND_WQE_CQE RC_SEND_WQE_FIELD_LOC(8, 8) #define RC_SEND_WQE_FENCE RC_SEND_WQE_FIELD_LOC(9, 9) -#define RC_SEND_WQE_SO RC_SEND_WQE_FIELD_LOC(10, 10) #define RC_SEND_WQE_SE RC_SEND_WQE_FIELD_LOC(11, 11) #define RC_SEND_WQE_INLINE RC_SEND_WQE_FIELD_LOC(12, 12) #define RC_SEND_WQE_WQE_INDEX RC_SEND_WQE_FIELD_LOC(30, 15) From 12bcaf87d8b66d8cd812479c8a6349dcb245375c Mon Sep 17 00:00:00 2001 From: Yixing Liu Date: Mon, 24 Oct 2022 16:38:14 +0800 Subject: [PATCH 0091/1823] RDMA/hns: Fix NULL pointer problem in free_mr_init() Lock grab occurs in a concurrent scenario, resulting in stepping on a NULL pointer. It should be init mutex_init() first before use the lock. Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000 Call trace: __mutex_lock.constprop.0+0xd0/0x5c0 __mutex_lock_slowpath+0x1c/0x2c mutex_lock+0x44/0x50 free_mr_send_cmd_to_hw+0x7c/0x1c0 [hns_roce_hw_v2] hns_roce_v2_dereg_mr+0x30/0x40 [hns_roce_hw_v2] hns_roce_dereg_mr+0x4c/0x130 [hns_roce_hw_v2] ib_dereg_mr_user+0x54/0x124 uverbs_free_mr+0x24/0x30 destroy_hw_idr_uobject+0x38/0x74 uverbs_destroy_uobject+0x48/0x1c4 uobj_destroy+0x74/0xcc ib_uverbs_cmd_verbs+0x368/0xbb0 ib_uverbs_ioctl+0xec/0x1a4 __arm64_sys_ioctl+0xb4/0x100 invoke_syscall+0x50/0x120 el0_svc_common.constprop.0+0x58/0x190 do_el0_svc+0x30/0x90 el0_svc+0x2c/0xb4 el0t_64_sync_handler+0x1a4/0x1b0 el0t_64_sync+0x19c/0x1a0 Fixes: 70f92521584f ("RDMA/hns: Use the reserved loopback QPs to free MR before destroying MPT") Signed-off-by: Yixing Liu Signed-off-by: Haoyue Xu Link: https://lore.kernel.org/r/20221024083814.1089722-3-xuhaoyue1@hisilicon.com Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index 7f5a4769cee0..1435fe2ea176 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -2801,8 +2801,12 @@ static int free_mr_modify_qp(struct hns_roce_dev *hr_dev) static int free_mr_init(struct hns_roce_dev *hr_dev) { + struct hns_roce_v2_priv *priv = hr_dev->priv; + struct hns_roce_v2_free_mr *free_mr = &priv->free_mr; int ret; + mutex_init(&free_mr->mutex); + ret = free_mr_alloc_res(hr_dev); if (ret) return ret; From f15fb2cd979a07fbfc666e2f04b8b30ec9233b2a Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 10 Oct 2022 18:36:06 +0800 Subject: [PATCH 0092/1823] btrfs: raid56: properly handle the error when unable to find the missing stripe In raid56_alloc_missing_rbio(), if we can not determine where the missing device is inside the full stripe, we just BUG_ON(). This is not necessary especially the only caller inside scrub.c is already properly checking the return value, and will treat it as a memory allocation failure. Fix the error handling by: - Add an extra warning for the reason Although personally speaking it may be better to be an ASSERT(). - Properly free the allocated rbio Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/raid56.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c index f6395e8288d6..892005f756cf 100644 --- a/fs/btrfs/raid56.c +++ b/fs/btrfs/raid56.c @@ -2742,8 +2742,10 @@ raid56_alloc_missing_rbio(struct bio *bio, struct btrfs_io_context *bioc) rbio->faila = find_logical_bio_stripe(rbio, bio); if (rbio->faila == -1) { - BUG(); - kfree(rbio); + btrfs_warn_rl(fs_info, + "can not determine the failed stripe number for full stripe %llu", + bioc->raid_map[0]); + __free_raid_bio(rbio); return NULL; } From ab4c54c643a01067669df8332b64e3f31b69e071 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Mon, 10 Oct 2022 18:36:07 +0800 Subject: [PATCH 0093/1823] btrfs: raid56: avoid double freeing for rbio if full_stripe_write() failed Currently if full_stripe_write() failed to allocate the pages for parity, it will call __free_raid_bio() first, then return -ENOMEM. But some caller of full_stripe_write() will also call __free_raid_bio() again, this would cause double freeing. And it's not a logically sound either, normally we should either free the memory at the same level where we allocated it, or let endio to handle everything. So this patch will solve the double freeing by make raid56_parity_write() to handle the error and free the rbio. Just like what we do in raid56_parity_recover(). Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/raid56.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c index 892005f756cf..82c8e991300e 100644 --- a/fs/btrfs/raid56.c +++ b/fs/btrfs/raid56.c @@ -1632,10 +1632,8 @@ static int full_stripe_write(struct btrfs_raid_bio *rbio) int ret; ret = alloc_rbio_parity_pages(rbio); - if (ret) { - __free_raid_bio(rbio); + if (ret) return ret; - } ret = lock_stripe_add(rbio); if (ret == 0) @@ -1823,8 +1821,10 @@ void raid56_parity_write(struct bio *bio, struct btrfs_io_context *bioc) */ if (rbio_is_full(rbio)) { ret = full_stripe_write(rbio); - if (ret) + if (ret) { + __free_raid_bio(rbio); goto fail; + } return; } @@ -1838,8 +1838,10 @@ void raid56_parity_write(struct bio *bio, struct btrfs_io_context *bioc) list_add_tail(&rbio->plug_list, &plug->rbio_list); } else { ret = __raid56_parity_write(rbio); - if (ret) + if (ret) { + __free_raid_bio(rbio); goto fail; + } } return; From ae0e5df4d1a4a2694c9c203cc25334aaaf9f2dfa Mon Sep 17 00:00:00 2001 From: David Sterba Date: Tue, 11 Oct 2022 12:02:31 +0200 Subject: [PATCH 0094/1823] btrfs: reorder btrfs_bio for better packing After changes in commit 917f32a23501 ("btrfs: give struct btrfs_bio a real end_io handler") the layout of btrfs_bio can be improved. There are two holes and the structure size is 264 bytes on release build. By reordering the iterator we can get rid of the holes and the size is 256 bytes which fits to slabs much better. Final layout: struct btrfs_bio { unsigned int mirror_num; /* 0 4 */ struct bvec_iter iter; /* 4 20 */ u64 file_offset; /* 24 8 */ struct btrfs_device * device; /* 32 8 */ u8 * csum; /* 40 8 */ u8 csum_inline[64]; /* 48 64 */ /* --- cacheline 1 boundary (64 bytes) was 48 bytes ago --- */ btrfs_bio_end_io_t end_io; /* 112 8 */ void * private; /* 120 8 */ /* --- cacheline 2 boundary (128 bytes) --- */ struct work_struct end_io_work; /* 128 32 */ struct bio bio; /* 160 96 */ /* size: 256, cachelines: 4, members: 10 */ }; Fixes: 917f32a23501 ("btrfs: give struct btrfs_bio a real end_io handler") Signed-off-by: David Sterba --- fs/btrfs/volumes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 599b9d5af349..f8b668dc8bf8 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -395,6 +395,7 @@ typedef void (*btrfs_bio_end_io_t)(struct btrfs_bio *bbio); */ struct btrfs_bio { unsigned int mirror_num; + struct bvec_iter iter; /* for direct I/O */ u64 file_offset; @@ -403,7 +404,6 @@ struct btrfs_bio { struct btrfs_device *device; u8 *csum; u8 csum_inline[BTRFS_BIO_INLINE_CSUM_SIZE]; - struct bvec_iter iter; /* End I/O information supplied to btrfs_bio_alloc */ btrfs_bio_end_io_t end_io; From 968b71583130b6104c9f33ba60446d598e327a8b Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Fri, 14 Oct 2022 08:52:46 -0400 Subject: [PATCH 0095/1823] btrfs: fix tree mod log mishandling of reallocated nodes We have been seeing the following panic in production kernel BUG at fs/btrfs/tree-mod-log.c:677! invalid opcode: 0000 [#1] SMP RIP: 0010:tree_mod_log_rewind+0x1b4/0x200 RSP: 0000:ffffc9002c02f890 EFLAGS: 00010293 RAX: 0000000000000003 RBX: ffff8882b448c700 RCX: 0000000000000000 RDX: 0000000000008000 RSI: 00000000000000a7 RDI: ffff88877d831c00 RBP: 0000000000000002 R08: 000000000000009f R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000100c40 R12: 0000000000000001 R13: ffff8886c26d6a00 R14: ffff88829f5424f8 R15: ffff88877d831a00 FS: 00007fee1d80c780(0000) GS:ffff8890400c0000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007fee1963a020 CR3: 0000000434f33002 CR4: 00000000007706e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 PKRU: 55555554 Call Trace: btrfs_get_old_root+0x12b/0x420 btrfs_search_old_slot+0x64/0x2f0 ? tree_mod_log_oldest_root+0x3d/0xf0 resolve_indirect_ref+0xfd/0x660 ? ulist_alloc+0x31/0x60 ? kmem_cache_alloc_trace+0x114/0x2c0 find_parent_nodes+0x97a/0x17e0 ? ulist_alloc+0x30/0x60 btrfs_find_all_roots_safe+0x97/0x150 iterate_extent_inodes+0x154/0x370 ? btrfs_search_path_in_tree+0x240/0x240 iterate_inodes_from_logical+0x98/0xd0 ? btrfs_search_path_in_tree+0x240/0x240 btrfs_ioctl_logical_to_ino+0xd9/0x180 btrfs_ioctl+0xe2/0x2ec0 ? __mod_memcg_lruvec_state+0x3d/0x280 ? do_sys_openat2+0x6d/0x140 ? kretprobe_dispatcher+0x47/0x70 ? kretprobe_rethook_handler+0x38/0x50 ? rethook_trampoline_handler+0x82/0x140 ? arch_rethook_trampoline_callback+0x3b/0x50 ? kmem_cache_free+0xfb/0x270 ? do_sys_openat2+0xd5/0x140 __x64_sys_ioctl+0x71/0xb0 do_syscall_64+0x2d/0x40 Which is this code in tree_mod_log_rewind() switch (tm->op) { case BTRFS_MOD_LOG_KEY_REMOVE_WHILE_FREEING: BUG_ON(tm->slot < n); This occurs because we replay the nodes in order that they happened, and when we do a REPLACE we will log a REMOVE_WHILE_FREEING for every slot, starting at 0. 'n' here is the number of items in this block, which in this case was 1, but we had 2 REMOVE_WHILE_FREEING operations. The actual root cause of this was that we were replaying operations for a block that shouldn't have been replayed. Consider the following sequence of events 1. We have an already modified root, and we do a btrfs_get_tree_mod_seq(). 2. We begin removing items from this root, triggering KEY_REPLACE for it's child slots. 3. We remove one of the 2 children this root node points to, thus triggering the root node promotion of the remaining child, and freeing this node. 4. We modify a new root, and re-allocate the above node to the root node of this other root. The tree mod log looks something like this logical 0 op KEY_REPLACE (slot 1) seq 2 logical 0 op KEY_REMOVE (slot 1) seq 3 logical 0 op KEY_REMOVE_WHILE_FREEING (slot 0) seq 4 logical 4096 op LOG_ROOT_REPLACE (old logical 0) seq 5 logical 8192 op KEY_REMOVE_WHILE_FREEING (slot 1) seq 6 logical 8192 op KEY_REMOVE_WHILE_FREEING (slot 0) seq 7 logical 0 op LOG_ROOT_REPLACE (old logical 8192) seq 8 >From here the bug is triggered by the following steps 1. Call btrfs_get_old_root() on the new_root. 2. We call tree_mod_log_oldest_root(btrfs_root_node(new_root)), which is currently logical 0. 3. tree_mod_log_oldest_root() calls tree_mod_log_search_oldest(), which gives us the KEY_REPLACE seq 2, and since that's not a LOG_ROOT_REPLACE we incorrectly believe that we don't have an old root, because we expect that the most recent change should be a LOG_ROOT_REPLACE. 4. Back in tree_mod_log_oldest_root() we don't have a LOG_ROOT_REPLACE, so we don't set old_root, we simply use our existing extent buffer. 5. Since we're using our existing extent buffer (logical 0) we call tree_mod_log_search(0) in order to get the newest change to start the rewind from, which ends up being the LOG_ROOT_REPLACE at seq 8. 6. Again since we didn't find an old_root we simply clone logical 0 at it's current state. 7. We call tree_mod_log_rewind() with the cloned extent buffer. 8. Set n = btrfs_header_nritems(logical 0), which would be whatever the original nritems was when we COWed the original root, say for this example it's 2. 9. We start from the newest operation and work our way forward, so we see LOG_ROOT_REPLACE which we ignore. 10. Next we see KEY_REMOVE_WHILE_FREEING for slot 0, which triggers the BUG_ON(tm->slot < n), because it expects if we've done this we have a completely empty extent buffer to replay completely. The correct thing would be to find the first LOG_ROOT_REPLACE, and then get the old_root set to logical 8192. In fact making that change fixes this particular problem. However consider the much more complicated case. We have a child node in this tree and the above situation. In the above case we freed one of the child blocks at the seq 3 operation. If this block was also re-allocated and got new tree mod log operations we would have a different problem. btrfs_search_old_slot(orig root) would get down to the logical 0 root that still pointed at that node. However in btrfs_search_old_slot() we call tree_mod_log_rewind(buf) directly. This is not context aware enough to know which operations we should be replaying. If the block was re-allocated multiple times we may only want to replay a range of operations, and determining what that range is isn't possible to determine. We could maybe solve this by keeping track of which root the node belonged to at every tree mod log operation, and then passing this around to make sure we're only replaying operations that relate to the root we're trying to rewind. However there's a simpler way to solve this problem, simply disallow reallocations if we have currently running tree mod log users. We already do this for leaf's, so we're simply expanding this to nodes as well. This is a relatively uncommon occurrence, and the problem is complicated enough I'm worried that we will still have corner cases in the reallocation case. So fix this in the most straightforward way possible. Fixes: bd989ba359f2 ("Btrfs: add tree modification log functions") CC: stable@vger.kernel.org # 3.3+ Reviewed-by: Filipe Manana Signed-off-by: Josef Bacik Signed-off-by: David Sterba --- fs/btrfs/extent-tree.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index cd2d36580f1a..2801c991814f 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -3295,21 +3295,22 @@ void btrfs_free_tree_block(struct btrfs_trans_handle *trans, } /* - * If this is a leaf and there are tree mod log users, we may - * have recorded mod log operations that point to this leaf. - * So we must make sure no one reuses this leaf's extent before - * mod log operations are applied to a node, otherwise after - * rewinding a node using the mod log operations we get an - * inconsistent btree, as the leaf's extent may now be used as - * a node or leaf for another different btree. + * If there are tree mod log users we may have recorded mod log + * operations for this node. If we re-allocate this node we + * could replay operations on this node that happened when it + * existed in a completely different root. For example if it + * was part of root A, then was reallocated to root B, and we + * are doing a btrfs_old_search_slot(root b), we could replay + * operations that happened when the block was part of root A, + * giving us an inconsistent view of the btree. + * * We are safe from races here because at this point no other * node or root points to this extent buffer, so if after this - * check a new tree mod log user joins, it will not be able to - * find a node pointing to this leaf and record operations that - * point to this leaf. + * check a new tree mod log user joins we will not have an + * existing log of operations on this node that we have to + * contend with. */ - if (btrfs_header_level(buf) == 0 && - test_bit(BTRFS_FS_TREE_MOD_LOG_USERS, &fs_info->flags)) + if (test_bit(BTRFS_FS_TREE_MOD_LOG_USERS, &fs_info->flags)) must_pin = true; if (must_pin || btrfs_is_zoned(fs_info)) { From 3d17adea74a56a4965f7a603d8ed8c66bb9356d9 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Tue, 18 Oct 2022 09:56:38 +0800 Subject: [PATCH 0096/1823] btrfs: make thaw time super block check to also verify checksum Previous commit a05d3c915314 ("btrfs: check superblock to ensure the fs was not modified at thaw time") only checks the content of the super block, but it doesn't really check if the on-disk super block has a matching checksum. This patch will add the checksum verification to thaw time superblock verification. This involves the following extra changes: - Export btrfs_check_super_csum() As we need to call it in super.c. - Change the argument list of btrfs_check_super_csum() Instead of passing a char *, directly pass struct btrfs_super_block * pointer. - Verify that our checksum type didn't change before checking the checksum value, like it's done at mount time Fixes: a05d3c915314 ("btrfs: check superblock to ensure the fs was not modified at thaw time") Reviewed-by: Johannes Thumshirn Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/disk-io.c | 10 ++++------ fs/btrfs/disk-io.h | 2 ++ fs/btrfs/super.c | 16 ++++++++++++++++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index a2da9313c694..4b28263c3d32 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -166,11 +166,9 @@ static bool btrfs_supported_super_csum(u16 csum_type) * Return 0 if the superblock checksum type matches the checksum value of that * algorithm. Pass the raw disk superblock data. */ -static int btrfs_check_super_csum(struct btrfs_fs_info *fs_info, - char *raw_disk_sb) +int btrfs_check_super_csum(struct btrfs_fs_info *fs_info, + const struct btrfs_super_block *disk_sb) { - struct btrfs_super_block *disk_sb = - (struct btrfs_super_block *)raw_disk_sb; char result[BTRFS_CSUM_SIZE]; SHASH_DESC_ON_STACK(shash, fs_info->csum_shash); @@ -181,7 +179,7 @@ static int btrfs_check_super_csum(struct btrfs_fs_info *fs_info, * BTRFS_SUPER_INFO_SIZE range, we expect that the unused space is * filled with zeros and is included in the checksum. */ - crypto_shash_digest(shash, raw_disk_sb + BTRFS_CSUM_SIZE, + crypto_shash_digest(shash, (const u8 *)disk_sb + BTRFS_CSUM_SIZE, BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE, result); if (memcmp(disk_sb->csum, result, fs_info->csum_size)) @@ -3479,7 +3477,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device * We want to check superblock checksum, the type is stored inside. * Pass the whole disk block of size BTRFS_SUPER_INFO_SIZE (4k). */ - if (btrfs_check_super_csum(fs_info, (u8 *)disk_super)) { + if (btrfs_check_super_csum(fs_info, disk_super)) { btrfs_err(fs_info, "superblock checksum mismatch"); err = -EINVAL; btrfs_release_disk_super(disk_super); diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h index c67c15d4d20b..9fa923e005a3 100644 --- a/fs/btrfs/disk-io.h +++ b/fs/btrfs/disk-io.h @@ -42,6 +42,8 @@ struct extent_buffer *btrfs_find_create_tree_block( void btrfs_clean_tree_block(struct extent_buffer *buf); void btrfs_clear_oneshot_options(struct btrfs_fs_info *fs_info); int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info); +int btrfs_check_super_csum(struct btrfs_fs_info *fs_info, + const struct btrfs_super_block *disk_sb); int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_devices, char *options); diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 9be4fd2db0f4..5942b9384088 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -2555,6 +2555,7 @@ static int check_dev_super(struct btrfs_device *dev) { struct btrfs_fs_info *fs_info = dev->fs_info; struct btrfs_super_block *sb; + u16 csum_type; int ret = 0; /* This should be called with fs still frozen. */ @@ -2569,6 +2570,21 @@ static int check_dev_super(struct btrfs_device *dev) if (IS_ERR(sb)) return PTR_ERR(sb); + /* Verify the checksum. */ + csum_type = btrfs_super_csum_type(sb); + if (csum_type != btrfs_super_csum_type(fs_info->super_copy)) { + btrfs_err(fs_info, "csum type changed, has %u expect %u", + csum_type, btrfs_super_csum_type(fs_info->super_copy)); + ret = -EUCLEAN; + goto out; + } + + if (btrfs_check_super_csum(fs_info, sb)) { + btrfs_err(fs_info, "csum for on-disk super block no longer matches"); + ret = -EUCLEAN; + goto out; + } + /* Btrfs_validate_super() includes fsid check against super->fsid. */ ret = btrfs_validate_super(fs_info, sb, 0); if (ret < 0) From 9b8be45f1ef29081c4b614aa559f934526e70d16 Mon Sep 17 00:00:00 2001 From: BingJing Chang Date: Sun, 16 Oct 2022 23:33:46 +0800 Subject: [PATCH 0097/1823] btrfs: send: fix send failure of a subcase of orphan inodes Commit 9ed0a72e5b35 ("btrfs: send: fix failures when processing inodes with no links") tries to fix all incremental send cases of orphan inodes the send operation will meet. However, there's still a bug causing the corner subcase fails with a ENOENT error. Here's shortened steps of that subcase: $ btrfs subvolume create vol $ touch vol/foo $ btrfs subvolume snapshot -r vol snap1 $ btrfs subvolume snapshot -r vol snap2 # Turn the second snapshot to RW mode and delete the file while # holding an open file descriptor on it $ btrfs property set snap2 ro false $ exec 73 /dev/null At subvol snap1 ERROR: send ioctl failed with -2: No such file or directory It's subcase 3 of BTRFS_COMPARE_TREE_CHANGED in the commit 9ed0a72e5b35 ("btrfs: send: fix failures when processing inodes with no links"). And it's not a common case. We still have not met it in the real world. Theoretically, this case can happen in a batch cascading snapshot backup. In cascading backups, the receive operation in the middle may cause orphan inodes to appear because of the open file descriptors on the snapshot files during receiving. And if we don't do the batch snapshot backups in their creation order, then we can have an inode, which is an orphan in the parent snapshot but refers to a file in the send snapshot. Since an orphan inode has no paths, the send operation will fail with a ENOENT error if it tries to generate a path for it. In that patch, this subcase will be treated as an inode with a new generation. However, when the routine tries to delete the old paths in the parent snapshot, the function process_all_refs() doesn't check whether there are paths recorded or not before it calls the function process_recorded_refs(). And the function process_recorded_refs() try to get the first path in the parent snapshot in the beginning. Since it has no paths in the parent snapshot, the send operation fails. To fix this, we can easily put a link count check to avoid entering the deletion routine like what we do a link count check to avoid creating a new one. Moreover, we can assume that the function process_all_refs() can always collect references to process because we know it has a positive link count. Fixes: 9ed0a72e5b35 ("btrfs: send: fix failures when processing inodes with no links") Reviewed-by: Filipe Manana Signed-off-by: BingJing Chang Signed-off-by: David Sterba --- fs/btrfs/send.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index ec6e1752af2c..145c84b44fd0 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -6668,17 +6668,19 @@ static int changed_inode(struct send_ctx *sctx, /* * First, process the inode as if it was deleted. */ - sctx->cur_inode_gen = right_gen; - sctx->cur_inode_new = false; - sctx->cur_inode_deleted = true; - sctx->cur_inode_size = btrfs_inode_size( - sctx->right_path->nodes[0], right_ii); - sctx->cur_inode_mode = btrfs_inode_mode( - sctx->right_path->nodes[0], right_ii); - ret = process_all_refs(sctx, - BTRFS_COMPARE_TREE_DELETED); - if (ret < 0) - goto out; + if (old_nlinks > 0) { + sctx->cur_inode_gen = right_gen; + sctx->cur_inode_new = false; + sctx->cur_inode_deleted = true; + sctx->cur_inode_size = btrfs_inode_size( + sctx->right_path->nodes[0], right_ii); + sctx->cur_inode_mode = btrfs_inode_mode( + sctx->right_path->nodes[0], right_ii); + ret = process_all_refs(sctx, + BTRFS_COMPARE_TREE_DELETED); + if (ret < 0) + goto out; + } /* * Now process the inode as if it was new. From 2398091f9c2c8e0040f4f9928666787a3e8108a7 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Tue, 18 Oct 2022 16:05:52 +0200 Subject: [PATCH 0098/1823] btrfs: fix type of parameter generation in btrfs_get_dentry The type of parameter generation has been u32 since the beginning, however all callers pass a u64 generation, so unify the types to prevent potential loss. CC: stable@vger.kernel.org # 4.9+ Reviewed-by: Josef Bacik Signed-off-by: David Sterba --- fs/btrfs/export.c | 2 +- fs/btrfs/export.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/export.c b/fs/btrfs/export.c index 1d4c2397d0d6..fab7eb76e53b 100644 --- a/fs/btrfs/export.c +++ b/fs/btrfs/export.c @@ -58,7 +58,7 @@ static int btrfs_encode_fh(struct inode *inode, u32 *fh, int *max_len, } struct dentry *btrfs_get_dentry(struct super_block *sb, u64 objectid, - u64 root_objectid, u32 generation, + u64 root_objectid, u64 generation, int check_generation) { struct btrfs_fs_info *fs_info = btrfs_sb(sb); diff --git a/fs/btrfs/export.h b/fs/btrfs/export.h index f32f4113c976..5afb7ca42828 100644 --- a/fs/btrfs/export.h +++ b/fs/btrfs/export.h @@ -19,7 +19,7 @@ struct btrfs_fid { } __attribute__ ((packed)); struct dentry *btrfs_get_dentry(struct super_block *sb, u64 objectid, - u64 root_objectid, u32 generation, + u64 root_objectid, u64 generation, int check_generation); struct dentry *btrfs_get_parent(struct dentry *child); From 9440c42941606af4c379afa3cf8624f0dc43a629 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 18 Oct 2022 14:27:08 +0200 Subject: [PATCH 0099/1823] x86/syscall: Include asm/ptrace.h in syscall_wrapper header With just the forward declaration of the 'struct pt_regs' in syscall_wrapper.h, the syscall stub functions: __[x64|ia32]_sys_*(struct pt_regs *regs) will have different definition of 'regs' argument in BTF data based on which object file they are defined in. If the syscall's object includes 'struct pt_regs' definition, the BTF argument data will point to a 'struct pt_regs' record, like: [226] STRUCT 'pt_regs' size=168 vlen=21 'r15' type_id=1 bits_offset=0 'r14' type_id=1 bits_offset=64 'r13' type_id=1 bits_offset=128 ... If not, it will point to a fwd declaration record: [15439] FWD 'pt_regs' fwd_kind=struct and make bpf tracing program hooking on those functions unable to access fields from 'struct pt_regs'. Include asm/ptrace.h directly in syscall_wrapper.h to make sure all syscalls see 'struct pt_regs' definition. This then results in BTF for '__*_sys_*(struct pt_regs *regs)' functions to point to the actual struct, not just the forward declaration. [ bp: No Fixes tag as this is not really a bug fix but "adjustment" so that BTF is happy. ] Reported-by: Akihiro HARAI Signed-off-by: Jiri Olsa Signed-off-by: Borislav Petkov Acked-by: Andrii Nakryiko Cc: # this is needed only for BTF so kernels >= 5.15 Link: https://lore.kernel.org/r/20221018122708.823792-1-jolsa@kernel.org --- arch/x86/include/asm/syscall_wrapper.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/include/asm/syscall_wrapper.h b/arch/x86/include/asm/syscall_wrapper.h index 59358d1bf880..fd2669b1cb2d 100644 --- a/arch/x86/include/asm/syscall_wrapper.h +++ b/arch/x86/include/asm/syscall_wrapper.h @@ -6,7 +6,7 @@ #ifndef _ASM_X86_SYSCALL_WRAPPER_H #define _ASM_X86_SYSCALL_WRAPPER_H -struct pt_regs; +#include extern long __x64_sys_ni_syscall(const struct pt_regs *regs); extern long __ia32_sys_ni_syscall(const struct pt_regs *regs); From 2b4337c8409b4e9e5aed15c597e4031dd567bdd8 Mon Sep 17 00:00:00 2001 From: Mihai Sain Date: Mon, 17 Oct 2022 11:31:19 +0300 Subject: [PATCH 0100/1823] ARM: dts: at91: sama7g5: fix signal name of pin PB2 The signal name of pin PB2 with function F is FLEXCOM11_IO1 as it is defined in the datasheet. Fixes: 7540629e2fc7 ("ARM: dts: at91: add sama7g5 SoC DT and sama7g5-ek") Signed-off-by: Mihai Sain Reviewed-by: Tudor Ambarus Acked-by: Nicolas Ferre Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20221017083119.1643-1-mihai.sain@microchip.com --- arch/arm/boot/dts/sama7g5-pinfunc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/sama7g5-pinfunc.h b/arch/arm/boot/dts/sama7g5-pinfunc.h index 4eb30445d205..6e87f0d4b8fc 100644 --- a/arch/arm/boot/dts/sama7g5-pinfunc.h +++ b/arch/arm/boot/dts/sama7g5-pinfunc.h @@ -261,7 +261,7 @@ #define PIN_PB2__FLEXCOM6_IO0 PINMUX_PIN(PIN_PB2, 2, 1) #define PIN_PB2__ADTRG PINMUX_PIN(PIN_PB2, 3, 1) #define PIN_PB2__A20 PINMUX_PIN(PIN_PB2, 4, 1) -#define PIN_PB2__FLEXCOM11_IO0 PINMUX_PIN(PIN_PB2, 6, 3) +#define PIN_PB2__FLEXCOM11_IO1 PINMUX_PIN(PIN_PB2, 6, 3) #define PIN_PB3 35 #define PIN_PB3__GPIO PINMUX_PIN(PIN_PB3, 0, 0) #define PIN_PB3__RF1 PINMUX_PIN(PIN_PB3, 1, 1) From ea522496afa1dd4ed295466e9c813b88ebda3284 Mon Sep 17 00:00:00 2001 From: Nick Desaulniers Date: Fri, 14 Oct 2022 10:10:40 -0700 Subject: [PATCH 0101/1823] Documentation: process: replace outdated LTS table w/ link The existing table was a bit outdated. 3.16 was EOL in 2020. 4.4 was EOL in 2022. 5.10 is new in 2020. 5.15 is new in 2021. We'll see if 6.1 becomes LTS in 2022. Rather than keep this table updated, it does duplicate information from multiple kernel.org pages. Make one less duplication site that needs to be updated and simply refer to the kernel.org page on releases. Suggested-by: Tyler Hicks Suggested-by: Bagas Sanjaya Signed-off-by: Nick Desaulniers Signed-off-by: Greg Kroah-Hartman Link: https://lore.kernel.org/stable/20221014171040.849726-1-ndesaulniers%40google.com Reviewed-by: Tyler Hicks (Microsoft) Link: https://lore.kernel.org/r/20221014171040.849726-1-ndesaulniers@google.com Signed-off-by: Jonathan Corbet --- Documentation/process/2.Process.rst | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/Documentation/process/2.Process.rst b/Documentation/process/2.Process.rst index e05fb1b8f8b6..6a919cffcbfd 100644 --- a/Documentation/process/2.Process.rst +++ b/Documentation/process/2.Process.rst @@ -126,17 +126,10 @@ than one development cycle past their initial release. So, for example, the 5.2.21 was the final stable update of the 5.2 release. Some kernels are designated "long term" kernels; they will receive support -for a longer period. As of this writing, the current long term kernels -and their maintainers are: +for a longer period. Please refer to the following link for the list of active +long term kernel versions and their maintainers: - ====== ================================ ======================= - 3.16 Ben Hutchings (very long-term kernel) - 4.4 Greg Kroah-Hartman & Sasha Levin (very long-term kernel) - 4.9 Greg Kroah-Hartman & Sasha Levin - 4.14 Greg Kroah-Hartman & Sasha Levin - 4.19 Greg Kroah-Hartman & Sasha Levin - 5.4 Greg Kroah-Hartman & Sasha Levin - ====== ================================ ======================= + https://www.kernel.org/category/releases.html The selection of a kernel for long-term support is purely a matter of a maintainer having the need and the time to maintain that release. There From e648174b53f1e29ee72ef33756a97ffb8241b6a5 Mon Sep 17 00:00:00 2001 From: Mushahid Hussain Date: Mon, 17 Oct 2022 16:20:26 +0500 Subject: [PATCH 0102/1823] Documentation: Fix spelling mistake in hacking.rst Fix `botton half locks` to `bottom half locks`. Signed-off-by: Mushahid Hussain Link: https://lore.kernel.org/r/20221017112026.88324-1-mushi.shar@gmail.com Signed-off-by: Jonathan Corbet --- Documentation/kernel-hacking/hacking.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/kernel-hacking/hacking.rst b/Documentation/kernel-hacking/hacking.rst index 9a1f020c8449..1717348a4404 100644 --- a/Documentation/kernel-hacking/hacking.rst +++ b/Documentation/kernel-hacking/hacking.rst @@ -120,7 +120,7 @@ You can tell you are in a softirq (or tasklet) using the .. warning:: Beware that this will return a false positive if a - :ref:`botton half lock ` is held. + :ref:`bottom half lock ` is held. Some Basic Rules ================ From 2f3f53d62307262f0086804ea7cea99b0e085450 Mon Sep 17 00:00:00 2001 From: Akira Yokosawa Date: Sat, 15 Oct 2022 18:22:01 +0900 Subject: [PATCH 0103/1823] docs/process/howto: Replace C89 with C11 Commit e8c07082a810 ("Kbuild: move to -std=gnu11") updated process/programming-language.rst, but failed to update process/howto.rst. Update howto.rst and resolve the inconsistency. Fixes: e8c07082a810 ("Kbuild: move to -std=gnu11") Signed-off-by: Akira Yokosawa Cc: Arnd Bergmann Cc: Federico Vaga Cc: Alex Shi Cc: Hu Haowen Cc: Tsugikazu Shibata Link: https://lore.kernel.org/r/20221015092201.32099-1-akiyks@gmail.com Signed-off-by: Jonathan Corbet --- Documentation/process/howto.rst | 2 +- Documentation/translations/it_IT/process/howto.rst | 2 +- Documentation/translations/ja_JP/howto.rst | 2 +- Documentation/translations/ko_KR/howto.rst | 2 +- Documentation/translations/zh_CN/process/howto.rst | 2 +- Documentation/translations/zh_TW/process/howto.rst | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Documentation/process/howto.rst b/Documentation/process/howto.rst index bd15c393ba3c..cb6abcb2b6d0 100644 --- a/Documentation/process/howto.rst +++ b/Documentation/process/howto.rst @@ -36,7 +36,7 @@ experience, the following books are good for, if anything, reference: - "C: A Reference Manual" by Harbison and Steele [Prentice Hall] The kernel is written using GNU C and the GNU toolchain. While it -adheres to the ISO C89 standard, it uses a number of extensions that are +adheres to the ISO C11 standard, it uses a number of extensions that are not featured in the standard. The kernel is a freestanding C environment, with no reliance on the standard C library, so some portions of the C standard are not supported. Arbitrary long long diff --git a/Documentation/translations/it_IT/process/howto.rst b/Documentation/translations/it_IT/process/howto.rst index 15c08aea1dfe..052f1b3610cb 100644 --- a/Documentation/translations/it_IT/process/howto.rst +++ b/Documentation/translations/it_IT/process/howto.rst @@ -44,7 +44,7 @@ altro, utili riferimenti: - "C: A Reference Manual" di Harbison and Steele [Prentice Hall] Il kernel è stato scritto usando GNU C e la toolchain GNU. -Sebbene si attenga allo standard ISO C89, esso utilizza una serie di +Sebbene si attenga allo standard ISO C11, esso utilizza una serie di estensioni che non sono previste in questo standard. Il kernel è un ambiente C indipendente, che non ha alcuna dipendenza dalle librerie C standard, così alcune parti del C standard non sono supportate. diff --git a/Documentation/translations/ja_JP/howto.rst b/Documentation/translations/ja_JP/howto.rst index b47a682d8ded..b8eeb45a02d4 100644 --- a/Documentation/translations/ja_JP/howto.rst +++ b/Documentation/translations/ja_JP/howto.rst @@ -65,7 +65,7 @@ Linux カーネル開発のやり方 - 『新・詳説 C 言語 H&S リファレンス』 (サミュエル P ハービソン/ガイ L スティール共著 斉藤 信男監訳)[ソフトバンク] カーネルは GNU C と GNU ツールチェインを使って書かれています。カーネル -は ISO C89 仕様に準拠して書く一方で、標準には無い言語拡張を多く使って +は ISO C11 仕様に準拠して書く一方で、標準には無い言語拡張を多く使って います。カーネルは標準 C ライブラリに依存しない、C 言語非依存環境です。 そのため、C の標準の中で使えないものもあります。特に任意の long long の除算や浮動小数点は使えません。カーネルがツールチェインや C 言語拡張 diff --git a/Documentation/translations/ko_KR/howto.rst b/Documentation/translations/ko_KR/howto.rst index df53fafd1b10..969e91a95bb0 100644 --- a/Documentation/translations/ko_KR/howto.rst +++ b/Documentation/translations/ko_KR/howto.rst @@ -62,7 +62,7 @@ Documentation/process/howto.rst - "Practical C Programming" by Steve Oualline [O'Reilly] - "C: A Reference Manual" by Harbison and Steele [Prentice Hall] -커널은 GNU C와 GNU 툴체인을 사용하여 작성되었다. 이 툴들은 ISO C89 표준을 +커널은 GNU C와 GNU 툴체인을 사용하여 작성되었다. 이 툴들은 ISO C11 표준을 따르는 반면 표준에 있지 않은 많은 확장기능도 가지고 있다. 커널은 표준 C 라이브러리와는 관계없이 freestanding C 환경이어서 C 표준의 일부는 지원되지 않는다. 임의의 long long 나누기나 floating point는 지원되지 않는다. diff --git a/Documentation/translations/zh_CN/process/howto.rst b/Documentation/translations/zh_CN/process/howto.rst index 5bf953146929..888978a62db3 100644 --- a/Documentation/translations/zh_CN/process/howto.rst +++ b/Documentation/translations/zh_CN/process/howto.rst @@ -45,7 +45,7 @@ Linux内核大部分是由C语言写成的,一些体系结构相关的代码 - "C: A Reference Manual" by Harbison and Steele [Prentice Hall] 《C语言参考手册(原书第5版)》(邱仲潘 等译)[机械工业出版社] -Linux内核使用GNU C和GNU工具链开发。虽然它遵循ISO C89标准,但也用到了一些 +Linux内核使用GNU C和GNU工具链开发。虽然它遵循ISO C11标准,但也用到了一些 标准中没有定义的扩展。内核是自给自足的C环境,不依赖于标准C库的支持,所以 并不支持C标准中的部分定义。比如long long类型的大数除法和浮点运算就不允许 使用。有时候确实很难弄清楚内核对工具链的要求和它所使用的扩展,不幸的是目 diff --git a/Documentation/translations/zh_TW/process/howto.rst b/Documentation/translations/zh_TW/process/howto.rst index 86b0d4c6d6f9..8fb8edcaee66 100644 --- a/Documentation/translations/zh_TW/process/howto.rst +++ b/Documentation/translations/zh_TW/process/howto.rst @@ -48,7 +48,7 @@ Linux內核大部分是由C語言寫成的,一些體系結構相關的代碼 - "C: A Reference Manual" by Harbison and Steele [Prentice Hall] 《C語言參考手冊(原書第5版)》(邱仲潘 等譯)[機械工業出版社] -Linux內核使用GNU C和GNU工具鏈開發。雖然它遵循ISO C89標準,但也用到了一些 +Linux內核使用GNU C和GNU工具鏈開發。雖然它遵循ISO C11標準,但也用到了一些 標準中沒有定義的擴展。內核是自給自足的C環境,不依賴於標準C庫的支持,所以 並不支持C標準中的部分定義。比如long long類型的大數除法和浮點運算就不允許 使用。有時候確實很難弄清楚內核對工具鏈的要求和它所使用的擴展,不幸的是目 From efa17e90e1711bdb084e3954fa44afb6647331c0 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Sat, 22 Oct 2022 15:42:12 +0800 Subject: [PATCH 0104/1823] iio: trigger: sysfs: fix possible memory leak in iio_sysfs_trig_init() dev_set_name() allocates memory for name, it need be freed when device_add() fails, call put_device() to give up the reference that hold in device_initialize(), so that it can be freed in kobject_cleanup() when the refcount hit to 0. Fault injection test can trigger this: unreferenced object 0xffff8e8340a7b4c0 (size 32): comm "modprobe", pid 243, jiffies 4294678145 (age 48.845s) hex dump (first 32 bytes): 69 69 6f 5f 73 79 73 66 73 5f 74 72 69 67 67 65 iio_sysfs_trigge 72 00 a7 40 83 8e ff ff 00 86 13 c4 f6 ee ff ff r..@............ backtrace: [<0000000074999de8>] __kmem_cache_alloc_node+0x1e9/0x360 [<00000000497fd30b>] __kmalloc_node_track_caller+0x44/0x1a0 [<000000003636c520>] kstrdup+0x2d/0x60 [<0000000032f84da2>] kobject_set_name_vargs+0x1e/0x90 [<0000000092efe493>] dev_set_name+0x4e/0x70 Fixes: 1f785681a870 ("staging:iio:trigger sysfs userspace trigger rework.") Signed-off-by: Yang Yingliang Cc: Link: https://lore.kernel.org/r/20221022074212.1386424-1-yangyingliang@huawei.com Signed-off-by: Jonathan Cameron --- drivers/iio/trigger/iio-trig-sysfs.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/iio/trigger/iio-trig-sysfs.c b/drivers/iio/trigger/iio-trig-sysfs.c index d6c5e9644738..6b05eed41612 100644 --- a/drivers/iio/trigger/iio-trig-sysfs.c +++ b/drivers/iio/trigger/iio-trig-sysfs.c @@ -203,9 +203,13 @@ static int iio_sysfs_trigger_remove(int id) static int __init iio_sysfs_trig_init(void) { + int ret; device_initialize(&iio_sysfs_trig_dev); dev_set_name(&iio_sysfs_trig_dev, "iio_sysfs_trigger"); - return device_add(&iio_sysfs_trig_dev); + ret = device_add(&iio_sysfs_trig_dev); + if (ret) + put_device(&iio_sysfs_trig_dev); + return ret; } module_init(iio_sysfs_trig_init); From 207777dc306a732cff76ab63bf19a7e0851410b3 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Thu, 20 Oct 2022 13:27:05 +0300 Subject: [PATCH 0105/1823] iio: adc: at91-sama5d2_adc: get rid of 5 degrees Celsius adjustment On SAMA7G5 final chip version there is no need for 5 degrees Celsius adjustment when computing junction temperature, thus, remove it. Fixes: 5ab38b81895c ("iio: adc: at91-sama5d2_adc: add support for temperature sensor") Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/20221020102705.3639204-1-claudiu.beznea@microchip.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index 33e251552214..870f4cb60923 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -2307,11 +2307,9 @@ static int at91_adc_temp_sensor_init(struct at91_adc_state *st, clb->p6 = buf[AT91_ADC_TS_CLB_IDX_P6]; /* - * We prepare here the conversion to milli and also add constant - * factor (5 degrees Celsius) to p1 here to avoid doing it on - * hotpath. + * We prepare here the conversion to milli to avoid doing it on hotpath. */ - clb->p1 = clb->p1 * 1000 + 5000; + clb->p1 = clb->p1 * 1000; free_buf: kfree(buf); From 30bbe38b28fbc7415e0585f2b05e6f6f95febbaa Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Mon, 24 Oct 2022 15:22:23 +0200 Subject: [PATCH 0106/1823] MAINTAINERS: adjust entry after renaming parisc serial driver Commit 9e4e2ce1a78e ("parisc/serial: Rename 8250_gsc.c to 8250_parisc.c") renames the parisc serial driver file, but does not adjust the entry in MAINTAINERS. Hence, ./scripts/get_maintainer.pl --self-test=patterns complains about a broken reference. Repair this file reference in PARISC ARCHITECTURE. Signed-off-by: Lukas Bulwahn Fixes: 9e4e2ce1a78e ("parisc/serial: Rename 8250_gsc.c to 8250_parisc.c") Signed-off-by: Helge Deller --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index cf0f18502372..107359c46f6c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15617,7 +15617,7 @@ F: drivers/input/serio/gscps2.c F: drivers/input/serio/hp_sdc* F: drivers/parisc/ F: drivers/parport/parport_gsc.* -F: drivers/tty/serial/8250/8250_gsc.c +F: drivers/tty/serial/8250/8250_parisc.c F: drivers/video/console/sti* F: drivers/video/fbdev/sti* F: drivers/video/logo/logo_parisc* From b5f9a01fae42684648c2ee3cd9985f80c67ab9f7 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Thu, 13 Oct 2022 12:03:33 +0800 Subject: [PATCH 0107/1823] RDMA/rxe: Fix mr leak in RESPST_ERR_RNR rxe_recheck_mr() will increase mr's ref_cnt, so we should call rxe_put(mr) to drop mr's ref_cnt in RESPST_ERR_RNR to avoid below warning: WARNING: CPU: 0 PID: 4156 at drivers/infiniband/sw/rxe/rxe_pool.c:259 __rxe_cleanup+0x1df/0x240 [rdma_rxe] ... Call Trace: rxe_dereg_mr+0x4c/0x60 [rdma_rxe] ib_dereg_mr_user+0xa8/0x200 [ib_core] ib_mr_pool_destroy+0x77/0xb0 [ib_core] nvme_rdma_destroy_queue_ib+0x89/0x240 [nvme_rdma] nvme_rdma_free_queue+0x40/0x50 [nvme_rdma] nvme_rdma_teardown_io_queues.part.0+0xc3/0x120 [nvme_rdma] nvme_rdma_error_recovery_work+0x4d/0xf0 [nvme_rdma] process_one_work+0x582/0xa40 ? pwq_dec_nr_in_flight+0x100/0x100 ? rwlock_bug.part.0+0x60/0x60 worker_thread+0x2a9/0x700 ? process_one_work+0xa40/0xa40 kthread+0x168/0x1a0 ? kthread_complete_and_exit+0x20/0x20 ret_from_fork+0x22/0x30 Link: https://lore.kernel.org/r/20221024052049.20577-1-lizhijian@fujitsu.com Fixes: 8a1a0be894da ("RDMA/rxe: Replace mr by rkey in responder resources") Signed-off-by: Li Zhijian Signed-off-by: Jason Gunthorpe Signed-off-by: Leon Romanovsky --- drivers/infiniband/sw/rxe/rxe_resp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/sw/rxe/rxe_resp.c b/drivers/infiniband/sw/rxe/rxe_resp.c index ed5a09e86417..693081e813ec 100644 --- a/drivers/infiniband/sw/rxe/rxe_resp.c +++ b/drivers/infiniband/sw/rxe/rxe_resp.c @@ -806,8 +806,10 @@ static enum resp_states read_reply(struct rxe_qp *qp, skb = prepare_ack_packet(qp, &ack_pkt, opcode, payload, res->cur_psn, AETH_ACK_UNLIMITED); - if (!skb) + if (!skb) { + rxe_put(mr); return RESPST_ERR_RNR; + } rxe_mr_copy(mr, res->read.va, payload_addr(&ack_pkt), payload, RXE_FROM_MR_OBJ); From 76a66ba101329316a5d7f4275070be22eb85fdf2 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Fri, 21 Oct 2022 08:43:45 +0800 Subject: [PATCH 0108/1823] btrfs: don't use btrfs_chunk::sub_stripes from disk [BUG] There are two reports (the earliest one from LKP, a more recent one from kernel bugzilla) that we can have some chunks with 0 as sub_stripes. This will cause divide-by-zero errors at btrfs_rmap_block, which is introduced by a recent kernel patch ac0677348f3c ("btrfs: merge calculations for simple striped profiles in btrfs_rmap_block"): if (map->type & (BTRFS_BLOCK_GROUP_RAID0 | BTRFS_BLOCK_GROUP_RAID10)) { stripe_nr = stripe_nr * map->num_stripes + i; stripe_nr = div_u64(stripe_nr, map->sub_stripes); <<< } [CAUSE] From the more recent report, it has been proven that we have some chunks with 0 as sub_stripes, mostly caused by older mkfs. It turns out that the mkfs.btrfs fix is only introduced in 6718ab4d33aa ("btrfs-progs: Initialize sub_stripes to 1 in btrfs_alloc_data_chunk") which is included in v5.4 btrfs-progs release. So there would be quite some old filesystems with such 0 sub_stripes. [FIX] Just don't trust the sub_stripes values from disk. We have a trusted btrfs_raid_array[] to fetch the correct sub_stripes numbers for each profile and that are fixed. By this, we can keep the compatibility with older filesystems while still avoid divide-by-zero bugs. Reported-by: kernel test robot Reported-by: Viktor Kuzmin Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=216559 Fixes: ac0677348f3c ("btrfs: merge calculations for simple striped profiles in btrfs_rmap_block") CC: stable@vger.kernel.org # 6.0 Reviewed-by: Su Yue Reviewed-by: Johannes Thumshirn Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 94ba46d57920..a8d4bc6a1937 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -7142,6 +7142,7 @@ static int read_one_chunk(struct btrfs_key *key, struct extent_buffer *leaf, u64 devid; u64 type; u8 uuid[BTRFS_UUID_SIZE]; + int index; int num_stripes; int ret; int i; @@ -7149,6 +7150,7 @@ static int read_one_chunk(struct btrfs_key *key, struct extent_buffer *leaf, logical = key->offset; length = btrfs_chunk_length(leaf, chunk); type = btrfs_chunk_type(leaf, chunk); + index = btrfs_bg_flags_to_raid_index(type); num_stripes = btrfs_chunk_num_stripes(leaf, chunk); #if BITS_PER_LONG == 32 @@ -7202,7 +7204,15 @@ static int read_one_chunk(struct btrfs_key *key, struct extent_buffer *leaf, map->io_align = btrfs_chunk_io_align(leaf, chunk); map->stripe_len = btrfs_chunk_stripe_len(leaf, chunk); map->type = type; - map->sub_stripes = btrfs_chunk_sub_stripes(leaf, chunk); + /* + * We can't use the sub_stripes value, as for profiles other than + * RAID10, they may have 0 as sub_stripes for filesystems created by + * older mkfs (sub_stripes = btrfs_raid_array[index].sub_stripes; map->verified_stripes = 0; em->orig_block_len = btrfs_calc_stripe_length(em); for (i = 0; i < num_stripes; i++) { From 6853a71726b6f5930b4450889faf02e8f1cfe35c Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Tue, 25 Oct 2022 14:51:56 +0000 Subject: [PATCH 0109/1823] KVM: arm64: Use correct accessor to parse stage-1 PTEs hyp_get_page_state() is used with pKVM to retrieve metadata about a page by parsing a hypervisor stage-1 PTE. However, it incorrectly uses a helper which parses *stage-2* mappings. Ouch. Luckily, pkvm_getstate() only looks at the software bits, which happen to be in the same place for stage-1 and stage-2 PTEs, and this all ends up working correctly by accident. But clearly, we should do better. Fix hyp_get_page_state() to use the correct helper. Fixes: e82edcc75c4e ("KVM: arm64: Implement do_share() helper for sharing memory") Signed-off-by: Quentin Perret Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20221025145156.855308-1-qperret@google.com --- arch/arm64/kvm/hyp/nvhe/mem_protect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c index 1e78acf9662e..07f9dc9848ef 100644 --- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c +++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c @@ -516,7 +516,7 @@ static enum pkvm_page_state hyp_get_page_state(kvm_pte_t pte) if (!kvm_pte_valid(pte)) return PKVM_NOPAGE; - return pkvm_getstate(kvm_pgtable_stage2_pte_prot(pte)); + return pkvm_getstate(kvm_pgtable_hyp_pte_prot(pte)); } static int __hyp_check_page_state_range(u64 addr, u64 size, From 937ec9f7d5f2625d60077bb7824fee35dc447c6e Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Tue, 25 Oct 2022 14:21:50 +0200 Subject: [PATCH 0110/1823] staging: rtl8192e: remove bogus ssid character sign test This error triggers on some architectures with unsigned `char` types: drivers/staging/rtl8192e/rtllib_softmac_wx.c:459 rtllib_wx_set_essid() warn: impossible condition '(extra[i] < 0) => (0-255 < 0)' But actually, the entire test is bogus, as ssids don't have any sign validity rules like that. So just remove this check look all together. Cc: Greg Kroah-Hartman Cc: linux-staging@lists.linux.dev Signed-off-by: Jason A. Donenfeld Reviewed-by: Dan Carpenter Link: https://lore.kernel.org/r/20221025122150.583617-1-Jason@zx2c4.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8192e/rtllib_softmac_wx.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/staging/rtl8192e/rtllib_softmac_wx.c b/drivers/staging/rtl8192e/rtllib_softmac_wx.c index f9589c5b62ba..1e5ad3b476ef 100644 --- a/drivers/staging/rtl8192e/rtllib_softmac_wx.c +++ b/drivers/staging/rtl8192e/rtllib_softmac_wx.c @@ -439,7 +439,7 @@ int rtllib_wx_set_essid(struct rtllib_device *ieee, union iwreq_data *wrqu, char *extra) { - int ret = 0, len, i; + int ret = 0, len; short proto_started; unsigned long flags; @@ -455,13 +455,6 @@ int rtllib_wx_set_essid(struct rtllib_device *ieee, goto out; } - for (i = 0; i < len; i++) { - if (extra[i] < 0) { - ret = -1; - goto out; - } - } - if (proto_started) rtllib_stop_protocol(ieee, true); From a9003f74f5a2f487e101f3aa1dd5c3d3a78c6999 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 7 Oct 2022 15:10:01 +0200 Subject: [PATCH 0111/1823] clk: renesas: r8a779g0: Fix HSCIF parent clocks As serial communication requires a clean clock signal, the High Speed Serial Communication Interfaces with FIFO (HSCIF) is clocked by a clock that is not affected by Spread Spectrum or Fractional Multiplication. Hence change the parent clocks for the HSCIF modules from the S0D3_PER clock to the SASYNCPERD1 clock (which has the same clock rate), cfr. R-Car V4H Hardware User's Manual rev. 0.54. Fixes: 0ab55cf1834177a2 ("clk: renesas: cpg-mssr: Add support for R-Car V4H") Signed-off-by: Geert Uytterhoeven Acked-by: Stephen Boyd Link: https://lore.kernel.org/r/b7928abc8b9f53d5b06ec8624342f449de3d24ec.1665147497.git.geert+renesas@glider.be --- drivers/clk/renesas/r8a779g0-cpg-mssr.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/clk/renesas/r8a779g0-cpg-mssr.c b/drivers/clk/renesas/r8a779g0-cpg-mssr.c index 3e8c93facfa1..d5b325e3c539 100644 --- a/drivers/clk/renesas/r8a779g0-cpg-mssr.c +++ b/drivers/clk/renesas/r8a779g0-cpg-mssr.c @@ -158,10 +158,10 @@ static const struct mssr_mod_clk r8a779g0_mod_clks[] __initconst = { DEF_MOD("avb0", 211, R8A779G0_CLK_S0D4_HSC), DEF_MOD("avb1", 212, R8A779G0_CLK_S0D4_HSC), DEF_MOD("avb2", 213, R8A779G0_CLK_S0D4_HSC), - DEF_MOD("hscif0", 514, R8A779G0_CLK_S0D3_PER), - DEF_MOD("hscif1", 515, R8A779G0_CLK_S0D3_PER), - DEF_MOD("hscif2", 516, R8A779G0_CLK_S0D3_PER), - DEF_MOD("hscif3", 517, R8A779G0_CLK_S0D3_PER), + DEF_MOD("hscif0", 514, R8A779G0_CLK_SASYNCPERD1), + DEF_MOD("hscif1", 515, R8A779G0_CLK_SASYNCPERD1), + DEF_MOD("hscif2", 516, R8A779G0_CLK_SASYNCPERD1), + DEF_MOD("hscif3", 517, R8A779G0_CLK_SASYNCPERD1), DEF_MOD("i2c0", 518, R8A779G0_CLK_S0D6_PER), DEF_MOD("i2c1", 519, R8A779G0_CLK_S0D6_PER), DEF_MOD("i2c2", 520, R8A779G0_CLK_S0D6_PER), From 1683d3282f240336a2b4b6b541d435facfe8bbb6 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Tue, 25 Oct 2022 16:01:49 +0100 Subject: [PATCH 0112/1823] ASoC: dapm: Don't use prefix for regulator name When a component has a prefix, and uses a SND_SOC_DAPM_REGULATOR_SUPPLY, the name of the regulator should not use the prefix, otherwise it won't be properly matched in the DT/ACPI. Fixes: 3caac759681e ("ASoC: soc-dapm.c: fixup snd_soc_dapm_new_control_unlocked() error handling") Signed-off-by: Paul Cercueil Link: https://lore.kernel.org/r/20221025150149.113129-1-paul@crapouillou.net Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index d515e7a78ea8..879cf1be67a9 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3645,7 +3645,7 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, switch (w->id) { case snd_soc_dapm_regulator_supply: - w->regulator = devm_regulator_get(dapm->dev, w->name); + w->regulator = devm_regulator_get(dapm->dev, widget->name); if (IS_ERR(w->regulator)) { ret = PTR_ERR(w->regulator); goto request_failed; From d40b6529c6269cd5afddb1116a383cab9f126694 Mon Sep 17 00:00:00 2001 From: Brent Mendelsohn Date: Mon, 24 Oct 2022 18:42:27 +0100 Subject: [PATCH 0113/1823] ASoC: amd: yc: Add Alienware m17 R5 AMD into DMI table This model requires an additional detection quirk to enable the internal microphone - BIOS doesn't seem to support AcpDmicConnected (nothing in acpidump output). Link: https://bugzilla.kernel.org/show_bug.cgi?id=216590 Signed-off-by: Brent Mendelsohn Reviewed-by: Mario Limonciello Link: https://lore.kernel.org/r/20221024174227.4160-1-mendiebm@gmail.com Signed-off-by: Mark Brown --- sound/soc/amd/yc/acp6x-mach.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index 6c0f1de10429..d9715bea965e 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -206,6 +206,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "UM5302TA"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Alienware"), + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m17 R5 AMD"), + } + }, {} }; From 8bb0ac0e6f64ebdf15d963c26b028de391c9bcf9 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 25 Oct 2022 16:09:42 +0200 Subject: [PATCH 0114/1823] ASoC: Intel: bytcht_es8316: Add quirk for the Nanote UMPC-01 The Nanote UMPC-01 mini laptop has stereo speakers, while the default bytcht_es8316 settings assume a mono speaker setup. Add a quirk for this. Signed-off-by: Hans de Goede Acked-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20221025140942.509066-1-hdegoede@redhat.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/bytcht_es8316.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/intel/boards/bytcht_es8316.c b/sound/soc/intel/boards/bytcht_es8316.c index 6432b83f616f..a935c5fd9edb 100644 --- a/sound/soc/intel/boards/bytcht_es8316.c +++ b/sound/soc/intel/boards/bytcht_es8316.c @@ -443,6 +443,13 @@ static const struct dmi_system_id byt_cht_es8316_quirk_table[] = { | BYT_CHT_ES8316_INTMIC_IN2_MAP | BYT_CHT_ES8316_JD_INVERTED), }, + { /* Nanote UMPC-01 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "RWC CO.,LTD"), + DMI_MATCH(DMI_PRODUCT_NAME, "UMPC-01"), + }, + .driver_data = (void *)BYT_CHT_ES8316_INTMIC_IN1_MAP, + }, { /* Teclast X98 Plus II */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TECLAST"), From 69d1abc0214e944dff1d30e201f8fc332a1adf1a Mon Sep 17 00:00:00 2001 From: Tzung-Bi Shih Date: Tue, 25 Oct 2022 10:49:29 +0800 Subject: [PATCH 0115/1823] MAINTAINERS: update Tzung-Bi's email address Use kernel.org account instead. Signed-off-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20221025024929.2652134-1-tzungbi@kernel.org Signed-off-by: Mark Brown --- .mailmap | 1 + .../devicetree/bindings/sound/google,cros-ec-codec.yaml | 2 +- Documentation/devicetree/bindings/sound/realtek,rt1015p.yaml | 2 +- MAINTAINERS | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.mailmap b/.mailmap index 380378e2db36..84342d781407 100644 --- a/.mailmap +++ b/.mailmap @@ -414,6 +414,7 @@ TripleX Chung TripleX Chung Tsuneo Yoshioka Tycho Andersen +Tzung-Bi Shih Uwe Kleine-König Uwe Kleine-König Uwe Kleine-König diff --git a/Documentation/devicetree/bindings/sound/google,cros-ec-codec.yaml b/Documentation/devicetree/bindings/sound/google,cros-ec-codec.yaml index c3e9f3485449..dea293f403d9 100644 --- a/Documentation/devicetree/bindings/sound/google,cros-ec-codec.yaml +++ b/Documentation/devicetree/bindings/sound/google,cros-ec-codec.yaml @@ -8,7 +8,7 @@ title: Audio codec controlled by ChromeOS EC maintainers: - Cheng-Yi Chiang - - Tzung-Bi Shih + - Tzung-Bi Shih description: | Google's ChromeOS EC codec is a digital mic codec provided by the diff --git a/Documentation/devicetree/bindings/sound/realtek,rt1015p.yaml b/Documentation/devicetree/bindings/sound/realtek,rt1015p.yaml index 1d73204451b1..ea7d4900ee4a 100644 --- a/Documentation/devicetree/bindings/sound/realtek,rt1015p.yaml +++ b/Documentation/devicetree/bindings/sound/realtek,rt1015p.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Realtek rt1015p codec devicetree bindings maintainers: - - Tzung-Bi Shih + - Tzung-Bi Shih description: | Rt1015p is a rt1015 variant which does not support I2C and diff --git a/MAINTAINERS b/MAINTAINERS index cf0f18502372..f9749afc0b9d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4906,7 +4906,7 @@ F: drivers/platform/chrome/ CHROMEOS EC CODEC DRIVER M: Cheng-Yi Chiang -M: Tzung-Bi Shih +M: Tzung-Bi Shih R: Guenter Roeck L: chrome-platform@lists.linux.dev S: Maintained From 04694e50020b62b10bd0d46ff9e9708a6e1c7eb3 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Wed, 26 Oct 2022 09:58:28 +0200 Subject: [PATCH 0116/1823] spi: meson-spicc: move wait completion in driver to take bursts delay in account Some delay occurs between each bursts, thus the default delay is wrong and a timeout will occur with big enough transfers. The solution is to handle the timeout management in the driver and add some delay for each bursts in the timeout calculation. Reported-by: Da Xue Signed-off-by: Neil Armstrong Link: https://lore.kernel.org/r/20221026-spicc-burst-delay-v1-0-1be5ffb7051a@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-meson-spicc.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-meson-spicc.c b/drivers/spi/spi-meson-spicc.c index bad201510a99..52bffab18329 100644 --- a/drivers/spi/spi-meson-spicc.c +++ b/drivers/spi/spi-meson-spicc.c @@ -160,6 +160,7 @@ struct meson_spicc_device { struct clk *clk; struct spi_message *message; struct spi_transfer *xfer; + struct completion done; const struct meson_spicc_data *data; u8 *tx_buf; u8 *rx_buf; @@ -282,7 +283,7 @@ static irqreturn_t meson_spicc_irq(int irq, void *data) /* Disable all IRQs */ writel(0, spicc->base + SPICC_INTREG); - spi_finalize_current_transfer(spicc->master); + complete(&spicc->done); return IRQ_HANDLED; } @@ -386,6 +387,7 @@ static int meson_spicc_transfer_one(struct spi_master *master, struct spi_transfer *xfer) { struct meson_spicc_device *spicc = spi_master_get_devdata(master); + unsigned long timeout; /* Store current transfer */ spicc->xfer = xfer; @@ -410,13 +412,29 @@ static int meson_spicc_transfer_one(struct spi_master *master, /* Setup burst */ meson_spicc_setup_burst(spicc); + /* Setup wait for completion */ + reinit_completion(&spicc->done); + + /* For each byte we wait for 8 cycles of the SPI clock */ + timeout = 8LL * MSEC_PER_SEC * xfer->len; + do_div(timeout, xfer->speed_hz); + + /* Add 10us delay between each fifo bursts */ + timeout += ((xfer->len >> 4) * 10) / MSEC_PER_SEC; + + /* Increase it twice and add 200 ms tolerance */ + timeout += timeout + 200; + /* Start burst */ writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG); /* Enable interrupts */ writel_relaxed(SPICC_TC_EN, spicc->base + SPICC_INTREG); - return 1; + if (!wait_for_completion_timeout(&spicc->done, msecs_to_jiffies(timeout))) + return -ETIMEDOUT; + + return 0; } static int meson_spicc_prepare_message(struct spi_master *master, @@ -743,6 +761,8 @@ static int meson_spicc_probe(struct platform_device *pdev) spicc->pdev = pdev; platform_set_drvdata(pdev, spicc); + init_completion(&spicc->done); + spicc->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(spicc->base)) { dev_err(&pdev->dev, "io resource mapping failed\n"); From e07ee6fe21f47cfd72ae566395c67a80e7c66163 Mon Sep 17 00:00:00 2001 From: Allison Henderson Date: Tue, 25 Oct 2022 12:16:27 -0700 Subject: [PATCH 0117/1823] xfs: increase rename inode reservation xfs_rename can update up to 5 inodes: src_dp, target_dp, src_ip, target_ip and wip. So we need to increase the inode reservation to match. Signed-off-by: Allison Henderson Reviewed-by: Darrick J. Wong Signed-off-by: Darrick J. Wong --- fs/xfs/libxfs/xfs_trans_resv.c | 4 ++-- fs/xfs/xfs_inode.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/xfs/libxfs/xfs_trans_resv.c b/fs/xfs/libxfs/xfs_trans_resv.c index 2c4ad6e4bb14..5b2f27cbdb80 100644 --- a/fs/xfs/libxfs/xfs_trans_resv.c +++ b/fs/xfs/libxfs/xfs_trans_resv.c @@ -422,7 +422,7 @@ xfs_calc_itruncate_reservation_minlogsize( /* * In renaming a files we can modify: - * the four inodes involved: 4 * inode size + * the five inodes involved: 5 * inode size * the two directory btrees: 2 * (max depth + v2) * dir block size * the two directory bmap btrees: 2 * max depth * block size * And the bmap_finish transaction can free dir and bmap blocks (two sets @@ -437,7 +437,7 @@ xfs_calc_rename_reservation( struct xfs_mount *mp) { return XFS_DQUOT_LOGRES(mp) + - max((xfs_calc_inode_res(mp, 4) + + max((xfs_calc_inode_res(mp, 5) + xfs_calc_buf_res(2 * XFS_DIROP_LOG_COUNT(mp), XFS_FSB_TO_B(mp, 1))), (xfs_calc_buf_res(7, mp->m_sb.sb_sectsize) + diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index c000b74dd203..aa303be11576 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -2818,7 +2818,7 @@ retry: * Lock all the participating inodes. Depending upon whether * the target_name exists in the target directory, and * whether the target directory is the same as the source - * directory, we can lock from 2 to 4 inodes. + * directory, we can lock from 2 to 5 inodes. */ xfs_lock_inodes(inodes, num_inodes, XFS_ILOCK_EXCL); From a0c9f1f2e53b8eb2ae43987a30e547ba56b4fa18 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Thu, 27 Oct 2022 09:12:05 +0200 Subject: [PATCH 0118/1823] parisc: Export iosapic_serial_irq() symbol for serial port driver The parisc serial port driver needs this symbol when it's compiled as module. Signed-off-by: Helge Deller Reported-by: kernel test robot Cc: --- drivers/parisc/iosapic.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/parisc/iosapic.c b/drivers/parisc/iosapic.c index bdef7a8d6ab8..bcc1dae00780 100644 --- a/drivers/parisc/iosapic.c +++ b/drivers/parisc/iosapic.c @@ -866,6 +866,7 @@ int iosapic_serial_irq(struct parisc_device *dev) return vi->txn_irq; } +EXPORT_SYMBOL(iosapic_serial_irq); #endif From 86c4f0d547f6460d0426ebb3ba0614f1134b8cda Mon Sep 17 00:00:00 2001 From: Jim Mattson Date: Thu, 29 Sep 2022 15:52:03 -0700 Subject: [PATCH 0119/1823] KVM: x86: Mask off reserved bits in CPUID.8000001FH KVM_GET_SUPPORTED_CPUID should only enumerate features that KVM actually supports. CPUID.8000001FH:EBX[31:16] are reserved bits and should be masked off. Fixes: 8765d75329a3 ("KVM: X86: Extend CPUID range to include new leaf") Signed-off-by: Jim Mattson Message-Id: <20220929225203.2234702-6-jmattson@google.com> Cc: stable@vger.kernel.org [Clear NumVMPL too. - Paolo] Signed-off-by: Paolo Bonzini --- arch/x86/kvm/cpuid.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index a0292ba650df..0810e93cbedc 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -1199,7 +1199,8 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function) entry->eax = entry->ebx = entry->ecx = entry->edx = 0; } else { cpuid_entry_override(entry, CPUID_8000_001F_EAX); - + /* Clear NumVMPL since KVM does not support VMPL. */ + entry->ebx &= ~GENMASK(31, 12); /* * Enumerate '0' for "PA bits reduction", the adjusted * MAXPHYADDR is enumerated directly (see 0x80000008). From 5aa02366773376a1fd3a5c6a815e5f6e026ab391 Mon Sep 17 00:00:00 2001 From: Hou Wenlong Date: Fri, 14 Oct 2022 15:55:11 +0800 Subject: [PATCH 0120/1823] KVM: x86: Reduce refcount if single_open() fails in kvm_mmu_rmaps_stat_open() Refcount is increased before calling single_open() in kvm_mmu_rmaps_stat_open(), If single_open() fails, refcount should be restored, otherwise the vm couldn't be destroyed. Fixes: 3bcd0662d66fd ("KVM: X86: Introduce mmu_rmaps_stat per-vm debugfs file") Signed-off-by: Hou Wenlong Message-Id: [Preserved return value of single_open. - Paolo] Signed-off-by: Paolo Bonzini --- arch/x86/kvm/debugfs.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/debugfs.c b/arch/x86/kvm/debugfs.c index cfed36aba2f7..c1390357126a 100644 --- a/arch/x86/kvm/debugfs.c +++ b/arch/x86/kvm/debugfs.c @@ -158,11 +158,16 @@ out: static int kvm_mmu_rmaps_stat_open(struct inode *inode, struct file *file) { struct kvm *kvm = inode->i_private; + int r; if (!kvm_get_kvm_safe(kvm)) return -ENOENT; - return single_open(file, kvm_mmu_rmaps_stat_show, kvm); + r = single_open(file, kvm_mmu_rmaps_stat_show, kvm); + if (r < 0) + kvm_put_kvm(kvm); + + return r; } static int kvm_mmu_rmaps_stat_release(struct inode *inode, struct file *file) From 180418e2eb33be5c8d0b703c843e0ebc045aef80 Mon Sep 17 00:00:00 2001 From: Hou Wenlong Date: Mon, 17 Oct 2022 11:06:10 +0800 Subject: [PATCH 0121/1823] KVM: debugfs: Return retval of simple_attr_open() if it fails Although simple_attr_open() fails only with -ENOMEM with current code base, it would be nicer to return retval of simple_attr_open() directly in kvm_debugfs_open(). No functional change intended. Signed-off-by: Hou Wenlong Message-Id: <69d64d93accd1f33691b8a383ae555baee80f943.1665975828.git.houwenlong.hwl@antgroup.com> Cc: stable@vger.kernel.org Signed-off-by: Paolo Bonzini --- virt/kvm/kvm_main.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 1376a47fedee..f1df24c2bc84 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -5409,6 +5409,7 @@ static int kvm_debugfs_open(struct inode *inode, struct file *file, int (*get)(void *, u64 *), int (*set)(void *, u64), const char *fmt) { + int ret; struct kvm_stat_data *stat_data = (struct kvm_stat_data *) inode->i_private; @@ -5420,15 +5421,13 @@ static int kvm_debugfs_open(struct inode *inode, struct file *file, if (!kvm_get_kvm_safe(stat_data->kvm)) return -ENOENT; - if (simple_attr_open(inode, file, get, - kvm_stats_debugfs_mode(stat_data->desc) & 0222 - ? set : NULL, - fmt)) { + ret = simple_attr_open(inode, file, get, + kvm_stats_debugfs_mode(stat_data->desc) & 0222 + ? set : NULL, fmt); + if (ret) kvm_put_kvm(stat_data->kvm); - return -ENOMEM; - } - return 0; + return ret; } static int kvm_debugfs_release(struct inode *inode, struct file *file) From 44fc40a015af7511408f7b447e2c0c2da056fd95 Mon Sep 17 00:00:00 2001 From: Palmer Dabbelt Date: Thu, 13 Oct 2022 23:46:38 +0200 Subject: [PATCH 0122/1823] MAINTAINERS: git://github -> https://github.com for kvm-riscv Github deprecated the git:// links about a year ago, so let's move to the https:// URLs instead. Reported-by: Conor Dooley Link: https://github.blog/2021-09-01-improving-git-protocol-security-github/ Signed-off-by: Palmer Dabbelt Signed-off-by: Paolo Bonzini --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index cf0f18502372..7d62b1640930 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11241,7 +11241,7 @@ L: kvm@vger.kernel.org L: kvm-riscv@lists.infradead.org L: linux-riscv@lists.infradead.org S: Maintained -T: git git://github.com/kvm-riscv/linux.git +T: git https://github.com/kvm-riscv/linux.git F: arch/riscv/include/asm/kvm* F: arch/riscv/include/uapi/asm/kvm* F: arch/riscv/kvm/ From dea0d5a2fde62237ff14c41cb05dd151cebf84c0 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 30 Sep 2022 23:00:08 +0000 Subject: [PATCH 0123/1823] KVM: x86: Exempt pending triple fault from event injection sanity check Exempt pending triple faults, a.k.a. KVM_REQ_TRIPLE_FAULT, when asserting that KVM didn't attempt to queue a new exception during event injection. KVM needs to emulate the injection itself when emulating Real Mode due to lack of unrestricted guest support (VMX) and will queue a triple fault if that emulation fails. Ideally the assertion would more precisely filter out the emulated Real Mode triple fault case, but rmode.vm86_active is buried in vcpu_vmx and can't be queried without a new kvm_x86_ops. And unlike "regular" exceptions, triple fault cannot put the vCPU into an infinite loop; the triple fault will force either an exit to userspace or a nested VM-Exit, and triple fault after nested VM-Exit will force an exit to userspace. I.e. there is no functional issue, so just suppress the warning for triple faults. Opportunistically convert the warning to a one-time thing, when it fires, it fires _a lot_, and is usually user triggerable, i.e. can be used to spam the kernel log. Fixes: 7055fb113116 ("KVM: x86: Treat pending TRIPLE_FAULT requests as pending exceptions") Reported-by: kernel test robot Link: https://lore.kernel.org/r/202209301338.aca913c3-yujie.liu@intel.com Signed-off-by: Sean Christopherson Message-Id: <20220930230008.1636044-1-seanjc@google.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/x86.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 9cf1ba865562..104b72df33d6 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -10044,7 +10044,20 @@ static int kvm_check_and_inject_events(struct kvm_vcpu *vcpu, kvm_x86_ops.nested_ops->has_events(vcpu)) *req_immediate_exit = true; - WARN_ON(kvm_is_exception_pending(vcpu)); + /* + * KVM must never queue a new exception while injecting an event; KVM + * is done emulating and should only propagate the to-be-injected event + * to the VMCS/VMCB. Queueing a new exception can put the vCPU into an + * infinite loop as KVM will bail from VM-Enter to inject the pending + * exception and start the cycle all over. + * + * Exempt triple faults as they have special handling and won't put the + * vCPU into an infinite loop. Triple fault can be queued when running + * VMX without unrestricted guest, as that requires KVM to emulate Real + * Mode events (see kvm_inject_realmode_interrupt()). + */ + WARN_ON_ONCE(vcpu->arch.exception.pending || + vcpu->arch.exception_vmexit.pending); return 0; out: From 1c1a41497ab879ac9608f3047f230af833eeef3d Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Tue, 25 Oct 2022 08:37:49 -0400 Subject: [PATCH 0124/1823] KVM: VMX: fully disable SGX if SECONDARY_EXEC_ENCLS_EXITING unavailable Clear enable_sgx if ENCLS-exiting is not supported, i.e. if SGX cannot be virtualized. When KVM is loaded, adjust_vmx_controls checks that the bit is available before enabling the feature; however, other parts of the code check enable_sgx and not clearing the variable caused two different bugs, mostly affecting nested virtualization scenarios. First, because enable_sgx remained true, SECONDARY_EXEC_ENCLS_EXITING would be marked available in the capability MSR that are accessed by a nested hypervisor. KVM would then propagate the control from vmcs12 to vmcs02 even if it isn't supported by the processor, thus causing an unexpected VM-Fail (exit code 0x7) in L1. Second, vmx_set_cpu_caps() would not clear the SGX bits when hardware support is unavailable. This is a much less problematic bug as it only happens if SGX is soft-disabled (available in the processor but hidden in CPUID) or if SGX is supported for bare metal but not in the VMCS (will never happen when running on bare metal, but can theoertically happen when running in a VM). Last but not least, this ensures that module params in sysfs reflect KVM's actual configuration. RHBZ: https://bugzilla.redhat.com/show_bug.cgi?id=2127128 Fixes: 72add915fbd5 ("KVM: VMX: Enable SGX virtualization for SGX1, SGX2 and LC") Cc: stable@vger.kernel.org Suggested-by: Sean Christopherson Suggested-by: Bandan Das Signed-off-by: Emanuele Giuseppe Esposito Message-Id: <20221025123749.2201649-1-eesposit@redhat.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx/vmx.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 9dba04b6b019..65f092e4a81b 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -8263,6 +8263,11 @@ static __init int hardware_setup(void) if (!cpu_has_virtual_nmis()) enable_vnmi = 0; +#ifdef CONFIG_X86_SGX_KVM + if (!cpu_has_vmx_encls_vmexit()) + enable_sgx = false; +#endif + /* * set_apic_access_page_addr() is used to reload apic access * page upon invalidation. No need to do anything if not From 52491a38b2c2411f3f0229dc6ad610349c704a41 Mon Sep 17 00:00:00 2001 From: Michal Luczaj Date: Thu, 13 Oct 2022 21:12:19 +0000 Subject: [PATCH 0125/1823] KVM: Initialize gfn_to_pfn_cache locks in dedicated helper Move the gfn_to_pfn_cache lock initialization to another helper and call the new helper during VM/vCPU creation. There are race conditions possible due to kvm_gfn_to_pfn_cache_init()'s ability to re-initialize the cache's locks. For example: a race between ioctl(KVM_XEN_HVM_EVTCHN_SEND) and kvm_gfn_to_pfn_cache_init() leads to a corrupted shinfo gpc lock. (thread 1) | (thread 2) | kvm_xen_set_evtchn_fast | read_lock_irqsave(&gpc->lock, ...) | | kvm_gfn_to_pfn_cache_init | rwlock_init(&gpc->lock) read_unlock_irqrestore(&gpc->lock, ...) | Rename "cache_init" and "cache_destroy" to activate+deactivate to avoid implying that the cache really is destroyed/freed. Note, there more races in the newly named kvm_gpc_activate() that will be addressed separately. Fixes: 982ed0de4753 ("KVM: Reinstate gfn_to_pfn_cache with invalidation support") Cc: stable@vger.kernel.org Suggested-by: Sean Christopherson Signed-off-by: Michal Luczaj [sean: call out that this is a bug fix] Signed-off-by: Sean Christopherson Message-Id: <20221013211234.1318131-2-seanjc@google.com> Signed-off-by: Paolo Bonzini --- arch/x86/kvm/x86.c | 12 +++++---- arch/x86/kvm/xen.c | 57 +++++++++++++++++++++------------------- include/linux/kvm_host.h | 24 ++++++++++++----- virt/kvm/pfncache.c | 21 ++++++++------- 4 files changed, 66 insertions(+), 48 deletions(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 104b72df33d6..521b433f978c 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -2315,11 +2315,11 @@ static void kvm_write_system_time(struct kvm_vcpu *vcpu, gpa_t system_time, /* we verify if the enable bit is set... */ if (system_time & 1) { - kvm_gfn_to_pfn_cache_init(vcpu->kvm, &vcpu->arch.pv_time, vcpu, - KVM_HOST_USES_PFN, system_time & ~1ULL, - sizeof(struct pvclock_vcpu_time_info)); + kvm_gpc_activate(vcpu->kvm, &vcpu->arch.pv_time, vcpu, + KVM_HOST_USES_PFN, system_time & ~1ULL, + sizeof(struct pvclock_vcpu_time_info)); } else { - kvm_gfn_to_pfn_cache_destroy(vcpu->kvm, &vcpu->arch.pv_time); + kvm_gpc_deactivate(vcpu->kvm, &vcpu->arch.pv_time); } return; @@ -3388,7 +3388,7 @@ static int kvm_pv_enable_async_pf_int(struct kvm_vcpu *vcpu, u64 data) static void kvmclock_reset(struct kvm_vcpu *vcpu) { - kvm_gfn_to_pfn_cache_destroy(vcpu->kvm, &vcpu->arch.pv_time); + kvm_gpc_deactivate(vcpu->kvm, &vcpu->arch.pv_time); vcpu->arch.time = 0; } @@ -11829,6 +11829,8 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) vcpu->arch.regs_avail = ~0; vcpu->arch.regs_dirty = ~0; + kvm_gpc_init(&vcpu->arch.pv_time); + if (!irqchip_in_kernel(vcpu->kvm) || kvm_vcpu_is_reset_bsp(vcpu)) vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE; else diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c index 93c628d3e3a9..b2be60c6efa4 100644 --- a/arch/x86/kvm/xen.c +++ b/arch/x86/kvm/xen.c @@ -42,13 +42,13 @@ static int kvm_xen_shared_info_init(struct kvm *kvm, gfn_t gfn) int idx = srcu_read_lock(&kvm->srcu); if (gfn == GPA_INVALID) { - kvm_gfn_to_pfn_cache_destroy(kvm, gpc); + kvm_gpc_deactivate(kvm, gpc); goto out; } do { - ret = kvm_gfn_to_pfn_cache_init(kvm, gpc, NULL, KVM_HOST_USES_PFN, - gpa, PAGE_SIZE); + ret = kvm_gpc_activate(kvm, gpc, NULL, KVM_HOST_USES_PFN, gpa, + PAGE_SIZE); if (ret) goto out; @@ -554,15 +554,15 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data) offsetof(struct compat_vcpu_info, time)); if (data->u.gpa == GPA_INVALID) { - kvm_gfn_to_pfn_cache_destroy(vcpu->kvm, &vcpu->arch.xen.vcpu_info_cache); + kvm_gpc_deactivate(vcpu->kvm, &vcpu->arch.xen.vcpu_info_cache); r = 0; break; } - r = kvm_gfn_to_pfn_cache_init(vcpu->kvm, - &vcpu->arch.xen.vcpu_info_cache, - NULL, KVM_HOST_USES_PFN, data->u.gpa, - sizeof(struct vcpu_info)); + r = kvm_gpc_activate(vcpu->kvm, + &vcpu->arch.xen.vcpu_info_cache, NULL, + KVM_HOST_USES_PFN, data->u.gpa, + sizeof(struct vcpu_info)); if (!r) kvm_make_request(KVM_REQ_CLOCK_UPDATE, vcpu); @@ -570,16 +570,16 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data) case KVM_XEN_VCPU_ATTR_TYPE_VCPU_TIME_INFO: if (data->u.gpa == GPA_INVALID) { - kvm_gfn_to_pfn_cache_destroy(vcpu->kvm, - &vcpu->arch.xen.vcpu_time_info_cache); + kvm_gpc_deactivate(vcpu->kvm, + &vcpu->arch.xen.vcpu_time_info_cache); r = 0; break; } - r = kvm_gfn_to_pfn_cache_init(vcpu->kvm, - &vcpu->arch.xen.vcpu_time_info_cache, - NULL, KVM_HOST_USES_PFN, data->u.gpa, - sizeof(struct pvclock_vcpu_time_info)); + r = kvm_gpc_activate(vcpu->kvm, + &vcpu->arch.xen.vcpu_time_info_cache, + NULL, KVM_HOST_USES_PFN, data->u.gpa, + sizeof(struct pvclock_vcpu_time_info)); if (!r) kvm_make_request(KVM_REQ_CLOCK_UPDATE, vcpu); break; @@ -590,16 +590,15 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data) break; } if (data->u.gpa == GPA_INVALID) { - kvm_gfn_to_pfn_cache_destroy(vcpu->kvm, - &vcpu->arch.xen.runstate_cache); + kvm_gpc_deactivate(vcpu->kvm, + &vcpu->arch.xen.runstate_cache); r = 0; break; } - r = kvm_gfn_to_pfn_cache_init(vcpu->kvm, - &vcpu->arch.xen.runstate_cache, - NULL, KVM_HOST_USES_PFN, data->u.gpa, - sizeof(struct vcpu_runstate_info)); + r = kvm_gpc_activate(vcpu->kvm, &vcpu->arch.xen.runstate_cache, + NULL, KVM_HOST_USES_PFN, data->u.gpa, + sizeof(struct vcpu_runstate_info)); break; case KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_CURRENT: @@ -1816,7 +1815,12 @@ void kvm_xen_init_vcpu(struct kvm_vcpu *vcpu) { vcpu->arch.xen.vcpu_id = vcpu->vcpu_idx; vcpu->arch.xen.poll_evtchn = 0; + timer_setup(&vcpu->arch.xen.poll_timer, cancel_evtchn_poll, 0); + + kvm_gpc_init(&vcpu->arch.xen.runstate_cache); + kvm_gpc_init(&vcpu->arch.xen.vcpu_info_cache); + kvm_gpc_init(&vcpu->arch.xen.vcpu_time_info_cache); } void kvm_xen_destroy_vcpu(struct kvm_vcpu *vcpu) @@ -1824,18 +1828,17 @@ void kvm_xen_destroy_vcpu(struct kvm_vcpu *vcpu) if (kvm_xen_timer_enabled(vcpu)) kvm_xen_stop_timer(vcpu); - kvm_gfn_to_pfn_cache_destroy(vcpu->kvm, - &vcpu->arch.xen.runstate_cache); - kvm_gfn_to_pfn_cache_destroy(vcpu->kvm, - &vcpu->arch.xen.vcpu_info_cache); - kvm_gfn_to_pfn_cache_destroy(vcpu->kvm, - &vcpu->arch.xen.vcpu_time_info_cache); + kvm_gpc_deactivate(vcpu->kvm, &vcpu->arch.xen.runstate_cache); + kvm_gpc_deactivate(vcpu->kvm, &vcpu->arch.xen.vcpu_info_cache); + kvm_gpc_deactivate(vcpu->kvm, &vcpu->arch.xen.vcpu_time_info_cache); + del_timer_sync(&vcpu->arch.xen.poll_timer); } void kvm_xen_init_vm(struct kvm *kvm) { idr_init(&kvm->arch.xen.evtchn_ports); + kvm_gpc_init(&kvm->arch.xen.shinfo_cache); } void kvm_xen_destroy_vm(struct kvm *kvm) @@ -1843,7 +1846,7 @@ void kvm_xen_destroy_vm(struct kvm *kvm) struct evtchnfd *evtchnfd; int i; - kvm_gfn_to_pfn_cache_destroy(kvm, &kvm->arch.xen.shinfo_cache); + kvm_gpc_deactivate(kvm, &kvm->arch.xen.shinfo_cache); idr_for_each_entry(&kvm->arch.xen.evtchn_ports, evtchnfd, i) { if (!evtchnfd->deliver.port.port) diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 00c3448ba7f8..18592bdf4c1b 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -1240,8 +1240,18 @@ int kvm_vcpu_write_guest(struct kvm_vcpu *vcpu, gpa_t gpa, const void *data, void kvm_vcpu_mark_page_dirty(struct kvm_vcpu *vcpu, gfn_t gfn); /** - * kvm_gfn_to_pfn_cache_init - prepare a cached kernel mapping and HPA for a - * given guest physical address. + * kvm_gpc_init - initialize gfn_to_pfn_cache. + * + * @gpc: struct gfn_to_pfn_cache object. + * + * This sets up a gfn_to_pfn_cache by initializing locks. Note, the cache must + * be zero-allocated (or zeroed by the caller before init). + */ +void kvm_gpc_init(struct gfn_to_pfn_cache *gpc); + +/** + * kvm_gpc_activate - prepare a cached kernel mapping and HPA for a given guest + * physical address. * * @kvm: pointer to kvm instance. * @gpc: struct gfn_to_pfn_cache object. @@ -1265,9 +1275,9 @@ void kvm_vcpu_mark_page_dirty(struct kvm_vcpu *vcpu, gfn_t gfn); * kvm_gfn_to_pfn_cache_check() to ensure that the cache is valid before * accessing the target page. */ -int kvm_gfn_to_pfn_cache_init(struct kvm *kvm, struct gfn_to_pfn_cache *gpc, - struct kvm_vcpu *vcpu, enum pfn_cache_usage usage, - gpa_t gpa, unsigned long len); +int kvm_gpc_activate(struct kvm *kvm, struct gfn_to_pfn_cache *gpc, + struct kvm_vcpu *vcpu, enum pfn_cache_usage usage, + gpa_t gpa, unsigned long len); /** * kvm_gfn_to_pfn_cache_check - check validity of a gfn_to_pfn_cache. @@ -1324,7 +1334,7 @@ int kvm_gfn_to_pfn_cache_refresh(struct kvm *kvm, struct gfn_to_pfn_cache *gpc, void kvm_gfn_to_pfn_cache_unmap(struct kvm *kvm, struct gfn_to_pfn_cache *gpc); /** - * kvm_gfn_to_pfn_cache_destroy - destroy and unlink a gfn_to_pfn_cache. + * kvm_gpc_deactivate - deactivate and unlink a gfn_to_pfn_cache. * * @kvm: pointer to kvm instance. * @gpc: struct gfn_to_pfn_cache object. @@ -1332,7 +1342,7 @@ void kvm_gfn_to_pfn_cache_unmap(struct kvm *kvm, struct gfn_to_pfn_cache *gpc); * This removes a cache from the @kvm's list to be processed on MMU notifier * invocation. */ -void kvm_gfn_to_pfn_cache_destroy(struct kvm *kvm, struct gfn_to_pfn_cache *gpc); +void kvm_gpc_deactivate(struct kvm *kvm, struct gfn_to_pfn_cache *gpc); void kvm_sigset_activate(struct kvm_vcpu *vcpu); void kvm_sigset_deactivate(struct kvm_vcpu *vcpu); diff --git a/virt/kvm/pfncache.c b/virt/kvm/pfncache.c index 68ff41d39545..08f97cf97264 100644 --- a/virt/kvm/pfncache.c +++ b/virt/kvm/pfncache.c @@ -346,17 +346,20 @@ void kvm_gfn_to_pfn_cache_unmap(struct kvm *kvm, struct gfn_to_pfn_cache *gpc) } EXPORT_SYMBOL_GPL(kvm_gfn_to_pfn_cache_unmap); +void kvm_gpc_init(struct gfn_to_pfn_cache *gpc) +{ + rwlock_init(&gpc->lock); + mutex_init(&gpc->refresh_lock); +} +EXPORT_SYMBOL_GPL(kvm_gpc_init); -int kvm_gfn_to_pfn_cache_init(struct kvm *kvm, struct gfn_to_pfn_cache *gpc, - struct kvm_vcpu *vcpu, enum pfn_cache_usage usage, - gpa_t gpa, unsigned long len) +int kvm_gpc_activate(struct kvm *kvm, struct gfn_to_pfn_cache *gpc, + struct kvm_vcpu *vcpu, enum pfn_cache_usage usage, + gpa_t gpa, unsigned long len) { WARN_ON_ONCE(!usage || (usage & KVM_GUEST_AND_HOST_USE_PFN) != usage); if (!gpc->active) { - rwlock_init(&gpc->lock); - mutex_init(&gpc->refresh_lock); - gpc->khva = NULL; gpc->pfn = KVM_PFN_ERR_FAULT; gpc->uhva = KVM_HVA_ERR_BAD; @@ -371,9 +374,9 @@ int kvm_gfn_to_pfn_cache_init(struct kvm *kvm, struct gfn_to_pfn_cache *gpc, } return kvm_gfn_to_pfn_cache_refresh(kvm, gpc, gpa, len); } -EXPORT_SYMBOL_GPL(kvm_gfn_to_pfn_cache_init); +EXPORT_SYMBOL_GPL(kvm_gpc_activate); -void kvm_gfn_to_pfn_cache_destroy(struct kvm *kvm, struct gfn_to_pfn_cache *gpc) +void kvm_gpc_deactivate(struct kvm *kvm, struct gfn_to_pfn_cache *gpc) { if (gpc->active) { spin_lock(&kvm->gpc_lock); @@ -384,4 +387,4 @@ void kvm_gfn_to_pfn_cache_destroy(struct kvm *kvm, struct gfn_to_pfn_cache *gpc) gpc->active = false; } } -EXPORT_SYMBOL_GPL(kvm_gfn_to_pfn_cache_destroy); +EXPORT_SYMBOL_GPL(kvm_gpc_deactivate); From ecbcf030b45666ad11bc98565e71dfbcb7be4393 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 13 Oct 2022 21:12:20 +0000 Subject: [PATCH 0126/1823] KVM: Reject attempts to consume or refresh inactive gfn_to_pfn_cache Reject kvm_gpc_check() and kvm_gpc_refresh() if the cache is inactive. Not checking the active flag during refresh is particularly egregious, as KVM can end up with a valid, inactive cache, which can lead to a variety of use-after-free bugs, e.g. consuming a NULL kernel pointer or missing an mmu_notifier invalidation due to the cache not being on the list of gfns to invalidate. Note, "active" needs to be set if and only if the cache is on the list of caches, i.e. is reachable via mmu_notifier events. If a relevant mmu_notifier event occurs while the cache is "active" but not on the list, KVM will not acquire the cache's lock and so will not serailize the mmu_notifier event with active users and/or kvm_gpc_refresh(). A race between KVM_XEN_ATTR_TYPE_SHARED_INFO and KVM_XEN_HVM_EVTCHN_SEND can be exploited to trigger the bug. 1. Deactivate shinfo cache: kvm_xen_hvm_set_attr case KVM_XEN_ATTR_TYPE_SHARED_INFO kvm_gpc_deactivate kvm_gpc_unmap gpc->valid = false gpc->khva = NULL gpc->active = false Result: active = false, valid = false 2. Cause cache refresh: kvm_arch_vm_ioctl case KVM_XEN_HVM_EVTCHN_SEND kvm_xen_hvm_evtchn_send kvm_xen_set_evtchn kvm_xen_set_evtchn_fast kvm_gpc_check return -EWOULDBLOCK because !gpc->valid kvm_xen_set_evtchn_fast return -EWOULDBLOCK kvm_gpc_refresh hva_to_pfn_retry gpc->valid = true gpc->khva = not NULL Result: active = false, valid = true 3. Race ioctl KVM_XEN_HVM_EVTCHN_SEND against ioctl KVM_XEN_ATTR_TYPE_SHARED_INFO: kvm_arch_vm_ioctl case KVM_XEN_HVM_EVTCHN_SEND kvm_xen_hvm_evtchn_send kvm_xen_set_evtchn kvm_xen_set_evtchn_fast read_lock gpc->lock kvm_xen_hvm_set_attr case KVM_XEN_ATTR_TYPE_SHARED_INFO mutex_lock kvm->lock kvm_xen_shared_info_init kvm_gpc_activate gpc->khva = NULL kvm_gpc_check [ Check passes because gpc->valid is still true, even though gpc->khva is already NULL. ] shinfo = gpc->khva pending_bits = shinfo->evtchn_pending CRASH: test_and_set_bit(..., pending_bits) Fixes: 982ed0de4753 ("KVM: Reinstate gfn_to_pfn_cache with invalidation support") Cc: stable@vger.kernel.org Reported-by: : Michal Luczaj Signed-off-by: Sean Christopherson Message-Id: <20221013211234.1318131-3-seanjc@google.com> Signed-off-by: Paolo Bonzini --- virt/kvm/pfncache.c | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/virt/kvm/pfncache.c b/virt/kvm/pfncache.c index 08f97cf97264..346e47f15572 100644 --- a/virt/kvm/pfncache.c +++ b/virt/kvm/pfncache.c @@ -81,6 +81,9 @@ bool kvm_gfn_to_pfn_cache_check(struct kvm *kvm, struct gfn_to_pfn_cache *gpc, { struct kvm_memslots *slots = kvm_memslots(kvm); + if (!gpc->active) + return false; + if ((gpa & ~PAGE_MASK) + len > PAGE_SIZE) return false; @@ -240,10 +243,11 @@ int kvm_gfn_to_pfn_cache_refresh(struct kvm *kvm, struct gfn_to_pfn_cache *gpc, { struct kvm_memslots *slots = kvm_memslots(kvm); unsigned long page_offset = gpa & ~PAGE_MASK; - kvm_pfn_t old_pfn, new_pfn; + bool unmap_old = false; unsigned long old_uhva; + kvm_pfn_t old_pfn; void *old_khva; - int ret = 0; + int ret; /* * If must fit within a single page. The 'len' argument is @@ -261,6 +265,11 @@ int kvm_gfn_to_pfn_cache_refresh(struct kvm *kvm, struct gfn_to_pfn_cache *gpc, write_lock_irq(&gpc->lock); + if (!gpc->active) { + ret = -EINVAL; + goto out_unlock; + } + old_pfn = gpc->pfn; old_khva = gpc->khva - offset_in_page(gpc->khva); old_uhva = gpc->uhva; @@ -291,6 +300,7 @@ int kvm_gfn_to_pfn_cache_refresh(struct kvm *kvm, struct gfn_to_pfn_cache *gpc, /* If the HVA→PFN mapping was already valid, don't unmap it. */ old_pfn = KVM_PFN_ERR_FAULT; old_khva = NULL; + ret = 0; } out: @@ -305,14 +315,15 @@ int kvm_gfn_to_pfn_cache_refresh(struct kvm *kvm, struct gfn_to_pfn_cache *gpc, gpc->khva = NULL; } - /* Snapshot the new pfn before dropping the lock! */ - new_pfn = gpc->pfn; + /* Detect a pfn change before dropping the lock! */ + unmap_old = (old_pfn != gpc->pfn); +out_unlock: write_unlock_irq(&gpc->lock); mutex_unlock(&gpc->refresh_lock); - if (old_pfn != new_pfn) + if (unmap_old) gpc_unmap_khva(kvm, old_pfn, old_khva); return ret; @@ -366,11 +377,19 @@ int kvm_gpc_activate(struct kvm *kvm, struct gfn_to_pfn_cache *gpc, gpc->vcpu = vcpu; gpc->usage = usage; gpc->valid = false; - gpc->active = true; spin_lock(&kvm->gpc_lock); list_add(&gpc->list, &kvm->gpc_list); spin_unlock(&kvm->gpc_lock); + + /* + * Activate the cache after adding it to the list, a concurrent + * refresh must not establish a mapping until the cache is + * reachable by mmu_notifier events. + */ + write_lock_irq(&gpc->lock); + gpc->active = true; + write_unlock_irq(&gpc->lock); } return kvm_gfn_to_pfn_cache_refresh(kvm, gpc, gpa, len); } @@ -379,12 +398,20 @@ EXPORT_SYMBOL_GPL(kvm_gpc_activate); void kvm_gpc_deactivate(struct kvm *kvm, struct gfn_to_pfn_cache *gpc) { if (gpc->active) { + /* + * Deactivate the cache before removing it from the list, KVM + * must stall mmu_notifier events until all users go away, i.e. + * until gpc->lock is dropped and refresh is guaranteed to fail. + */ + write_lock_irq(&gpc->lock); + gpc->active = false; + write_unlock_irq(&gpc->lock); + spin_lock(&kvm->gpc_lock); list_del(&gpc->list); spin_unlock(&kvm->gpc_lock); kvm_gfn_to_pfn_cache_unmap(kvm, gpc); - gpc->active = false; } } EXPORT_SYMBOL_GPL(kvm_gpc_deactivate); From 03b9a6e18d325ede28c6dc218cedda53969eb41b Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Thu, 20 Oct 2022 16:38:20 +0800 Subject: [PATCH 0127/1823] x86/hyperv: Remove BUG_ON() for kmap_local_page() The commit 154fb14df7a3c ("x86/hyperv: Replace kmap() with kmap_local_page()") keeps the BUG_ON() to check if kmap_local_page() fails. But in fact, kmap_local_page() always returns a valid kernel address and won't return NULL here. It will BUG on its own if it fails. [1] So directly use memcpy_to_page() which creates local mapping to copy. [1]: https://lore.kernel.org/lkml/YztFEyUA48et0yTt@iweiny-mobl/ Suggested-by: Fabio M. De Francesco Suggested-by: Ira Weiny Reviewed-by: Ira Weiny Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20221020083820.2341088-1-zhao1.liu@linux.intel.com Signed-off-by: Wei Liu --- arch/x86/hyperv/hv_init.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c index 29774126e931..d46f70522ddf 100644 --- a/arch/x86/hyperv/hv_init.c +++ b/arch/x86/hyperv/hv_init.c @@ -444,7 +444,7 @@ void __init hyperv_init(void) if (hv_root_partition) { struct page *pg; - void *src, *dst; + void *src; /* * For the root partition, the hypervisor will set up its @@ -459,13 +459,11 @@ void __init hyperv_init(void) wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); pg = vmalloc_to_page(hv_hypercall_pg); - dst = kmap_local_page(pg); src = memremap(hypercall_msr.guest_physical_address << PAGE_SHIFT, PAGE_SIZE, MEMREMAP_WB); - BUG_ON(!(src && dst)); - memcpy(dst, src, HV_HYP_PAGE_SIZE); + BUG_ON(!src); + memcpy_to_page(pg, 0, src, HV_HYP_PAGE_SIZE); memunmap(src); - kunmap_local(dst); } else { hypercall_msr.guest_physical_address = vmalloc_to_pfn(hv_hypercall_pg); wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); From 99632e3dbeb25a0ff86c4af4bba2dcf638624423 Mon Sep 17 00:00:00 2001 From: Jilin Yuan Date: Wed, 19 Oct 2022 20:56:04 +0800 Subject: [PATCH 0128/1823] Drivers: hv: fix repeated words in comments Delete the redundant word 'of'. Signed-off-by: Jilin Yuan Reviewed-by: Michael Kelley Link: https://lore.kernel.org/r/20221019125604.52999-1-yuanjilin@cdjrlc.com Signed-off-by: Wei Liu --- drivers/hv/hv_balloon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index fdf6decacf06..6c127f061f06 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -905,7 +905,7 @@ static unsigned long handle_pg_range(unsigned long pg_start, * We have some residual hot add range * that needs to be hot added; hot add * it now. Hot add a multiple of - * of HA_CHUNK that fully covers the pages + * HA_CHUNK that fully covers the pages * we have. */ size = (has->end_pfn - has->ha_end_pfn); From df3414b0a245f43476061fddd78cee7d6cff797f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Monin?= Date: Thu, 13 Oct 2022 16:26:48 +0200 Subject: [PATCH 0129/1823] USB: serial: option: add Sierra Wireless EM9191 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for the AT and diag ports, similar to other qualcomm SDX55 modems. In QDL mode, the modem uses a different device ID and support is provided by qcserial in commit 11c52d250b34 ("USB: serial: qcserial: add EM9191 QDL support"). T: Bus=08 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 3 Spd=5000 MxCh= 0 D: Ver= 3.20 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 9 #Cfgs= 1 P: Vendor=1199 ProdID=90d3 Rev=00.06 S: Manufacturer=Sierra Wireless, Incorporated S: Product=Sierra Wireless EM9191 S: SerialNumber=xxxxxxxxxxxxxxxx C: #Ifs= 4 Cfg#= 1 Atr=a0 MxPwr=896mA I: If#=0x0 Alt= 0 #EPs= 1 Cls=02(commc) Sub=0e Prot=00 Driver=cdc_mbim I: If#=0x1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim I: If#=0x3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=(none) I: If#=0x4 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=(none) Signed-off-by: Benoît Monin Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold --- drivers/usb/serial/option.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 697683e3fbff..37257a52287d 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -581,6 +581,9 @@ static void option_instat_callback(struct urb *urb); #define OPPO_VENDOR_ID 0x22d9 #define OPPO_PRODUCT_R11 0x276c +/* Sierra Wireless products */ +#define SIERRA_VENDOR_ID 0x1199 +#define SIERRA_PRODUCT_EM9191 0x90d3 /* Device flags */ @@ -2176,6 +2179,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1405, 0xff) }, /* GosunCn GM500 MBIM */ { USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1406, 0xff) }, /* GosunCn GM500 ECM/NCM */ { USB_DEVICE_AND_INTERFACE_INFO(OPPO_VENDOR_ID, OPPO_PRODUCT_R11, 0xff, 0xff, 0x30) }, + { USB_DEVICE_AND_INTERFACE_INFO(SIERRA_VENDOR_ID, SIERRA_PRODUCT_EM9191, 0xff, 0xff, 0x30) }, + { USB_DEVICE_AND_INTERFACE_INFO(SIERRA_VENDOR_ID, SIERRA_PRODUCT_EM9191, 0xff, 0, 0) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); From 7f57f8165cb6d2c206e2b9ada53b9e2d6d8af42f Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Tue, 25 Oct 2022 14:06:48 +0800 Subject: [PATCH 0130/1823] af_key: Fix send_acquire race with pfkey_register The function pfkey_send_acquire may race with pfkey_register (which could even be in a different name space). This may result in a buffer overrun. Allocating the maximum amount of memory that could be used prevents this. Reported-by: syzbot+1e9af9185d8850e2c2fa@syzkaller.appspotmail.com Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Herbert Xu Reviewed-by: Sabrina Dubroca Reviewed-by: Eric Dumazet Signed-off-by: Steffen Klassert --- net/key/af_key.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/net/key/af_key.c b/net/key/af_key.c index c85df5b958d2..213287814328 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -2905,7 +2905,7 @@ static int count_ah_combs(const struct xfrm_tmpl *t) break; if (!aalg->pfkey_supported) continue; - if (aalg_tmpl_set(t, aalg) && aalg->available) + if (aalg_tmpl_set(t, aalg)) sz += sizeof(struct sadb_comb); } return sz + sizeof(struct sadb_prop); @@ -2923,7 +2923,7 @@ static int count_esp_combs(const struct xfrm_tmpl *t) if (!ealg->pfkey_supported) continue; - if (!(ealg_tmpl_set(t, ealg) && ealg->available)) + if (!(ealg_tmpl_set(t, ealg))) continue; for (k = 1; ; k++) { @@ -2934,16 +2934,17 @@ static int count_esp_combs(const struct xfrm_tmpl *t) if (!aalg->pfkey_supported) continue; - if (aalg_tmpl_set(t, aalg) && aalg->available) + if (aalg_tmpl_set(t, aalg)) sz += sizeof(struct sadb_comb); } } return sz + sizeof(struct sadb_prop); } -static void dump_ah_combs(struct sk_buff *skb, const struct xfrm_tmpl *t) +static int dump_ah_combs(struct sk_buff *skb, const struct xfrm_tmpl *t) { struct sadb_prop *p; + int sz = 0; int i; p = skb_put(skb, sizeof(struct sadb_prop)); @@ -2971,13 +2972,17 @@ static void dump_ah_combs(struct sk_buff *skb, const struct xfrm_tmpl *t) c->sadb_comb_soft_addtime = 20*60*60; c->sadb_comb_hard_usetime = 8*60*60; c->sadb_comb_soft_usetime = 7*60*60; + sz += sizeof(*c); } } + + return sz + sizeof(*p); } -static void dump_esp_combs(struct sk_buff *skb, const struct xfrm_tmpl *t) +static int dump_esp_combs(struct sk_buff *skb, const struct xfrm_tmpl *t) { struct sadb_prop *p; + int sz = 0; int i, k; p = skb_put(skb, sizeof(struct sadb_prop)); @@ -3019,8 +3024,11 @@ static void dump_esp_combs(struct sk_buff *skb, const struct xfrm_tmpl *t) c->sadb_comb_soft_addtime = 20*60*60; c->sadb_comb_hard_usetime = 8*60*60; c->sadb_comb_soft_usetime = 7*60*60; + sz += sizeof(*c); } } + + return sz + sizeof(*p); } static int key_notify_policy_expire(struct xfrm_policy *xp, const struct km_event *c) @@ -3150,6 +3158,7 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct struct sadb_x_sec_ctx *sec_ctx; struct xfrm_sec_ctx *xfrm_ctx; int ctx_size = 0; + int alg_size = 0; sockaddr_size = pfkey_sockaddr_size(x->props.family); if (!sockaddr_size) @@ -3161,16 +3170,16 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct sizeof(struct sadb_x_policy); if (x->id.proto == IPPROTO_AH) - size += count_ah_combs(t); + alg_size = count_ah_combs(t); else if (x->id.proto == IPPROTO_ESP) - size += count_esp_combs(t); + alg_size = count_esp_combs(t); if ((xfrm_ctx = x->security)) { ctx_size = PFKEY_ALIGN8(xfrm_ctx->ctx_len); size += sizeof(struct sadb_x_sec_ctx) + ctx_size; } - skb = alloc_skb(size + 16, GFP_ATOMIC); + skb = alloc_skb(size + alg_size + 16, GFP_ATOMIC); if (skb == NULL) return -ENOMEM; @@ -3224,10 +3233,13 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct pol->sadb_x_policy_priority = xp->priority; /* Set sadb_comb's. */ + alg_size = 0; if (x->id.proto == IPPROTO_AH) - dump_ah_combs(skb, t); + alg_size = dump_ah_combs(skb, t); else if (x->id.proto == IPPROTO_ESP) - dump_esp_combs(skb, t); + alg_size = dump_esp_combs(skb, t); + + hdr->sadb_msg_len += alg_size / 8; /* security context */ if (xfrm_ctx) { From 5c1df62ca65936139a4b008561110d9fc285c58a Mon Sep 17 00:00:00 2001 From: Stefan Hansson Date: Mon, 17 Oct 2022 17:01:14 +0200 Subject: [PATCH 0131/1823] kbuild: use POSIX-compatible grep option --file is a GNU extension to grep which is not available in all implementations (such as BusyBox). Use the -f option instead which is eqvuialent according to the GNU grep manpage[1] and is present in POSIX[2]. [1] https://www.gnu.org/software/grep/manual/grep.html [2] https://pubs.opengroup.org/onlinepubs/9699919799/utilities/grep.html Signed-off-by: Stefan Hansson Signed-off-by: Masahiro Yamada --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d148a55bfd0f..e90bb2b38607 100644 --- a/Makefile +++ b/Makefile @@ -1218,7 +1218,7 @@ quiet_cmd_ar_vmlinux.a = AR $@ cmd_ar_vmlinux.a = \ rm -f $@; \ $(AR) cDPrST $@ $(KBUILD_VMLINUX_OBJS); \ - $(AR) mPiT $$($(AR) t $@ | head -n1) $@ $$($(AR) t $@ | grep -F --file=$(srctree)/scripts/head-object-list.txt) + $(AR) mPiT $$($(AR) t $@ | head -n1) $@ $$($(AR) t $@ | grep -F -f $(srctree)/scripts/head-object-list.txt) targets += vmlinux.a vmlinux.a: $(KBUILD_VMLINUX_OBJS) scripts/head-object-list.txt autoksyms_recursive FORCE From 114ff6fe6cfbe81659f9e517d0b25f53db5dfc5d Mon Sep 17 00:00:00 2001 From: Dan Li Date: Thu, 20 Oct 2022 03:38:23 -0700 Subject: [PATCH 0132/1823] Documentation: kbuild: Add description of git for reproducible builds The status of git will affect the final compilation result, add it to the documentation of reproducible builds. Signed-off-by: Dan Li Signed-off-by: Masahiro Yamada --- Documentation/kbuild/reproducible-builds.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/kbuild/reproducible-builds.rst b/Documentation/kbuild/reproducible-builds.rst index 071f0151a7a4..f2dcc39044e6 100644 --- a/Documentation/kbuild/reproducible-builds.rst +++ b/Documentation/kbuild/reproducible-builds.rst @@ -119,6 +119,16 @@ To avoid this, you can make the vDSO different for different kernel versions by including an arbitrary string of "salt" in it. This is specified by the Kconfig symbol ``CONFIG_BUILD_SALT``. +Git +--- + +Uncommitted changes or different commit ids in git can also lead +to different compilation results. For example, after executing +``git reset HEAD^``, even if the code is the same, the +``include/config/kernel.release`` generated during compilation +will be different, which will eventually lead to binary differences. +See ``scripts/setlocalversion`` for details. + .. _KBUILD_BUILD_TIMESTAMP: kbuild.html#kbuild-build-timestamp .. _KBUILD_BUILD_USER and KBUILD_BUILD_HOST: kbuild.html#kbuild-build-user-kbuild-build-host .. _KCFLAGS: kbuild.html#kcflags From 3b1e0dd2dc8a280b1e89c0df6d38cd28768575a5 Mon Sep 17 00:00:00 2001 From: Will McVicker Date: Tue, 25 Oct 2022 13:17:44 -0700 Subject: [PATCH 0133/1823] kbuild: fix typo in modpost Commit f73edc8951b2 ("kbuild: unify two modpost invocations") introduced a typo (moudle.symvers-if-present) which results in the kernel's Module.symvers to not be included as a prerequisite for $(KBUILD_EXTMOD)/Module.symvers. Fix the typo to restore the intended functionality. Fixes: f73edc8951b2 ("kbuild: unify two modpost invocations") Signed-off-by: Will McVicker Signed-off-by: Masahiro Yamada --- scripts/Makefile.modpost | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost index 8489a3402eb8..e41dee64d429 100644 --- a/scripts/Makefile.modpost +++ b/scripts/Makefile.modpost @@ -122,7 +122,7 @@ quiet_cmd_modpost = MODPOST $@ sed 's/ko$$/o/' $(or $(modorder-if-needed), /dev/null) | $(MODPOST) $(modpost-args) -T - $(vmlinux.o-if-present) targets += $(output-symdump) -$(output-symdump): $(modorder-if-needed) $(vmlinux.o-if-present) $(moudle.symvers-if-present) $(MODPOST) FORCE +$(output-symdump): $(modorder-if-needed) $(vmlinux.o-if-present) $(module.symvers-if-present) $(MODPOST) FORCE $(call if_changed,modpost) __modpost: $(output-symdump) From b6bcdc9f6b8321e4471ff45413b6410e16762a8d Mon Sep 17 00:00:00 2001 From: Ryan Roberts Date: Thu, 27 Oct 2022 13:09:45 +0100 Subject: [PATCH 0134/1823] KVM: arm64: Fix bad dereference on MTE-enabled systems enter_exception64() performs an MTE check, which involves dereferencing vcpu->kvm. While vcpu has already been fixed up to be a HYP VA pointer, kvm is still a pointer in the kernel VA space. This only affects nVHE configurations with MTE enabled, as in other cases, the pointer is either valid (VHE) or not dereferenced (!MTE). Fix this by first converting kvm to a HYP VA pointer. Fixes: ea7fc1bb1cd1 ("KVM: arm64: Introduce MTE VM feature") Signed-off-by: Ryan Roberts Reviewed-by: Steven Price [maz: commit message tidy-up] Signed-off-by: Marc Zyngier Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20221027120945.29679-1-ryan.roberts@arm.com --- arch/arm64/kvm/hyp/exception.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kvm/hyp/exception.c b/arch/arm64/kvm/hyp/exception.c index b7557b25ed56..791d3de76771 100644 --- a/arch/arm64/kvm/hyp/exception.c +++ b/arch/arm64/kvm/hyp/exception.c @@ -13,6 +13,7 @@ #include #include #include +#include #if !defined (__KVM_NVHE_HYPERVISOR__) && !defined (__KVM_VHE_HYPERVISOR__) #error Hypervisor code only! @@ -115,7 +116,7 @@ static void enter_exception64(struct kvm_vcpu *vcpu, unsigned long target_mode, new |= (old & PSR_C_BIT); new |= (old & PSR_V_BIT); - if (kvm_has_mte(vcpu->kvm)) + if (kvm_has_mte(kern_hyp_va(vcpu->kvm))) new |= PSR_TCO_BIT; new |= (old & PSR_DIT_BIT); From 121affdf8a940555ceef6ab10a709030e52a4f91 Mon Sep 17 00:00:00 2001 From: Yushan Zhou Date: Tue, 18 Oct 2022 12:07:08 +0800 Subject: [PATCH 0135/1823] nfs: Remove redundant null checks before kfree Fix the following coccicheck warning: fs/nfs/dir.c:2494:2-7: WARNING: NULL check before some freeing functions is not needed. Signed-off-by: Yushan Zhou Signed-off-by: Anna Schumaker --- fs/nfs/dir.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 58036f657126..f594dac436a7 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -2489,9 +2489,8 @@ int nfs_unlink(struct inode *dir, struct dentry *dentry) spin_unlock(&dentry->d_lock); goto out; } - if (dentry->d_fsdata) - /* old devname */ - kfree(dentry->d_fsdata); + /* old devname */ + kfree(dentry->d_fsdata); dentry->d_fsdata = NFS_FSDATA_BLOCKED; spin_unlock(&dentry->d_lock); From cf0d7e7f4520814f45e1313872ad5777ed504004 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Sun, 16 Oct 2022 21:36:50 -0700 Subject: [PATCH 0136/1823] NFS: Avoid memcpy() run-time warning for struct sockaddr overflows The 'nfs_server' and 'mount_server' structures include a union of 'struct sockaddr' (with the older 16 bytes max address size) and 'struct sockaddr_storage' which is large enough to hold all the supported sa_family types (128 bytes max size). The runtime memcpy() buffer overflow checker is seeing attempts to write beyond the 16 bytes as an overflow, but the actual expected size is that of 'struct sockaddr_storage'. Plumb the use of 'struct sockaddr_storage' more completely through-out NFS, which results in adjusting the memcpy() buffers to the correct union members. Avoids this false positive run-time warning under CONFIG_FORTIFY_SOURCE: memcpy: detected field-spanning write (size 28) of single field "&ctx->nfs_server.address" at fs/nfs/namespace.c:178 (size 16) Reported-by: kernel test robot Link: https://lore.kernel.org/all/202210110948.26b43120-yujie.liu@intel.com Cc: Trond Myklebust Cc: Anna Schumaker Cc: linux-nfs@vger.kernel.org Signed-off-by: Kees Cook Signed-off-by: Anna Schumaker --- fs/nfs/client.c | 4 ++-- fs/nfs/dns_resolve.c | 7 ++++--- fs/nfs/dns_resolve.h | 2 +- fs/nfs/fs_context.c | 14 +++++++------- fs/nfs/internal.h | 14 +++++++------- fs/nfs/mount_clnt.c | 4 ++-- fs/nfs/namespace.c | 2 +- fs/nfs/nfs3client.c | 4 ++-- fs/nfs/nfs4_fs.h | 2 +- fs/nfs/nfs4client.c | 18 +++++++++--------- fs/nfs/nfs4namespace.c | 16 ++++++++-------- fs/nfs/nfs4proc.c | 4 ++-- fs/nfs/pnfs_nfs.c | 6 +++--- fs/nfs/super.c | 5 ++--- 14 files changed, 51 insertions(+), 51 deletions(-) diff --git a/fs/nfs/client.c b/fs/nfs/client.c index da8da5cdbbc1..f50e025ae406 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -280,7 +280,7 @@ EXPORT_SYMBOL_GPL(nfs_put_client); static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *data) { struct nfs_client *clp; - const struct sockaddr *sap = data->addr; + const struct sockaddr *sap = (struct sockaddr *)data->addr; struct nfs_net *nn = net_generic(data->net, nfs_net_id); int error; @@ -666,7 +666,7 @@ static int nfs_init_server(struct nfs_server *server, struct rpc_timeout timeparms; struct nfs_client_initdata cl_init = { .hostname = ctx->nfs_server.hostname, - .addr = (const struct sockaddr *)&ctx->nfs_server.address, + .addr = &ctx->nfs_server._address, .addrlen = ctx->nfs_server.addrlen, .nfs_mod = ctx->nfs_mod, .proto = ctx->nfs_server.protocol, diff --git a/fs/nfs/dns_resolve.c b/fs/nfs/dns_resolve.c index e87d500ad95a..6603b5cee029 100644 --- a/fs/nfs/dns_resolve.c +++ b/fs/nfs/dns_resolve.c @@ -16,8 +16,9 @@ #include "dns_resolve.h" ssize_t nfs_dns_resolve_name(struct net *net, char *name, size_t namelen, - struct sockaddr *sa, size_t salen) + struct sockaddr_storage *ss, size_t salen) { + struct sockaddr *sa = (struct sockaddr *)ss; ssize_t ret; char *ip_addr = NULL; int ip_len; @@ -341,7 +342,7 @@ out: } ssize_t nfs_dns_resolve_name(struct net *net, char *name, - size_t namelen, struct sockaddr *sa, size_t salen) + size_t namelen, struct sockaddr_storage *ss, size_t salen) { struct nfs_dns_ent key = { .hostname = name, @@ -354,7 +355,7 @@ ssize_t nfs_dns_resolve_name(struct net *net, char *name, ret = do_cache_lookup_wait(nn->nfs_dns_resolve, &key, &item); if (ret == 0) { if (salen >= item->addrlen) { - memcpy(sa, &item->addr, item->addrlen); + memcpy(ss, &item->addr, item->addrlen); ret = item->addrlen; } else ret = -EOVERFLOW; diff --git a/fs/nfs/dns_resolve.h b/fs/nfs/dns_resolve.h index 576ff4b54c82..fe3b172c4de1 100644 --- a/fs/nfs/dns_resolve.h +++ b/fs/nfs/dns_resolve.h @@ -32,6 +32,6 @@ extern void nfs_dns_resolver_cache_destroy(struct net *net); #endif extern ssize_t nfs_dns_resolve_name(struct net *net, char *name, - size_t namelen, struct sockaddr *sa, size_t salen); + size_t namelen, struct sockaddr_storage *sa, size_t salen); #endif diff --git a/fs/nfs/fs_context.c b/fs/nfs/fs_context.c index 4da701fd1424..09833ec102fc 100644 --- a/fs/nfs/fs_context.c +++ b/fs/nfs/fs_context.c @@ -273,9 +273,9 @@ static const struct constant_table nfs_secflavor_tokens[] = { * Address family must be initialized, and address must not be * the ANY address for that family. */ -static int nfs_verify_server_address(struct sockaddr *addr) +static int nfs_verify_server_address(struct sockaddr_storage *addr) { - switch (addr->sa_family) { + switch (addr->ss_family) { case AF_INET: { struct sockaddr_in *sa = (struct sockaddr_in *)addr; return sa->sin_addr.s_addr != htonl(INADDR_ANY); @@ -969,7 +969,7 @@ static int nfs23_parse_monolithic(struct fs_context *fc, { struct nfs_fs_context *ctx = nfs_fc2context(fc); struct nfs_fh *mntfh = ctx->mntfh; - struct sockaddr *sap = (struct sockaddr *)&ctx->nfs_server.address; + struct sockaddr_storage *sap = &ctx->nfs_server._address; int extra_flags = NFS_MOUNT_LEGACY_INTERFACE; int ret; @@ -1044,7 +1044,7 @@ static int nfs23_parse_monolithic(struct fs_context *fc, memcpy(sap, &data->addr, sizeof(data->addr)); ctx->nfs_server.addrlen = sizeof(data->addr); ctx->nfs_server.port = ntohs(data->addr.sin_port); - if (sap->sa_family != AF_INET || + if (sap->ss_family != AF_INET || !nfs_verify_server_address(sap)) goto out_no_address; @@ -1200,7 +1200,7 @@ static int nfs4_parse_monolithic(struct fs_context *fc, struct nfs4_mount_data *data) { struct nfs_fs_context *ctx = nfs_fc2context(fc); - struct sockaddr *sap = (struct sockaddr *)&ctx->nfs_server.address; + struct sockaddr_storage *sap = &ctx->nfs_server._address; int ret; char *c; @@ -1314,7 +1314,7 @@ static int nfs_fs_context_validate(struct fs_context *fc) { struct nfs_fs_context *ctx = nfs_fc2context(fc); struct nfs_subversion *nfs_mod; - struct sockaddr *sap = (struct sockaddr *)&ctx->nfs_server.address; + struct sockaddr_storage *sap = &ctx->nfs_server._address; int max_namelen = PAGE_SIZE; int max_pathlen = NFS_MAXPATHLEN; int port = 0; @@ -1540,7 +1540,7 @@ static int nfs_init_fs_context(struct fs_context *fc) ctx->version = nfss->nfs_client->rpc_ops->version; ctx->minorversion = nfss->nfs_client->cl_minorversion; - memcpy(&ctx->nfs_server.address, &nfss->nfs_client->cl_addr, + memcpy(&ctx->nfs_server._address, &nfss->nfs_client->cl_addr, ctx->nfs_server.addrlen); if (fc->net_ns != net) { diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index d914d609b85b..647fc3f547cb 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -69,7 +69,7 @@ static inline fmode_t flags_to_mode(int flags) struct nfs_client_initdata { unsigned long init_flags; const char *hostname; /* Hostname of the server */ - const struct sockaddr *addr; /* Address of the server */ + const struct sockaddr_storage *addr; /* Address of the server */ const char *nodename; /* Hostname of the client */ const char *ip_addr; /* IP address of the client */ size_t addrlen; @@ -180,7 +180,7 @@ static inline struct nfs_fs_context *nfs_fc2context(const struct fs_context *fc) /* mount_clnt.c */ struct nfs_mount_request { - struct sockaddr *sap; + struct sockaddr_storage *sap; size_t salen; char *hostname; char *dirpath; @@ -223,7 +223,7 @@ extern void nfs4_server_set_init_caps(struct nfs_server *); extern struct nfs_server *nfs4_create_server(struct fs_context *); extern struct nfs_server *nfs4_create_referral_server(struct fs_context *); extern int nfs4_update_server(struct nfs_server *server, const char *hostname, - struct sockaddr *sap, size_t salen, + struct sockaddr_storage *sap, size_t salen, struct net *net); extern void nfs_free_server(struct nfs_server *server); extern struct nfs_server *nfs_clone_server(struct nfs_server *, @@ -235,7 +235,7 @@ extern int nfs_client_init_status(const struct nfs_client *clp); extern int nfs_wait_client_init_complete(const struct nfs_client *clp); extern void nfs_mark_client_ready(struct nfs_client *clp, int state); extern struct nfs_client *nfs4_set_ds_client(struct nfs_server *mds_srv, - const struct sockaddr *ds_addr, + const struct sockaddr_storage *ds_addr, int ds_addrlen, int ds_proto, unsigned int ds_timeo, unsigned int ds_retrans, @@ -243,7 +243,7 @@ extern struct nfs_client *nfs4_set_ds_client(struct nfs_server *mds_srv, extern struct rpc_clnt *nfs4_find_or_create_ds_client(struct nfs_client *, struct inode *); extern struct nfs_client *nfs3_set_ds_client(struct nfs_server *mds_srv, - const struct sockaddr *ds_addr, int ds_addrlen, + const struct sockaddr_storage *ds_addr, int ds_addrlen, int ds_proto, unsigned int ds_timeo, unsigned int ds_retrans); #ifdef CONFIG_PROC_FS @@ -894,13 +894,13 @@ static inline bool nfs_error_is_fatal_on_server(int err) * Select between a default port value and a user-specified port value. * If a zero value is set, then autobind will be used. */ -static inline void nfs_set_port(struct sockaddr *sap, int *port, +static inline void nfs_set_port(struct sockaddr_storage *sap, int *port, const unsigned short default_port) { if (*port == NFS_UNSPEC_PORT) *port = default_port; - rpc_set_port(sap, *port); + rpc_set_port((struct sockaddr *)sap, *port); } struct nfs_direct_req { diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c index c5e3b6b3366a..68e76b626371 100644 --- a/fs/nfs/mount_clnt.c +++ b/fs/nfs/mount_clnt.c @@ -158,7 +158,7 @@ int nfs_mount(struct nfs_mount_request *info, int timeo, int retrans) struct rpc_create_args args = { .net = info->net, .protocol = info->protocol, - .address = info->sap, + .address = (struct sockaddr *)info->sap, .addrsize = info->salen, .timeout = &mnt_timeout, .servername = info->hostname, @@ -245,7 +245,7 @@ void nfs_umount(const struct nfs_mount_request *info) struct rpc_create_args args = { .net = info->net, .protocol = IPPROTO_UDP, - .address = info->sap, + .address = (struct sockaddr *)info->sap, .addrsize = info->salen, .timeout = &nfs_umnt_timeout, .servername = info->hostname, diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c index 3295af4110f1..2f336ace7555 100644 --- a/fs/nfs/namespace.c +++ b/fs/nfs/namespace.c @@ -175,7 +175,7 @@ struct vfsmount *nfs_d_automount(struct path *path) } /* for submounts we want the same server; referrals will reassign */ - memcpy(&ctx->nfs_server.address, &client->cl_addr, client->cl_addrlen); + memcpy(&ctx->nfs_server._address, &client->cl_addr, client->cl_addrlen); ctx->nfs_server.addrlen = client->cl_addrlen; ctx->nfs_server.port = server->port; diff --git a/fs/nfs/nfs3client.c b/fs/nfs/nfs3client.c index b49359afac88..669cda757a5c 100644 --- a/fs/nfs/nfs3client.c +++ b/fs/nfs/nfs3client.c @@ -78,7 +78,7 @@ struct nfs_server *nfs3_clone_server(struct nfs_server *source, * the MDS. */ struct nfs_client *nfs3_set_ds_client(struct nfs_server *mds_srv, - const struct sockaddr *ds_addr, int ds_addrlen, + const struct sockaddr_storage *ds_addr, int ds_addrlen, int ds_proto, unsigned int ds_timeo, unsigned int ds_retrans) { struct rpc_timeout ds_timeout; @@ -98,7 +98,7 @@ struct nfs_client *nfs3_set_ds_client(struct nfs_server *mds_srv, char buf[INET6_ADDRSTRLEN + 1]; /* fake a hostname because lockd wants it */ - if (rpc_ntop(ds_addr, buf, sizeof(buf)) <= 0) + if (rpc_ntop((struct sockaddr *)ds_addr, buf, sizeof(buf)) <= 0) return ERR_PTR(-EINVAL); cl_init.hostname = buf; diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 400a71e75238..cfef738d765e 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -281,7 +281,7 @@ struct rpc_clnt *nfs4_negotiate_security(struct rpc_clnt *, struct inode *, int nfs4_submount(struct fs_context *, struct nfs_server *); int nfs4_replace_transport(struct nfs_server *server, const struct nfs4_fs_locations *locations); -size_t nfs_parse_server_name(char *string, size_t len, struct sockaddr *sa, +size_t nfs_parse_server_name(char *string, size_t len, struct sockaddr_storage *ss, size_t salen, struct net *net, int port); /* nfs4proc.c */ extern int nfs4_handle_exception(struct nfs_server *, int, struct nfs4_exception *); diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index 7a5162afa5c0..3b9b53481309 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c @@ -889,7 +889,7 @@ nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr, */ static int nfs4_set_client(struct nfs_server *server, const char *hostname, - const struct sockaddr *addr, + const struct sockaddr_storage *addr, const size_t addrlen, const char *ip_addr, int proto, const struct rpc_timeout *timeparms, @@ -924,7 +924,7 @@ static int nfs4_set_client(struct nfs_server *server, __set_bit(NFS_CS_MIGRATION, &cl_init.init_flags); if (test_bit(NFS_MIG_TSM_POSSIBLE, &server->mig_status)) __set_bit(NFS_CS_TSM_POSSIBLE, &cl_init.init_flags); - server->port = rpc_get_port(addr); + server->port = rpc_get_port((struct sockaddr *)addr); /* Allocate or find a client reference we can use */ clp = nfs_get_client(&cl_init); @@ -960,7 +960,7 @@ static int nfs4_set_client(struct nfs_server *server, * the MDS. */ struct nfs_client *nfs4_set_ds_client(struct nfs_server *mds_srv, - const struct sockaddr *ds_addr, int ds_addrlen, + const struct sockaddr_storage *ds_addr, int ds_addrlen, int ds_proto, unsigned int ds_timeo, unsigned int ds_retrans, u32 minor_version) { @@ -980,7 +980,7 @@ struct nfs_client *nfs4_set_ds_client(struct nfs_server *mds_srv, }; char buf[INET6_ADDRSTRLEN + 1]; - if (rpc_ntop(ds_addr, buf, sizeof(buf)) <= 0) + if (rpc_ntop((struct sockaddr *)ds_addr, buf, sizeof(buf)) <= 0) return ERR_PTR(-EINVAL); cl_init.hostname = buf; @@ -1148,7 +1148,7 @@ static int nfs4_init_server(struct nfs_server *server, struct fs_context *fc) /* Get a client record */ error = nfs4_set_client(server, ctx->nfs_server.hostname, - &ctx->nfs_server.address, + &ctx->nfs_server._address, ctx->nfs_server.addrlen, ctx->client_address, ctx->nfs_server.protocol, @@ -1238,7 +1238,7 @@ struct nfs_server *nfs4_create_referral_server(struct fs_context *fc) rpc_set_port(&ctx->nfs_server.address, NFS_RDMA_PORT); error = nfs4_set_client(server, ctx->nfs_server.hostname, - &ctx->nfs_server.address, + &ctx->nfs_server._address, ctx->nfs_server.addrlen, parent_client->cl_ipaddr, XPRT_TRANSPORT_RDMA, @@ -1254,7 +1254,7 @@ struct nfs_server *nfs4_create_referral_server(struct fs_context *fc) rpc_set_port(&ctx->nfs_server.address, NFS_PORT); error = nfs4_set_client(server, ctx->nfs_server.hostname, - &ctx->nfs_server.address, + &ctx->nfs_server._address, ctx->nfs_server.addrlen, parent_client->cl_ipaddr, XPRT_TRANSPORT_TCP, @@ -1303,14 +1303,14 @@ error: * Returns zero on success, or a negative errno value. */ int nfs4_update_server(struct nfs_server *server, const char *hostname, - struct sockaddr *sap, size_t salen, struct net *net) + struct sockaddr_storage *sap, size_t salen, struct net *net) { struct nfs_client *clp = server->nfs_client; struct rpc_clnt *clnt = server->client; struct xprt_create xargs = { .ident = clp->cl_proto, .net = net, - .dstaddr = sap, + .dstaddr = (struct sockaddr *)sap, .addrlen = salen, .servername = hostname, }; diff --git a/fs/nfs/nfs4namespace.c b/fs/nfs/nfs4namespace.c index f2dbf904c598..9a98595bb160 100644 --- a/fs/nfs/nfs4namespace.c +++ b/fs/nfs/nfs4namespace.c @@ -164,16 +164,17 @@ static int nfs4_validate_fspath(struct dentry *dentry, return 0; } -size_t nfs_parse_server_name(char *string, size_t len, struct sockaddr *sa, +size_t nfs_parse_server_name(char *string, size_t len, struct sockaddr_storage *ss, size_t salen, struct net *net, int port) { + struct sockaddr *sa = (struct sockaddr *)ss; ssize_t ret; ret = rpc_pton(net, string, len, sa, salen); if (ret == 0) { ret = rpc_uaddr2sockaddr(net, string, len, sa, salen); if (ret == 0) { - ret = nfs_dns_resolve_name(net, string, len, sa, salen); + ret = nfs_dns_resolve_name(net, string, len, ss, salen); if (ret < 0) ret = 0; } @@ -331,7 +332,7 @@ static int try_location(struct fs_context *fc, ctx->nfs_server.addrlen = nfs_parse_server_name(buf->data, buf->len, - &ctx->nfs_server.address, + &ctx->nfs_server._address, sizeof(ctx->nfs_server._address), fc->net_ns, 0); if (ctx->nfs_server.addrlen == 0) @@ -483,14 +484,13 @@ static int nfs4_try_replacing_one_location(struct nfs_server *server, char *page, char *page2, const struct nfs4_fs_location *location) { - const size_t addr_bufsize = sizeof(struct sockaddr_storage); struct net *net = rpc_net_ns(server->client); - struct sockaddr *sap; + struct sockaddr_storage *sap; unsigned int s; size_t salen; int error; - sap = kmalloc(addr_bufsize, GFP_KERNEL); + sap = kmalloc(sizeof(*sap), GFP_KERNEL); if (sap == NULL) return -ENOMEM; @@ -506,10 +506,10 @@ static int nfs4_try_replacing_one_location(struct nfs_server *server, continue; salen = nfs_parse_server_name(buf->data, buf->len, - sap, addr_bufsize, net, 0); + sap, sizeof(*sap), net, 0); if (salen == 0) continue; - rpc_set_port(sap, NFS_PORT); + rpc_set_port((struct sockaddr *)sap, NFS_PORT); error = -ENOMEM; hostname = kmemdup_nul(buf->data, buf->len, GFP_KERNEL); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index e2efcd26336c..4c4df7f213b6 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -3951,7 +3951,7 @@ static void test_fs_location_for_trunking(struct nfs4_fs_location *location, for (i = 0; i < location->nservers; i++) { struct nfs4_string *srv_loc = &location->servers[i]; - struct sockaddr addr; + struct sockaddr_storage addr; size_t addrlen; struct xprt_create xprt_args = { .ident = 0, @@ -3974,7 +3974,7 @@ static void test_fs_location_for_trunking(struct nfs4_fs_location *location, clp->cl_net, server->port); if (!addrlen) return; - xprt_args.dstaddr = &addr; + xprt_args.dstaddr = (struct sockaddr *)&addr; xprt_args.addrlen = addrlen; servername = kmalloc(srv_loc->len + 1, GFP_KERNEL); if (!servername) diff --git a/fs/nfs/pnfs_nfs.c b/fs/nfs/pnfs_nfs.c index 987c88ddeaf0..5d035dd2d7bf 100644 --- a/fs/nfs/pnfs_nfs.c +++ b/fs/nfs/pnfs_nfs.c @@ -821,7 +821,7 @@ static void nfs4_clear_ds_conn_bit(struct nfs4_pnfs_ds *ds) static struct nfs_client *(*get_v3_ds_connect)( struct nfs_server *mds_srv, - const struct sockaddr *ds_addr, + const struct sockaddr_storage *ds_addr, int ds_addrlen, int ds_proto, unsigned int ds_timeo, @@ -882,7 +882,7 @@ static int _nfs4_pnfs_v3_ds_connect(struct nfs_server *mds_srv, continue; } clp = get_v3_ds_connect(mds_srv, - (struct sockaddr *)&da->da_addr, + &da->da_addr, da->da_addrlen, da->da_transport, timeo, retrans); if (IS_ERR(clp)) @@ -951,7 +951,7 @@ static int _nfs4_pnfs_v4_ds_connect(struct nfs_server *mds_srv, put_cred(xprtdata.cred); } else { clp = nfs4_set_ds_client(mds_srv, - (struct sockaddr *)&da->da_addr, + &da->da_addr, da->da_addrlen, da->da_transport, timeo, retrans, minor_version); diff --git a/fs/nfs/super.c b/fs/nfs/super.c index ee66ffdb985e..05ae23657527 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -822,8 +822,7 @@ static int nfs_request_mount(struct fs_context *fc, { struct nfs_fs_context *ctx = nfs_fc2context(fc); struct nfs_mount_request request = { - .sap = (struct sockaddr *) - &ctx->mount_server.address, + .sap = &ctx->mount_server._address, .dirpath = ctx->nfs_server.export_path, .protocol = ctx->mount_server.protocol, .fh = root_fh, @@ -854,7 +853,7 @@ static int nfs_request_mount(struct fs_context *fc, * Construct the mount server's address. */ if (ctx->mount_server.address.sa_family == AF_UNSPEC) { - memcpy(request.sap, &ctx->nfs_server.address, + memcpy(request.sap, &ctx->nfs_server._address, ctx->nfs_server.addrlen); ctx->mount_server.addrlen = ctx->nfs_server.addrlen; } From 1ba04394e028ea8b45d92685cc0d6ab582cf7647 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sun, 16 Oct 2022 14:44:31 -0400 Subject: [PATCH 0137/1823] NFSv4: Fix a potential state reclaim deadlock If the server reboots while we are engaged in a delegation return, and there is a pNFS layout with return-on-close set, then the current code can end up deadlocking in pnfs_roc() when nfs_inode_set_delegation() tries to return the old delegation. Now that delegreturn actually uses its own copy of the stateid, it should be safe to just always update the delegation stateid in place. Fixes: 078000d02d57 ("pNFS: We want return-on-close to complete when evicting the inode") Signed-off-by: Trond Myklebust Signed-off-by: Anna Schumaker --- fs/nfs/delegation.c | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 5c97cad741a7..ead8a0e06abf 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -228,8 +228,7 @@ again: * */ void nfs_inode_reclaim_delegation(struct inode *inode, const struct cred *cred, - fmode_t type, - const nfs4_stateid *stateid, + fmode_t type, const nfs4_stateid *stateid, unsigned long pagemod_limit) { struct nfs_delegation *delegation; @@ -239,25 +238,24 @@ void nfs_inode_reclaim_delegation(struct inode *inode, const struct cred *cred, delegation = rcu_dereference(NFS_I(inode)->delegation); if (delegation != NULL) { spin_lock(&delegation->lock); - if (nfs4_is_valid_delegation(delegation, 0)) { - nfs4_stateid_copy(&delegation->stateid, stateid); - delegation->type = type; - delegation->pagemod_limit = pagemod_limit; - oldcred = delegation->cred; - delegation->cred = get_cred(cred); - clear_bit(NFS_DELEGATION_NEED_RECLAIM, - &delegation->flags); - spin_unlock(&delegation->lock); - rcu_read_unlock(); - put_cred(oldcred); - trace_nfs4_reclaim_delegation(inode, type); - return; - } - /* We appear to have raced with a delegation return. */ + nfs4_stateid_copy(&delegation->stateid, stateid); + delegation->type = type; + delegation->pagemod_limit = pagemod_limit; + oldcred = delegation->cred; + delegation->cred = get_cred(cred); + clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); + if (test_and_clear_bit(NFS_DELEGATION_REVOKED, + &delegation->flags)) + atomic_long_inc(&nfs_active_delegations); spin_unlock(&delegation->lock); + rcu_read_unlock(); + put_cred(oldcred); + trace_nfs4_reclaim_delegation(inode, type); + } else { + rcu_read_unlock(); + nfs_inode_set_delegation(inode, cred, type, stateid, + pagemod_limit); } - rcu_read_unlock(); - nfs_inode_set_delegation(inode, cred, type, stateid, pagemod_limit); } static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) From 5d917cba3201e5c25059df96c29252fd99c4f6a7 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sun, 16 Oct 2022 14:44:32 -0400 Subject: [PATCH 0138/1823] NFSv4.1: Handle RECLAIM_COMPLETE trunking errors If RECLAIM_COMPLETE sets the NFS4CLNT_BIND_CONN_TO_SESSION flag, then we need to loop back in order to handle it. Fixes: 0048fdd06614 ("NFSv4.1: RECLAIM_COMPLETE must handle NFS4ERR_CONN_NOT_BOUND_TO_SESSION") Signed-off-by: Trond Myklebust Signed-off-by: Anna Schumaker --- fs/nfs/nfs4state.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index c3503fb26fa2..acdc74baec1f 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -2670,6 +2670,7 @@ static void nfs4_state_manager(struct nfs_client *clp) if (status < 0) goto out_error; nfs4_state_end_reclaim_reboot(clp); + continue; } /* Detect expired delegations... */ From e59679f2b7e522ecad99974e5636291ffd47c184 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sun, 16 Oct 2022 14:44:33 -0400 Subject: [PATCH 0139/1823] NFSv4.1: We must always send RECLAIM_COMPLETE after a reboot Currently, we are only guaranteed to send RECLAIM_COMPLETE if we have open state to recover. Fix the client to always send RECLAIM_COMPLETE after setting up the lease. Fixes: fce5c838e133 ("nfs41: RECLAIM_COMPLETE functionality") Signed-off-by: Trond Myklebust Signed-off-by: Anna Schumaker --- fs/nfs/nfs4state.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index acdc74baec1f..a2d2d5d1b088 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1786,6 +1786,7 @@ static void nfs4_state_mark_reclaim_helper(struct nfs_client *clp, static void nfs4_state_start_reclaim_reboot(struct nfs_client *clp) { + set_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state); /* Mark all delegations for reclaim */ nfs_delegation_mark_reclaim(clp); nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_reboot); From cbdeaee94a415800c65a8c3fa04d9664a8b8fb3a Mon Sep 17 00:00:00 2001 From: Zhang Xiaoxu Date: Thu, 20 Oct 2022 11:42:17 +0800 Subject: [PATCH 0140/1823] SUNRPC: Fix null-ptr-deref when xps sysfs alloc failed There is a null-ptr-deref when xps sysfs alloc failed: BUG: KASAN: null-ptr-deref in sysfs_do_create_link_sd+0x40/0xd0 Read of size 8 at addr 0000000000000030 by task gssproxy/457 CPU: 5 PID: 457 Comm: gssproxy Not tainted 6.0.0-09040-g02357b27ee03 #9 Call Trace: dump_stack_lvl+0x34/0x44 kasan_report+0xa3/0x120 sysfs_do_create_link_sd+0x40/0xd0 rpc_sysfs_client_setup+0x161/0x1b0 rpc_new_client+0x3fc/0x6e0 rpc_create_xprt+0x71/0x220 rpc_create+0x1d4/0x350 gssp_rpc_create+0xc3/0x160 set_gssp_clnt+0xbc/0x140 write_gssp+0x116/0x1a0 proc_reg_write+0xd6/0x130 vfs_write+0x177/0x690 ksys_write+0xb9/0x150 do_syscall_64+0x35/0x80 entry_SYSCALL_64_after_hwframe+0x46/0xb0 When the xprt_switch sysfs alloc failed, should not add xprt and switch sysfs to it, otherwise, maybe null-ptr-deref; also initialize the 'xps_sysfs' to NULL to avoid oops when destroy it. Fixes: 2a338a543163 ("sunrpc: add a symlink from rpc-client directory to the xprt_switch") Fixes: d408ebe04ac5 ("sunrpc: add add sysfs directory per xprt under each xprt_switch") Fixes: baea99445dd4 ("sunrpc: add xprt_switch direcotry to sunrpc's sysfs") Signed-off-by: Zhang Xiaoxu Signed-off-by: Anna Schumaker --- net/sunrpc/sysfs.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/net/sunrpc/sysfs.c b/net/sunrpc/sysfs.c index c65c90ad626a..c1f559892ae8 100644 --- a/net/sunrpc/sysfs.c +++ b/net/sunrpc/sysfs.c @@ -518,13 +518,16 @@ void rpc_sysfs_client_setup(struct rpc_clnt *clnt, struct net *net) { struct rpc_sysfs_client *rpc_client; + struct rpc_sysfs_xprt_switch *xswitch = + (struct rpc_sysfs_xprt_switch *)xprt_switch->xps_sysfs; + + if (!xswitch) + return; rpc_client = rpc_sysfs_client_alloc(rpc_sunrpc_client_kobj, net, clnt->cl_clid); if (rpc_client) { char name[] = "switch"; - struct rpc_sysfs_xprt_switch *xswitch = - (struct rpc_sysfs_xprt_switch *)xprt_switch->xps_sysfs; int ret; clnt->cl_sysfs = rpc_client; @@ -558,6 +561,8 @@ void rpc_sysfs_xprt_switch_setup(struct rpc_xprt_switch *xprt_switch, rpc_xprt_switch->xprt_switch = xprt_switch; rpc_xprt_switch->xprt = xprt; kobject_uevent(&rpc_xprt_switch->kobject, KOBJ_ADD); + } else { + xprt_switch->xps_sysfs = NULL; } } @@ -569,6 +574,9 @@ void rpc_sysfs_xprt_setup(struct rpc_xprt_switch *xprt_switch, struct rpc_sysfs_xprt_switch *switch_obj = (struct rpc_sysfs_xprt_switch *)xprt_switch->xps_sysfs; + if (!switch_obj) + return; + rpc_xprt = rpc_sysfs_xprt_alloc(&switch_obj->kobject, xprt, gfp_flags); if (rpc_xprt) { xprt->xprt_sysfs = rpc_xprt; From f5ea16137a3fa2858620dc9084466491c128535f Mon Sep 17 00:00:00 2001 From: Benjamin Coddington Date: Wed, 19 Oct 2022 12:09:18 -0400 Subject: [PATCH 0141/1823] NFSv4: Retry LOCK on OLD_STATEID during delegation return There's a small window where a LOCK sent during a delegation return can race with another OPEN on client, but the open stateid has not yet been updated. In this case, the client doesn't handle the OLD_STATEID error from the server and will lose this lock, emitting: "NFS: nfs4_handle_delegation_recall_error: unhandled error -10024". Fix this by sending the task through the nfs4 error handling in nfs4_lock_done() when we may have to reconcile our stateid with what the server believes it to be. For this case, the result is a retry of the LOCK operation with the updated stateid. Reported-by: Gonzalo Siero Humet Signed-off-by: Benjamin Coddington Signed-off-by: Anna Schumaker --- fs/nfs/nfs4proc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 4c4df7f213b6..86ed5c0142c3 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -7138,6 +7138,7 @@ static void nfs4_lock_done(struct rpc_task *task, void *calldata) { struct nfs4_lockdata *data = calldata; struct nfs4_lock_state *lsp = data->lsp; + struct nfs_server *server = NFS_SERVER(d_inode(data->ctx->dentry)); if (!nfs4_sequence_done(task, &data->res.seq_res)) return; @@ -7145,8 +7146,7 @@ static void nfs4_lock_done(struct rpc_task *task, void *calldata) data->rpc_status = task->tk_status; switch (task->tk_status) { case 0: - renew_lease(NFS_SERVER(d_inode(data->ctx->dentry)), - data->timestamp); + renew_lease(server, data->timestamp); if (data->arg.new_lock && !data->cancelled) { data->fl.fl_flags &= ~(FL_SLEEP | FL_ACCESS); if (locks_lock_inode_wait(lsp->ls_state->inode, &data->fl) < 0) @@ -7167,6 +7167,8 @@ static void nfs4_lock_done(struct rpc_task *task, void *calldata) if (!nfs4_stateid_match(&data->arg.open_stateid, &lsp->ls_state->open_stateid)) goto out_restart; + else if (nfs4_async_handle_error(task, server, lsp->ls_state, NULL) == -EAGAIN) + goto out_restart; } else if (!nfs4_stateid_match(&data->arg.lock_stateid, &lsp->ls_stateid)) goto out_restart; From 8a0fa3ff3b606b55c4edc71ad133e61529b64549 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Sat, 8 Oct 2022 14:58:29 -0400 Subject: [PATCH 0142/1823] SUNRPC: Fix crasher in gss_unwrap_resp_integ() If a zero length is passed to kmalloc() it returns 0x10, which is not a valid address. gss_unwrap_resp_integ() subsequently crashes when it attempts to dereference that pointer. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- net/sunrpc/auth_gss/auth_gss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index a31a27816cc0..7bb247c51e2f 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -1989,7 +1989,7 @@ gss_unwrap_resp_integ(struct rpc_task *task, struct rpc_cred *cred, goto unwrap_failed; mic.len = len; mic.data = kmalloc(len, GFP_KERNEL); - if (!mic.data) + if (ZERO_OR_NULL_PTR(mic.data)) goto unwrap_failed; if (read_bytes_from_xdr_buf(rcv_buf, offset, mic.data, mic.len)) goto unwrap_failed; From 038efb6348ce96228f6828354cb809c22a661681 Mon Sep 17 00:00:00 2001 From: Benjamin Coddington Date: Thu, 13 Oct 2022 11:58:01 -0400 Subject: [PATCH 0143/1823] NFSv4.2: Fixup CLONE dest file size for zero-length count When holding a delegation, the NFS client optimizes away setting the attributes of a file from the GETATTR in the compound after CLONE, and for a zero-length CLONE we will end up setting the inode's size to zero in nfs42_copy_dest_done(). Handle this case by computing the resulting count from the server's reported size after CLONE's GETATTR. Suggested-by: Trond Myklebust Signed-off-by: Benjamin Coddington Fixes: 94d202d5ca39 ("NFSv42: Copy offload should update the file size when appropriate") Signed-off-by: Anna Schumaker --- fs/nfs/nfs42proc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/nfs/nfs42proc.c b/fs/nfs/nfs42proc.c index 13424f0d793b..ecb428512fe1 100644 --- a/fs/nfs/nfs42proc.c +++ b/fs/nfs/nfs42proc.c @@ -1093,6 +1093,9 @@ static int _nfs42_proc_clone(struct rpc_message *msg, struct file *src_f, &args.seq_args, &res.seq_res, 0); trace_nfs4_clone(src_inode, dst_inode, &args, status); if (status == 0) { + /* a zero-length count means clone to EOF in src */ + if (count == 0 && res.dst_fattr->valid & NFS_ATTR_FATTR_SIZE) + count = nfs_size_to_loff_t(res.dst_fattr->size) - dst_offset; nfs42_copy_dest_done(dst_inode, dst_offset, count); status = nfs_post_op_update_inode(dst_inode, res.dst_fattr); } From 7e8436728e22181c3f12a5dbabd35ed3a8b8c593 Mon Sep 17 00:00:00 2001 From: Zhang Xiaoxu Date: Thu, 20 Oct 2022 11:20:54 +0800 Subject: [PATCH 0144/1823] nfs4: Fix kmemleak when allocate slot failed If one of the slot allocate failed, should cleanup all the other allocated slots, otherwise, the allocated slots will leak: unreferenced object 0xffff8881115aa100 (size 64): comm ""mount.nfs"", pid 679, jiffies 4294744957 (age 115.037s) hex dump (first 32 bytes): 00 cc 19 73 81 88 ff ff 00 a0 5a 11 81 88 ff ff ...s......Z..... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [<000000007a4c434a>] nfs4_find_or_create_slot+0x8e/0x130 [<000000005472a39c>] nfs4_realloc_slot_table+0x23f/0x270 [<00000000cd8ca0eb>] nfs40_init_client+0x4a/0x90 [<00000000128486db>] nfs4_init_client+0xce/0x270 [<000000008d2cacad>] nfs4_set_client+0x1a2/0x2b0 [<000000000e593b52>] nfs4_create_server+0x300/0x5f0 [<00000000e4425dd2>] nfs4_try_get_tree+0x65/0x110 [<00000000d3a6176f>] vfs_get_tree+0x41/0xf0 [<0000000016b5ad4c>] path_mount+0x9b3/0xdd0 [<00000000494cae71>] __x64_sys_mount+0x190/0x1d0 [<000000005d56bdec>] do_syscall_64+0x35/0x80 [<00000000687c9ae4>] entry_SYSCALL_64_after_hwframe+0x46/0xb0 Fixes: abf79bb341bf ("NFS: Add a slot table to struct nfs_client for NFSv4.0 transport blocking") Signed-off-by: Zhang Xiaoxu Signed-off-by: Anna Schumaker --- fs/nfs/nfs4client.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index 3b9b53481309..d3051b051a56 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c @@ -346,6 +346,7 @@ int nfs40_init_client(struct nfs_client *clp) ret = nfs4_setup_slot_table(tbl, NFS4_MAX_SLOT_TABLE, "NFSv4.0 transport Slot table"); if (ret) { + nfs4_shutdown_slot_table(tbl); kfree(tbl); return ret; } From 08a32902a56e1670850fe5d518d8203e9ce354b8 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 18 Oct 2022 15:52:56 +0200 Subject: [PATCH 0145/1823] clk: Remove WARN_ON NULL parent in clk_core_init_rate_req() If a clock has CLK_SET_RATE_PARENT, but core->parent is NULL (most likely because it's orphan), callers of clk_core_init_rate_req() will blindly call this function leading to a very verbose warning. Since it's a fairly common situation, let's just remove the WARN_ON but keep the check that prevents us from dereferencing the pointer. Interestingly, it fixes a regression on the Mediatek MT8195 where the GPU would stall during a clk_set_rate for its main clock. We couldn't come up with a proper explanation since the condition is essentially the same. It was then assumed that it could be timing related since printing the warning stacktrace takes a while, but we couldn't replicate the failure by using fairly large (10ms) mdelays. Fixes: 262ca38f4b6e ("clk: Stop forwarding clk_rate_requests to the parent") Reported-by: AngeloGioacchino Del Regno Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20221018-clk-range-checks-fixes-v1-1-f3ef80518140@cerno.tech Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Stephen Boyd --- drivers/clk/clk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index c3c3f8c07258..37d623c7b73b 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1459,7 +1459,7 @@ static void clk_core_init_rate_req(struct clk_core * const core, { struct clk_core *parent; - if (WARN_ON(!core || !req)) + if (!core || WARN_ON(!req)) return; memset(req, 0, sizeof(*req)); From 2079d029387adfc0cc123f01a6fcf9eb6540ee4d Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 18 Oct 2022 15:52:57 +0200 Subject: [PATCH 0146/1823] clk: Initialize the clk_rate_request even if clk_core is NULL Since commit c35e84b09776 ("clk: Introduce clk_hw_init_rate_request()"), users that used to initialize their clk_rate_request by initializing their local structure now rely on clk_hw_init_rate_request(). This function is backed by clk_core_init_rate_req(), which will skip the initialization if either the pointer to struct clk_core or to struct clk_rate_request are NULL. However, the core->parent pointer might be NULL because the clock is orphan, and we will thus end up with our local struct clk_rate_request left untouched. And since clk_hw_init_rate_request() doesn't return an error, we will then call a determine_rate variant with that unitialized structure. In order to avoid this, let's clear our clk_rate_request if the pointer to it is valid but the pointer to struct clk_core isn't. Fixes: c35e84b09776 ("clk: Introduce clk_hw_init_rate_request()") Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20221018-clk-range-checks-fixes-v1-2-f3ef80518140@cerno.tech Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Stephen Boyd --- drivers/clk/clk.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 37d623c7b73b..eb2f9be9b9aa 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1459,11 +1459,14 @@ static void clk_core_init_rate_req(struct clk_core * const core, { struct clk_core *parent; - if (!core || WARN_ON(!req)) + if (WARN_ON(!req)) return; memset(req, 0, sizeof(*req)); + if (!core) + return; + req->rate = rate; clk_core_get_boundaries(core, &req->min_rate, &req->max_rate); From 774560cf28fe115f106b6176c54ec641491136ac Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 18 Oct 2022 15:52:58 +0200 Subject: [PATCH 0147/1823] clk: Initialize max_rate in struct clk_rate_request Since commit b46fd8dbe8ad ("clk: Zero the clk_rate_request structure"), the clk_core_init_rate_req() function clears the struct clk_rate_request passed as argument. However, the default value for max_rate isn't 0 but ULONG_MAX, and we end up creating a clk_rate_request instance where the maximum rate is 0. Let's initialize max_rate to ULONG_MAX properly. Fixes: b46fd8dbe8ad ("clk: Zero the clk_rate_request structure") Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20221018-clk-range-checks-fixes-v1-3-f3ef80518140@cerno.tech Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Stephen Boyd --- drivers/clk/clk.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index eb2f9be9b9aa..57b83665e5c3 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1463,6 +1463,7 @@ static void clk_core_init_rate_req(struct clk_core * const core, return; memset(req, 0, sizeof(*req)); + req->max_rate = ULONG_MAX; if (!core) return; From ffa20aa581cf5377fc397b0d0ff9d67ea823629b Mon Sep 17 00:00:00 2001 From: Taniya Das Date: Wed, 19 Oct 2022 11:35:35 +0530 Subject: [PATCH 0148/1823] clk: qcom: Update the force mem core bit for GPU clocks There are few GPU clocks which are powering up the memories and thus enable the FORCE_MEM_PERIPH always for these clocks to force the periph_on signal to remain active during halt state of the clock. Fixes: a3cc092196ef ("clk: qcom: Add Global Clock controller (GCC) driver for SC7280") Fixes: 3e0f01d6c7e7 ("clk: qcom: Add graphics clock controller driver for SC7280") Signed-off-by: Taniya Das Signed-off-by: Satya Priya Link: https://lore.kernel.org/r/1666159535-6447-1-git-send-email-quic_c_skakit@quicinc.com Signed-off-by: Stephen Boyd --- drivers/clk/qcom/gcc-sc7280.c | 1 + drivers/clk/qcom/gpucc-sc7280.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/clk/qcom/gcc-sc7280.c b/drivers/clk/qcom/gcc-sc7280.c index 8afb7575e712..46d41ebce2b0 100644 --- a/drivers/clk/qcom/gcc-sc7280.c +++ b/drivers/clk/qcom/gcc-sc7280.c @@ -3467,6 +3467,7 @@ static int gcc_sc7280_probe(struct platform_device *pdev) regmap_update_bits(regmap, 0x28004, BIT(0), BIT(0)); regmap_update_bits(regmap, 0x28014, BIT(0), BIT(0)); regmap_update_bits(regmap, 0x71004, BIT(0), BIT(0)); + regmap_update_bits(regmap, 0x7100C, BIT(13), BIT(13)); ret = qcom_cc_register_rcg_dfs(regmap, gcc_dfs_clocks, ARRAY_SIZE(gcc_dfs_clocks)); diff --git a/drivers/clk/qcom/gpucc-sc7280.c b/drivers/clk/qcom/gpucc-sc7280.c index 9a832f2bcf49..1490cd45a654 100644 --- a/drivers/clk/qcom/gpucc-sc7280.c +++ b/drivers/clk/qcom/gpucc-sc7280.c @@ -463,6 +463,7 @@ static int gpu_cc_sc7280_probe(struct platform_device *pdev) */ regmap_update_bits(regmap, 0x1170, BIT(0), BIT(0)); regmap_update_bits(regmap, 0x1098, BIT(0), BIT(0)); + regmap_update_bits(regmap, 0x1098, BIT(13), BIT(13)); return qcom_cc_really_probe(pdev, &gpu_cc_sc7280_desc, regmap); } From 9a8c5b0d061554fedd7dbe894e63aa34d0bac7c4 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Thu, 27 Oct 2022 16:04:36 -0400 Subject: [PATCH 0149/1823] ext4: update the backup superblock's at the end of the online resize When expanding a file system using online resize, various fields in the superblock (e.g., s_blocks_count, s_inodes_count, etc.) change. To update the backup superblocks, the online resize uses the function update_backups() in fs/ext4/resize.c. This function was not updating the checksum field in the backup superblocks. This wasn't a big deal previously, because e2fsck didn't care about the checksum field in the backup superblock. (And indeed, update_backups() goes all the way back to the ext3 days, well before we had support for metadata checksums.) However, there is an alternate, more general way of updating superblock fields, ext4_update_primary_sb() in fs/ext4/ioctl.c. This function does check the checksum of the backup superblock, and if it doesn't match will mark the file system as corrupted. That was clearly not the intent, so avoid to aborting the resize when a bad superblock is found. In addition, teach update_backups() to properly update the checksum in the backup superblocks. We will eventually want to unify updapte_backups() with the infrasture in ext4_update_primary_sb(), but that's for another day. Note: The problem has been around for a while; it just didn't really matter until ext4_update_primary_sb() was added by commit bbc605cdb1e1 ("ext4: implement support for get/set fs label"). And it became trivially easy to reproduce after commit 827891a38acc ("ext4: update the s_overhead_clusters in the backup sb's when resizing") in v6.0. Cc: stable@kernel.org # 5.17+ Fixes: bbc605cdb1e1 ("ext4: implement support for get/set fs label") Signed-off-by: Theodore Ts'o --- fs/ext4/ioctl.c | 3 +-- fs/ext4/resize.c | 5 +++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index 4d49c5cfb690..790d5ffe8559 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -145,9 +145,8 @@ static int ext4_update_backup_sb(struct super_block *sb, if (ext4_has_metadata_csum(sb) && es->s_checksum != ext4_superblock_csum(sb, es)) { ext4_msg(sb, KERN_ERR, "Invalid checksum for backup " - "superblock %llu\n", sb_block); + "superblock %llu", sb_block); unlock_buffer(bh); - err = -EFSBADCRC; goto out_bh; } func(es, arg); diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c index 6dfe9ccae0c5..46b87ffeb304 100644 --- a/fs/ext4/resize.c +++ b/fs/ext4/resize.c @@ -1158,6 +1158,7 @@ static void update_backups(struct super_block *sb, sector_t blk_off, char *data, while (group < sbi->s_groups_count) { struct buffer_head *bh; ext4_fsblk_t backup_block; + struct ext4_super_block *es; /* Out of journal space, and can't get more - abort - so sad */ err = ext4_resize_ensure_credits_batch(handle, 1); @@ -1186,6 +1187,10 @@ static void update_backups(struct super_block *sb, sector_t blk_off, char *data, memcpy(bh->b_data, data, size); if (rest) memset(bh->b_data + size, 0, rest); + es = (struct ext4_super_block *) bh->b_data; + es->s_block_group_nr = cpu_to_le16(group); + if (ext4_has_metadata_csum(sb)) + es->s_checksum = ext4_superblock_csum(sb, es); set_buffer_uptodate(bh); unlock_buffer(bh); err = ext4_handle_dirty_metadata(handle, NULL, bh); From fb3041d61f6867158088c627c2790f94e208d1ea Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 28 Oct 2022 01:28:39 +0900 Subject: [PATCH 0150/1823] kbuild: fix SIGPIPE error message for AR=gcc-ar and AR=llvm-ar Jiri Slaby reported that building the kernel with AR=gcc-ar shows: /usr/bin/ar terminated with signal 13 [Broken pipe] Nathan Chancellor reported the latest AR=llvm-ar shows: error: write on a pipe with no reader The latter occurs since LLVM commit 51b557adc131 ("Add an error message to the default SIGPIPE handler"). The resulting vmlinux is correct, but it is better to silence it. 'head -n1' exits after reading the first line, so the pipe is closed. Use 'sed -n 1p' to eat the stream till the end. Fixes: 321648455061 ("kbuild: use obj-y instead extra-y for objects placed at the head") Link: https://github.com/ClangBuiltLinux/linux/issues/1651 Reported-by: Jiri Slaby Reported-by: Nathan Chancellor Signed-off-by: Masahiro Yamada Tested-by: Nick Desaulniers Reviewed-by: Nick Desaulniers Tested-by: Nathan Chancellor --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e90bb2b38607..e9e7eff906a5 100644 --- a/Makefile +++ b/Makefile @@ -1218,7 +1218,7 @@ quiet_cmd_ar_vmlinux.a = AR $@ cmd_ar_vmlinux.a = \ rm -f $@; \ $(AR) cDPrST $@ $(KBUILD_VMLINUX_OBJS); \ - $(AR) mPiT $$($(AR) t $@ | head -n1) $@ $$($(AR) t $@ | grep -F -f $(srctree)/scripts/head-object-list.txt) + $(AR) mPiT $$($(AR) t $@ | sed -n 1p) $@ $$($(AR) t $@ | grep -F -f $(srctree)/scripts/head-object-list.txt) targets += vmlinux.a vmlinux.a: $(KBUILD_VMLINUX_OBJS) scripts/head-object-list.txt autoksyms_recursive FORCE From 633efc8b3dc96f56f5a57f2a49764853a2fa3f50 Mon Sep 17 00:00:00 2001 From: Chen Zhongjin Date: Wed, 26 Oct 2022 10:03:21 +0800 Subject: [PATCH 0151/1823] net: dsa: Fix possible memory leaks in dsa_loop_init() kmemleak reported memory leaks in dsa_loop_init(): kmemleak: 12 new suspected memory leaks unreferenced object 0xffff8880138ce000 (size 2048): comm "modprobe", pid 390, jiffies 4295040478 (age 238.976s) backtrace: [<000000006a94f1d5>] kmalloc_trace+0x26/0x60 [<00000000a9c44622>] phy_device_create+0x5d/0x970 [<00000000d0ee2afc>] get_phy_device+0xf3/0x2b0 [<00000000dca0c71f>] __fixed_phy_register.part.0+0x92/0x4e0 [<000000008a834798>] fixed_phy_register+0x84/0xb0 [<0000000055223fcb>] dsa_loop_init+0xa9/0x116 [dsa_loop] ... There are two reasons for memleak in dsa_loop_init(). First, fixed_phy_register() create and register phy_device: fixed_phy_register() get_phy_device() phy_device_create() # freed by phy_device_free() phy_device_register() # freed by phy_device_remove() But fixed_phy_unregister() only calls phy_device_remove(). So the memory allocated in phy_device_create() is leaked. Second, when mdio_driver_register() fail in dsa_loop_init(), it just returns and there is no cleanup for phydevs. Fix the problems by catching the error of mdio_driver_register() in dsa_loop_init(), then calling both fixed_phy_unregister() and phy_device_free() to release phydevs. Also add a function for phydevs cleanup to avoid duplacate. Fixes: 98cd1552ea27 ("net: dsa: Mock-up driver") Signed-off-by: Chen Zhongjin Signed-off-by: David S. Miller --- drivers/net/dsa/dsa_loop.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/drivers/net/dsa/dsa_loop.c b/drivers/net/dsa/dsa_loop.c index b9107fe40023..5b139f2206b6 100644 --- a/drivers/net/dsa/dsa_loop.c +++ b/drivers/net/dsa/dsa_loop.c @@ -376,6 +376,17 @@ static struct mdio_driver dsa_loop_drv = { #define NUM_FIXED_PHYS (DSA_LOOP_NUM_PORTS - 2) +static void dsa_loop_phydevs_unregister(void) +{ + unsigned int i; + + for (i = 0; i < NUM_FIXED_PHYS; i++) + if (!IS_ERR(phydevs[i])) { + fixed_phy_unregister(phydevs[i]); + phy_device_free(phydevs[i]); + } +} + static int __init dsa_loop_init(void) { struct fixed_phy_status status = { @@ -383,23 +394,23 @@ static int __init dsa_loop_init(void) .speed = SPEED_100, .duplex = DUPLEX_FULL, }; - unsigned int i; + unsigned int i, ret; for (i = 0; i < NUM_FIXED_PHYS; i++) phydevs[i] = fixed_phy_register(PHY_POLL, &status, NULL); - return mdio_driver_register(&dsa_loop_drv); + ret = mdio_driver_register(&dsa_loop_drv); + if (ret) + dsa_loop_phydevs_unregister(); + + return ret; } module_init(dsa_loop_init); static void __exit dsa_loop_exit(void) { - unsigned int i; - mdio_driver_unregister(&dsa_loop_drv); - for (i = 0; i < NUM_FIXED_PHYS; i++) - if (!IS_ERR(phydevs[i])) - fixed_phy_unregister(phydevs[i]); + dsa_loop_phydevs_unregister(); } module_exit(dsa_loop_exit); From a51abbbf25317c07cb00b40ae7d04a209d2a3d54 Mon Sep 17 00:00:00 2001 From: Michal Luczaj Date: Thu, 13 Oct 2022 21:12:33 +0000 Subject: [PATCH 0152/1823] KVM: selftests: Add tests in xen_shinfo_test to detect lock races Tests for races between shinfo_cache (de)activation and hypercall+ioctl() processing. KVM has had bugs where activating the shared info cache multiple times and/or with concurrent users results in lock corruption, NULL pointer dereferences, and other fun. For the timer injection testcase (#22), re-arm the timer until the IRQ is successfully injected. If the timer expires while the shared info is deactivated (invalid), KVM will drop the event. Signed-off-by: Michal Luczaj Co-developed-by: Sean Christopherson Signed-off-by: Sean Christopherson Message-Id: <20221013211234.1318131-16-seanjc@google.com> Signed-off-by: Paolo Bonzini --- .../selftests/kvm/x86_64/xen_shinfo_test.c | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c b/tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c index 8a5cb800f50e..caa3f5ab9e10 100644 --- a/tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c +++ b/tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c @@ -15,9 +15,13 @@ #include #include #include +#include #include +/* Defined in include/linux/kvm_types.h */ +#define GPA_INVALID (~(ulong)0) + #define SHINFO_REGION_GVA 0xc0000000ULL #define SHINFO_REGION_GPA 0xc0000000ULL #define SHINFO_REGION_SLOT 10 @@ -44,6 +48,8 @@ #define MIN_STEAL_TIME 50000 +#define SHINFO_RACE_TIMEOUT 2 /* seconds */ + #define __HYPERVISOR_set_timer_op 15 #define __HYPERVISOR_sched_op 29 #define __HYPERVISOR_event_channel_op 32 @@ -148,6 +154,7 @@ static void guest_wait_for_irq(void) static void guest_code(void) { struct vcpu_runstate_info *rs = (void *)RUNSTATE_VADDR; + int i; __asm__ __volatile__( "sti\n" @@ -325,6 +332,49 @@ static void guest_code(void) guest_wait_for_irq(); GUEST_SYNC(21); + /* Racing host ioctls */ + + guest_wait_for_irq(); + + GUEST_SYNC(22); + /* Racing vmcall against host ioctl */ + + ports[0] = 0; + + p = (struct sched_poll) { + .ports = ports, + .nr_ports = 1, + .timeout = 0 + }; + +wait_for_timer: + /* + * Poll for a timer wake event while the worker thread is mucking with + * the shared info. KVM XEN drops timer IRQs if the shared info is + * invalid when the timer expires. Arbitrarily poll 100 times before + * giving up and asking the VMM to re-arm the timer. 100 polls should + * consume enough time to beat on KVM without taking too long if the + * timer IRQ is dropped due to an invalid event channel. + */ + for (i = 0; i < 100 && !guest_saw_irq; i++) + asm volatile("vmcall" + : "=a" (rax) + : "a" (__HYPERVISOR_sched_op), + "D" (SCHEDOP_poll), + "S" (&p) + : "memory"); + + /* + * Re-send the timer IRQ if it was (likely) dropped due to the timer + * expiring while the event channel was invalid. + */ + if (!guest_saw_irq) { + GUEST_SYNC(23); + goto wait_for_timer; + } + guest_saw_irq = false; + + GUEST_SYNC(24); } static int cmp_timespec(struct timespec *a, struct timespec *b) @@ -352,11 +402,36 @@ static void handle_alrm(int sig) TEST_FAIL("IRQ delivery timed out"); } +static void *juggle_shinfo_state(void *arg) +{ + struct kvm_vm *vm = (struct kvm_vm *)arg; + + struct kvm_xen_hvm_attr cache_init = { + .type = KVM_XEN_ATTR_TYPE_SHARED_INFO, + .u.shared_info.gfn = SHINFO_REGION_GPA / PAGE_SIZE + }; + + struct kvm_xen_hvm_attr cache_destroy = { + .type = KVM_XEN_ATTR_TYPE_SHARED_INFO, + .u.shared_info.gfn = GPA_INVALID + }; + + for (;;) { + __vm_ioctl(vm, KVM_XEN_HVM_SET_ATTR, &cache_init); + __vm_ioctl(vm, KVM_XEN_HVM_SET_ATTR, &cache_destroy); + pthread_testcancel(); + }; + + return NULL; +} + int main(int argc, char *argv[]) { struct timespec min_ts, max_ts, vm_ts; struct kvm_vm *vm; + pthread_t thread; bool verbose; + int ret; verbose = argc > 1 && (!strncmp(argv[1], "-v", 3) || !strncmp(argv[1], "--verbose", 10)); @@ -785,6 +860,71 @@ int main(int argc, char *argv[]) case 21: TEST_ASSERT(!evtchn_irq_expected, "Expected event channel IRQ but it didn't happen"); + alarm(0); + + if (verbose) + printf("Testing shinfo lock corruption (KVM_XEN_HVM_EVTCHN_SEND)\n"); + + ret = pthread_create(&thread, NULL, &juggle_shinfo_state, (void *)vm); + TEST_ASSERT(ret == 0, "pthread_create() failed: %s", strerror(ret)); + + struct kvm_irq_routing_xen_evtchn uxe = { + .port = 1, + .vcpu = vcpu->id, + .priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL + }; + + evtchn_irq_expected = true; + for (time_t t = time(NULL) + SHINFO_RACE_TIMEOUT; time(NULL) < t;) + __vm_ioctl(vm, KVM_XEN_HVM_EVTCHN_SEND, &uxe); + break; + + case 22: + TEST_ASSERT(!evtchn_irq_expected, + "Expected event channel IRQ but it didn't happen"); + + if (verbose) + printf("Testing shinfo lock corruption (SCHEDOP_poll)\n"); + + shinfo->evtchn_pending[0] = 1; + + evtchn_irq_expected = true; + tmr.u.timer.expires_ns = rs->state_entry_time + + SHINFO_RACE_TIMEOUT * 1000000000ULL; + vcpu_ioctl(vcpu, KVM_XEN_VCPU_SET_ATTR, &tmr); + break; + + case 23: + /* + * Optional and possibly repeated sync point. + * Injecting the timer IRQ may fail if the + * shinfo is invalid when the timer expires. + * If the timer has expired but the IRQ hasn't + * been delivered, rearm the timer and retry. + */ + vcpu_ioctl(vcpu, KVM_XEN_VCPU_GET_ATTR, &tmr); + + /* Resume the guest if the timer is still pending. */ + if (tmr.u.timer.expires_ns) + break; + + /* All done if the IRQ was delivered. */ + if (!evtchn_irq_expected) + break; + + tmr.u.timer.expires_ns = rs->state_entry_time + + SHINFO_RACE_TIMEOUT * 1000000000ULL; + vcpu_ioctl(vcpu, KVM_XEN_VCPU_SET_ATTR, &tmr); + break; + case 24: + TEST_ASSERT(!evtchn_irq_expected, + "Expected event channel IRQ but it didn't happen"); + + ret = pthread_cancel(thread); + TEST_ASSERT(ret == 0, "pthread_cancel() failed: %s", strerror(ret)); + + ret = pthread_join(thread, 0); + TEST_ASSERT(ret == 0, "pthread_join() failed: %s", strerror(ret)); goto done; case 0x20: From 5addaf530995ac203fa46efde0d1ded4c15ff98e Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 13 Oct 2022 21:12:34 +0000 Subject: [PATCH 0153/1823] KVM: selftests: Mark "guest_saw_irq" as volatile in xen_shinfo_test Tag "guest_saw_irq" as "volatile" to ensure that the compiler will never optimize away lookups. Relying on the compiler thinking that the flag is global and thus might change also works, but it's subtle, less robust, and looks like a bug at first glance, e.g. risks being "fixed" and breaking the test. Make the flag "static" as well since convincing the compiler it's global is no longer necessary. Alternatively, the flag could be accessed with {READ,WRITE}_ONCE(), but literally every access would need the wrappers, and eking out performance isn't exactly top priority for selftests. Signed-off-by: Sean Christopherson Message-Id: <20221013211234.1318131-17-seanjc@google.com> Signed-off-by: Paolo Bonzini --- tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c b/tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c index caa3f5ab9e10..2a5727188c8d 100644 --- a/tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c +++ b/tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c @@ -132,7 +132,7 @@ struct { struct kvm_irq_routing_entry entries[2]; } irq_routes; -bool guest_saw_irq; +static volatile bool guest_saw_irq; static void evtchn_handler(struct ex_regs *regs) { From 5015bb89b58225f97df6ac44383e7e8c8662c8c9 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Tue, 25 Oct 2022 15:47:28 +0300 Subject: [PATCH 0154/1823] KVM: x86: emulator: em_sysexit should update ctxt->mode SYSEXIT is one of the instructions that can change the processor mode, thus ctxt->mode should be updated after it. Note that this is likely a benign bug, because the only problematic mode change is from 32 bit to 64 bit which can lead to truncation of RIP, and it is not possible to do with sysexit, since sysexit running in 32 bit mode will be limited to 32 bit version. Signed-off-by: Maxim Levitsky Message-Id: <20221025124741.228045-11-mlevitsk@redhat.com> Cc: stable@vger.kernel.org Signed-off-by: Paolo Bonzini --- arch/x86/kvm/emulate.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 3b27622d4642..261732957431 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -2876,6 +2876,7 @@ static int em_sysexit(struct x86_emulate_ctxt *ctxt) ops->set_segment(ctxt, ss_sel, &ss, 0, VCPU_SREG_SS); ctxt->_eip = rdx; + ctxt->mode = usermode; *reg_write(ctxt, VCPU_REGS_RSP) = rcx; return X86EMUL_CONTINUE; From d087e0f79fa0dd336a9a6b2f79ec23120f5eff73 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Tue, 25 Oct 2022 15:47:29 +0300 Subject: [PATCH 0155/1823] KVM: x86: emulator: introduce emulator_recalc_and_set_mode Some instructions update the cpu execution mode, which needs to update the emulation mode. Extract this code, and make assign_eip_far use it. assign_eip_far now reads CS, instead of getting it via a parameter, which is ok, because callers always assign CS to the same value before calling this function. No functional change is intended. Signed-off-by: Maxim Levitsky Message-Id: <20221025124741.228045-12-mlevitsk@redhat.com> Cc: stable@vger.kernel.org Signed-off-by: Paolo Bonzini --- arch/x86/kvm/emulate.c | 87 ++++++++++++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 29 deletions(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 261732957431..e5522a23d985 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -791,8 +791,7 @@ static int linearize(struct x86_emulate_ctxt *ctxt, ctxt->mode, linear); } -static inline int assign_eip(struct x86_emulate_ctxt *ctxt, ulong dst, - enum x86emul_mode mode) +static inline int assign_eip(struct x86_emulate_ctxt *ctxt, ulong dst) { ulong linear; int rc; @@ -802,41 +801,71 @@ static inline int assign_eip(struct x86_emulate_ctxt *ctxt, ulong dst, if (ctxt->op_bytes != sizeof(unsigned long)) addr.ea = dst & ((1UL << (ctxt->op_bytes << 3)) - 1); - rc = __linearize(ctxt, addr, &max_size, 1, false, true, mode, &linear); + rc = __linearize(ctxt, addr, &max_size, 1, false, true, ctxt->mode, &linear); if (rc == X86EMUL_CONTINUE) ctxt->_eip = addr.ea; return rc; } -static inline int assign_eip_near(struct x86_emulate_ctxt *ctxt, ulong dst) +static inline int emulator_recalc_and_set_mode(struct x86_emulate_ctxt *ctxt) { - return assign_eip(ctxt, dst, ctxt->mode); + u64 efer; + struct desc_struct cs; + u16 selector; + u32 base3; + + ctxt->ops->get_msr(ctxt, MSR_EFER, &efer); + + if (!(ctxt->ops->get_cr(ctxt, 0) & X86_CR0_PE)) { + /* Real mode. cpu must not have long mode active */ + if (efer & EFER_LMA) + return X86EMUL_UNHANDLEABLE; + ctxt->mode = X86EMUL_MODE_REAL; + return X86EMUL_CONTINUE; + } + + if (ctxt->eflags & X86_EFLAGS_VM) { + /* Protected/VM86 mode. cpu must not have long mode active */ + if (efer & EFER_LMA) + return X86EMUL_UNHANDLEABLE; + ctxt->mode = X86EMUL_MODE_VM86; + return X86EMUL_CONTINUE; + } + + if (!ctxt->ops->get_segment(ctxt, &selector, &cs, &base3, VCPU_SREG_CS)) + return X86EMUL_UNHANDLEABLE; + + if (efer & EFER_LMA) { + if (cs.l) { + /* Proper long mode */ + ctxt->mode = X86EMUL_MODE_PROT64; + } else if (cs.d) { + /* 32 bit compatibility mode*/ + ctxt->mode = X86EMUL_MODE_PROT32; + } else { + ctxt->mode = X86EMUL_MODE_PROT16; + } + } else { + /* Legacy 32 bit / 16 bit mode */ + ctxt->mode = cs.d ? X86EMUL_MODE_PROT32 : X86EMUL_MODE_PROT16; + } + + return X86EMUL_CONTINUE; } -static int assign_eip_far(struct x86_emulate_ctxt *ctxt, ulong dst, - const struct desc_struct *cs_desc) +static inline int assign_eip_near(struct x86_emulate_ctxt *ctxt, ulong dst) { - enum x86emul_mode mode = ctxt->mode; - int rc; + return assign_eip(ctxt, dst); +} -#ifdef CONFIG_X86_64 - if (ctxt->mode >= X86EMUL_MODE_PROT16) { - if (cs_desc->l) { - u64 efer = 0; +static int assign_eip_far(struct x86_emulate_ctxt *ctxt, ulong dst) +{ + int rc = emulator_recalc_and_set_mode(ctxt); - ctxt->ops->get_msr(ctxt, MSR_EFER, &efer); - if (efer & EFER_LMA) - mode = X86EMUL_MODE_PROT64; - } else - mode = X86EMUL_MODE_PROT32; /* temporary value */ - } -#endif - if (mode == X86EMUL_MODE_PROT16 || mode == X86EMUL_MODE_PROT32) - mode = cs_desc->d ? X86EMUL_MODE_PROT32 : X86EMUL_MODE_PROT16; - rc = assign_eip(ctxt, dst, mode); - if (rc == X86EMUL_CONTINUE) - ctxt->mode = mode; - return rc; + if (rc != X86EMUL_CONTINUE) + return rc; + + return assign_eip(ctxt, dst); } static inline int jmp_rel(struct x86_emulate_ctxt *ctxt, int rel) @@ -2172,7 +2201,7 @@ static int em_jmp_far(struct x86_emulate_ctxt *ctxt) if (rc != X86EMUL_CONTINUE) return rc; - rc = assign_eip_far(ctxt, ctxt->src.val, &new_desc); + rc = assign_eip_far(ctxt, ctxt->src.val); /* Error handling is not implemented. */ if (rc != X86EMUL_CONTINUE) return X86EMUL_UNHANDLEABLE; @@ -2250,7 +2279,7 @@ static int em_ret_far(struct x86_emulate_ctxt *ctxt) &new_desc); if (rc != X86EMUL_CONTINUE) return rc; - rc = assign_eip_far(ctxt, eip, &new_desc); + rc = assign_eip_far(ctxt, eip); /* Error handling is not implemented. */ if (rc != X86EMUL_CONTINUE) return X86EMUL_UNHANDLEABLE; @@ -3470,7 +3499,7 @@ static int em_call_far(struct x86_emulate_ctxt *ctxt) if (rc != X86EMUL_CONTINUE) return rc; - rc = assign_eip_far(ctxt, ctxt->src.val, &new_desc); + rc = assign_eip_far(ctxt, ctxt->src.val); if (rc != X86EMUL_CONTINUE) goto fail; From 055f37f84e304e59c046d1accfd8f08462f52c4c Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Tue, 25 Oct 2022 15:47:30 +0300 Subject: [PATCH 0156/1823] KVM: x86: emulator: update the emulation mode after rsm Update the emulation mode after RSM so that RIP will be correctly written back, because the RSM instruction can switch the CPU mode from 32 bit (or less) to 64 bit. This fixes a guest crash in case the #SMI is received while the guest runs a code from an address > 32 bit. Signed-off-by: Maxim Levitsky Message-Id: <20221025124741.228045-13-mlevitsk@redhat.com> Cc: stable@vger.kernel.org Signed-off-by: Paolo Bonzini --- arch/x86/kvm/emulate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index e5522a23d985..33385ebae100 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -2662,7 +2662,7 @@ static int em_rsm(struct x86_emulate_ctxt *ctxt) * those side effects need to be explicitly handled for both success * and shutdown. */ - return X86EMUL_CONTINUE; + return emulator_recalc_and_set_mode(ctxt); emulate_shutdown: ctxt->ops->triple_fault(ctxt); From ad8f9e69942c7db90758d9d774157e53bce94840 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Tue, 25 Oct 2022 15:47:31 +0300 Subject: [PATCH 0157/1823] KVM: x86: emulator: update the emulation mode after CR0 write Update the emulation mode when handling writes to CR0, because toggling CR0.PE switches between Real and Protected Mode, and toggling CR0.PG when EFER.LME=1 switches between Long and Protected Mode. This is likely a benign bug because there is no writeback of state, other than the RIP increment, and when toggling CR0.PE, the CPU has to execute code from a very low memory address. Signed-off-by: Maxim Levitsky Message-Id: <20221025124741.228045-14-mlevitsk@redhat.com> Cc: stable@vger.kernel.org Signed-off-by: Paolo Bonzini --- arch/x86/kvm/emulate.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 33385ebae100..2954c046740b 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -3641,11 +3641,25 @@ static int em_movbe(struct x86_emulate_ctxt *ctxt) static int em_cr_write(struct x86_emulate_ctxt *ctxt) { - if (ctxt->ops->set_cr(ctxt, ctxt->modrm_reg, ctxt->src.val)) + int cr_num = ctxt->modrm_reg; + int r; + + if (ctxt->ops->set_cr(ctxt, cr_num, ctxt->src.val)) return emulate_gp(ctxt, 0); /* Disable writeback. */ ctxt->dst.type = OP_NONE; + + if (cr_num == 0) { + /* + * CR0 write might have updated CR0.PE and/or CR0.PG + * which can affect the cpu's execution mode. + */ + r = emulator_recalc_and_set_mode(ctxt); + if (r != X86EMUL_CONTINUE) + return r; + } + return X86EMUL_CONTINUE; } From 696db303e54f7352623d9f640e6c51d8fa9d5588 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Tue, 25 Oct 2022 15:47:32 +0300 Subject: [PATCH 0158/1823] KVM: x86: smm: number of GPRs in the SMRAM image depends on the image format On 64 bit host, if the guest doesn't have X86_FEATURE_LM, KVM will access 16 gprs to 32-bit smram image, causing out-ouf-bound ram access. On 32 bit host, the rsm_load_state_64/enter_smm_save_state_64 is compiled out, thus access overflow can't happen. Fixes: b443183a25ab61 ("KVM: x86: Reduce the number of emulator GPRs to '8' for 32-bit KVM") Signed-off-by: Maxim Levitsky Reviewed-by: Sean Christopherson Message-Id: <20221025124741.228045-15-mlevitsk@redhat.com> Cc: stable@vger.kernel.org Signed-off-by: Paolo Bonzini --- arch/x86/kvm/emulate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 2954c046740b..4a43261d25a2 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -2461,7 +2461,7 @@ static int rsm_load_state_32(struct x86_emulate_ctxt *ctxt, ctxt->eflags = GET_SMSTATE(u32, smstate, 0x7ff4) | X86_EFLAGS_FIXED; ctxt->_eip = GET_SMSTATE(u32, smstate, 0x7ff0); - for (i = 0; i < NR_EMULATOR_GPRS; i++) + for (i = 0; i < 8; i++) *reg_write(ctxt, i) = GET_SMSTATE(u32, smstate, 0x7fd0 + i * 4); val = GET_SMSTATE(u32, smstate, 0x7fcc); @@ -2518,7 +2518,7 @@ static int rsm_load_state_64(struct x86_emulate_ctxt *ctxt, u16 selector; int i, r; - for (i = 0; i < NR_EMULATOR_GPRS; i++) + for (i = 0; i < 16; i++) *reg_write(ctxt, i) = GET_SMSTATE(u64, smstate, 0x7ff8 - i * 8); ctxt->_eip = GET_SMSTATE(u64, smstate, 0x7f78); From 8fdf3f6aba7cfa0c0e2bf66ecca7bb5783acd0d6 Mon Sep 17 00:00:00 2001 From: Radhey Shyam Pandey Date: Wed, 26 Oct 2022 20:45:24 +0530 Subject: [PATCH 0159/1823] net: emaclite: update reset_lock member documentation Instead of generic description, mention what reset_lock actually protects i.e. lock to serialize xmit and tx_timeout execution. Signed-off-by: Radhey Shyam Pandey Signed-off-by: David S. Miller --- drivers/net/ethernet/xilinx/xilinx_emaclite.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c index 05848ff15fb5..a3967f8de417 100644 --- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c +++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c @@ -108,7 +108,7 @@ * @next_tx_buf_to_use: next Tx buffer to write to * @next_rx_buf_to_use: next Rx buffer to read from * @base_addr: base address of the Emaclite device - * @reset_lock: lock used for synchronization + * @reset_lock: lock to serialize xmit and tx_timeout execution * @deferred_skb: holds an skb (for transmission at a later time) when the * Tx buffer is not free * @phy_dev: pointer to the PHY device From 8cf0a1bc12870d148ae830a4ba88cfdf0e879cee Mon Sep 17 00:00:00 2001 From: Gaosheng Cui Date: Tue, 25 Oct 2022 21:33:57 +0800 Subject: [PATCH 0160/1823] capabilities: fix potential memleak on error path from vfs_getxattr_alloc() In cap_inode_getsecurity(), we will use vfs_getxattr_alloc() to complete the memory allocation of tmpbuf, if we have completed the memory allocation of tmpbuf, but failed to call handler->get(...), there will be a memleak in below logic: |-- ret = (int)vfs_getxattr_alloc(mnt_userns, ...) | /* ^^^ alloc for tmpbuf */ |-- value = krealloc(*xattr_value, error + 1, flags) | /* ^^^ alloc memory */ |-- error = handler->get(handler, ...) | /* error! */ |-- *xattr_value = value | /* xattr_value is &tmpbuf (memory leak!) */ So we will try to free(tmpbuf) after vfs_getxattr_alloc() fails to fix it. Cc: stable@vger.kernel.org Fixes: 8db6c34f1dbc ("Introduce v3 namespaced file capabilities") Signed-off-by: Gaosheng Cui Acked-by: Serge Hallyn [PM: subject line and backtrace tweaks] Signed-off-by: Paul Moore --- security/commoncap.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/security/commoncap.c b/security/commoncap.c index 5fc8986c3c77..bc751fa5adad 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -401,8 +401,10 @@ int cap_inode_getsecurity(struct user_namespace *mnt_userns, &tmpbuf, size, GFP_NOFS); dput(dentry); - if (ret < 0 || !tmpbuf) - return ret; + if (ret < 0 || !tmpbuf) { + size = ret; + goto out_free; + } fs_ns = inode->i_sb->s_user_ns; cap = (struct vfs_cap_data *) tmpbuf; From 7353633814f6e5b4899fb9ee1483709d6bb0e1cd Mon Sep 17 00:00:00 2001 From: Eiichi Tsukata Date: Fri, 28 Oct 2022 09:26:31 +0000 Subject: [PATCH 0161/1823] KVM: x86/xen: Fix eventfd error handling in kvm_xen_eventfd_assign() Should not call eventfd_ctx_put() in case of error. Fixes: 2fd6df2f2b47 ("KVM: x86/xen: intercept EVTCHNOP_send from guests") Reported-by: syzbot+6f0c896c5a9449a10ded@syzkaller.appspotmail.com Signed-off-by: Eiichi Tsukata Message-Id: <20221028092631.117438-1-eiichi.tsukata@nutanix.com> [Introduce new goto target instead. - Paolo] Signed-off-by: Paolo Bonzini --- arch/x86/kvm/xen.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c index b2be60c6efa4..2dae413bd62a 100644 --- a/arch/x86/kvm/xen.c +++ b/arch/x86/kvm/xen.c @@ -1666,18 +1666,18 @@ static int kvm_xen_eventfd_assign(struct kvm *kvm, case EVTCHNSTAT_ipi: /* IPI must map back to the same port# */ if (data->u.evtchn.deliver.port.port != data->u.evtchn.send_port) - goto out; /* -EINVAL */ + goto out_noeventfd; /* -EINVAL */ break; case EVTCHNSTAT_interdomain: if (data->u.evtchn.deliver.port.port) { if (data->u.evtchn.deliver.port.port >= max_evtchn_port(kvm)) - goto out; /* -EINVAL */ + goto out_noeventfd; /* -EINVAL */ } else { eventfd = eventfd_ctx_fdget(data->u.evtchn.deliver.eventfd.fd); if (IS_ERR(eventfd)) { ret = PTR_ERR(eventfd); - goto out; + goto out_noeventfd; } } break; @@ -1717,6 +1717,7 @@ static int kvm_xen_eventfd_assign(struct kvm *kvm, out: if (eventfd) eventfd_ctx_put(eventfd); +out_noeventfd: kfree(evtchnfd); return ret; } From 13c30a755847c7e804e1bf755e66e3ff7b7f9367 Mon Sep 17 00:00:00 2001 From: Sjoerd Simons Date: Thu, 20 Oct 2022 09:56:24 +0800 Subject: [PATCH 0162/1823] soundwire: intel: Initialize clock stop timeout The bus->clk_stop_timeout member is only initialized to a non-zero value during the codec driver probe. This can lead to corner cases where this value remains pegged at zero when the bus suspends, which results in an endless loop in sdw_bus_wait_for_clk_prep_deprep(). Corner cases include configurations with no codecs described in the firmware, or delays in probing codec drivers. Initializing the default timeout to the smallest non-zero value avoid this problem and allows for the existing logic to be preserved: the bus->clk_stop_timeout is set as the maximum required by all codecs connected on the bus. Fixes: 1f2dcf3a154ac ("soundwire: intel: set dev_num_ida_min") Signed-off-by: Sjoerd Simons Reviewed-by: Pierre-Louis Bossart Reviewed-by: Chao Song Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20221020015624.1703950-1-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 244209358784..8c76541d553f 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1513,6 +1513,7 @@ static int intel_link_probe(struct auxiliary_device *auxdev, bus->link_id = auxdev->id; bus->dev_num_ida_min = INTEL_DEV_NUM_IDA_MIN; + bus->clk_stop_timeout = 1; sdw_cdns_probe(cdns); From f936fa7a954b262cb3908bbc8f01ba19dfaf9fbf Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Wed, 26 Oct 2022 12:02:05 +0100 Subject: [PATCH 0163/1823] soundwire: qcom: reinit broadcast completion For some reason we never reinit the broadcast completion, there is a danger that broadcast commands could be treated as completed by driver from previous complete status. Fix this by reinitializing the completion before sending a broadcast command. Fixes: ddea6cf7b619 ("soundwire: qcom: update register read/write routine") Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20221026110210.6575-2-srinivas.kandagatla@linaro.org Signed-off-by: Vinod Koul --- drivers/soundwire/qcom.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index b33d5db494a5..8f1a1eb017a2 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -344,6 +344,9 @@ static int qcom_swrm_cmd_fifo_wr_cmd(struct qcom_swrm_ctrl *swrm, u8 cmd_data, if (swrm_wait_for_wr_fifo_avail(swrm)) return SDW_CMD_FAIL_OTHER; + if (cmd_id == SWR_BROADCAST_CMD_ID) + reinit_completion(&swrm->broadcast); + /* Its assumed that write is okay as we do not get any status back */ swrm->reg_write(swrm, SWRM_CMD_FIFO_WR_CMD, val); From 49a467310dc4fae591a3547860ee04d8730780f4 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Wed, 26 Oct 2022 12:02:06 +0100 Subject: [PATCH 0164/1823] soundwire: qcom: check for outanding writes before doing a read Reading will increase the fifo count, so check for outstanding cmd wrt. write fifo depth to avoid overflow as read will also increase write fifo cnt. Fixes: a661308c34de ("soundwire: qcom: wait for fifo space to be available before read/write") Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20221026110210.6575-3-srinivas.kandagatla@linaro.org Signed-off-by: Vinod Koul --- drivers/soundwire/qcom.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index 8f1a1eb017a2..cee2b2223141 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -380,6 +380,12 @@ static int qcom_swrm_cmd_fifo_rd_cmd(struct qcom_swrm_ctrl *swrm, val = swrm_get_packed_reg_val(&swrm->rcmd_id, len, dev_addr, reg_addr); + /* + * Check for outstanding cmd wrt. write fifo depth to avoid + * overflow as read will also increase write fifo cnt. + */ + swrm_wait_for_wr_fifo_avail(swrm); + /* wait for FIFO RD to complete to avoid overflow */ usleep_range(100, 105); swrm->reg_write(swrm, SWRM_CMD_FIFO_RD_CMD, val); From 6ec27c53886c8963729885bcf2dd996eba2767a7 Mon Sep 17 00:00:00 2001 From: Chen Zhongjin Date: Fri, 28 Oct 2022 11:16:03 +0800 Subject: [PATCH 0165/1823] ASoC: core: Fix use-after-free in snd_soc_exit() KASAN reports a use-after-free: BUG: KASAN: use-after-free in device_del+0xb5b/0xc60 Read of size 8 at addr ffff888008655050 by task rmmod/387 CPU: 2 PID: 387 Comm: rmmod Hardware name: QEMU Standard PC (i440FX + PIIX, 1996) Call Trace: dump_stack_lvl+0x79/0x9a print_report+0x17f/0x47b kasan_report+0xbb/0xf0 device_del+0xb5b/0xc60 platform_device_del.part.0+0x24/0x200 platform_device_unregister+0x2e/0x40 snd_soc_exit+0xa/0x22 [snd_soc_core] __do_sys_delete_module.constprop.0+0x34f/0x5b0 do_syscall_64+0x3a/0x90 entry_SYSCALL_64_after_hwframe+0x63/0xcd ... It's bacause in snd_soc_init(), snd_soc_util_init() is possble to fail, but its ret is ignored, which makes soc_dummy_dev unregistered twice. snd_soc_init() snd_soc_util_init() platform_device_register_simple(soc_dummy_dev) platform_driver_register() # fail platform_device_unregister(soc_dummy_dev) platform_driver_register() # success ... snd_soc_exit() snd_soc_util_exit() # soc_dummy_dev will be unregistered for second time To fix it, handle error and stop snd_soc_init() when util_init() fail. Also clean debugfs when util_init() or driver_register() fail. Fixes: fb257897bf20 ("ASoC: Work around allmodconfig failure") Signed-off-by: Chen Zhongjin Link: https://lore.kernel.org/r/20221028031603.59416-1-chenzhongjin@huawei.com Signed-off-by: Mark Brown --- sound/soc/soc-core.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 12a82f5a3ff6..a409fbed8f34 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3477,10 +3477,23 @@ EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_link_cpus); static int __init snd_soc_init(void) { - snd_soc_debugfs_init(); - snd_soc_util_init(); + int ret; - return platform_driver_register(&soc_driver); + snd_soc_debugfs_init(); + ret = snd_soc_util_init(); + if (ret) + goto err_util_init; + + ret = platform_driver_register(&soc_driver); + if (ret) + goto err_register; + return 0; + +err_register: + snd_soc_util_exit(); +err_util_init: + snd_soc_debugfs_exit(); + return ret; } module_init(snd_soc_init); From c1de355e04e6ae8da21cb6bfbd5bbfd8deee0fe5 Mon Sep 17 00:00:00 2001 From: Jay Fang Date: Fri, 28 Oct 2022 10:37:39 +0800 Subject: [PATCH 0166/1823] MAINTAINERS: Update HiSilicon SFC Driver maintainer Add Jay Fang as the maintainer of the HiSilicon SFC Driver, replacing John Garry. Signed-off-by: Jay Fang Acked-by: John Garry Link: https://lore.kernel.org/r/20221028023739.4113998-1-f.fangjian@huawei.com Signed-off-by: Mark Brown --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 653501e7aa03..6297d5d1dd90 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9260,7 +9260,7 @@ S: Maintained F: drivers/crypto/hisilicon/trng/trng.c HISILICON V3XX SPI NOR FLASH Controller Driver -M: John Garry +M: Jay Fang S: Maintained W: http://www.hisilicon.com F: drivers/spi/spi-hisi-sfc-v3xx.c From 4a6f278d4827b59ba26ceae0ff4529ee826aa258 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 28 Oct 2022 14:25:20 +0200 Subject: [PATCH 0167/1823] fuse: add file_modified() to fallocate Add missing file_modified() call to fuse_file_fallocate(). Without this fallocate on fuse failed to clear privileges. Fixes: 05ba1f082300 ("fuse: add FALLOCATE operation") Cc: Signed-off-by: Miklos Szeredi --- fs/fuse/file.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 1a3afd469e3a..71bfb663aac5 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -3001,6 +3001,10 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, goto out; } + err = file_modified(file); + if (err) + goto out; + if (!(mode & FALLOC_FL_KEEP_SIZE)) set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state); From 04948e757148f870a31f4887ea2239403f516c3c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 26 Oct 2022 18:21:16 +0200 Subject: [PATCH 0168/1823] phy: qcom-qmp-combo: fix NULL-deref on runtime resume Commit fc64623637da ("phy: qcom-qmp-combo,usb: add support for separate PCS_USB region") started treating the PCS_USB registers as potentially separate from the PCS registers but used the wrong base when no PCS_USB offset has been provided. Fix the PCS_USB base used at runtime resume to prevent dereferencing a NULL pointer on platforms that do not provide a PCS_USB offset (e.g. SC7180). Fixes: fc64623637da ("phy: qcom-qmp-combo,usb: add support for separate PCS_USB region") Cc: stable@vger.kernel.org # 5.20 Signed-off-by: Johan Hovold Reviewed-by: Dmitry Baryshkov Reviewed-by: Andrew Halaney Link: https://lore.kernel.org/r/20221026162116.26462-1-johan+linaro@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp-combo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c index 9807c4d935cd..ba9d761ec49a 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c @@ -2240,7 +2240,7 @@ static void qmp_combo_enable_autonomous_mode(struct qmp_phy *qphy) static void qmp_combo_disable_autonomous_mode(struct qmp_phy *qphy) { const struct qmp_phy_cfg *cfg = qphy->cfg; - void __iomem *pcs_usb = qphy->pcs_usb ?: qphy->pcs_usb; + void __iomem *pcs_usb = qphy->pcs_usb ?: qphy->pcs; void __iomem *pcs_misc = qphy->pcs_misc; /* Disable i/o clamp_n on resume for normal mode */ From 8d06679b25fc6813eb2438fac7fa13f4f3c2ef37 Mon Sep 17 00:00:00 2001 From: Stefan Binding Date: Fri, 28 Oct 2022 11:27:42 +0100 Subject: [PATCH 0169/1823] ALSA: hda/realtek: Add quirk for ASUS Zenbook using CS35L41 This Asus Zenbook laptop use Realtek HDA codec combined with 2xCS35L41 Amplifiers using I2C with Internal Boost. Signed-off-by: Stefan Binding Cc: Link: https://lore.kernel.org/r/20221028102742.2588687-1-sbinding@opensource.cirrus.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 701a72ec5629..b4f7ff8cfe41 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9404,6 +9404,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1e8e, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA401), SND_PCI_QUIRK(0x1043, 0x1c52, "ASUS Zephyrus G15 2022", ALC289_FIXUP_ASUS_GA401), SND_PCI_QUIRK(0x1043, 0x1f11, "ASUS Zephyrus G14", ALC289_FIXUP_ASUS_GA401), + SND_PCI_QUIRK(0x1043, 0x1f12, "ASUS UM5302", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x1f92, "ASUS ROG Flow X16", ALC289_FIXUP_ASUS_GA401), SND_PCI_QUIRK(0x1043, 0x3030, "ASUS ZN270IE", ALC256_FIXUP_ASUS_AIO_GPIO2), SND_PCI_QUIRK(0x1043, 0x831a, "ASUS P901", ALC269_FIXUP_STEREO_DMIC), From 2124becad797245d49252d2d733aee0322233d7e Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Fri, 14 Oct 2022 07:11:36 -0500 Subject: [PATCH 0170/1823] ACPI: x86: Add another system to quirk list for forcing StorageD3Enable commit 018d6711c26e4 ("ACPI: x86: Add a quirk for Dell Inspiron 14 2-in-1 for StorageD3Enable") introduced a quirk to allow a system with ambiguous use of _ADR 0 to force StorageD3Enable. Julius Brockmann reports that Inspiron 16 5625 suffers that same symptoms. Add this other system to the list as well. Link: https://bugzilla.kernel.org/show_bug.cgi?id=216440 Reported-and-tested-by: Julius Brockmann Signed-off-by: Mario Limonciello Signed-off-by: Rafael J. Wysocki --- drivers/acpi/x86/utils.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/acpi/x86/utils.c b/drivers/acpi/x86/utils.c index f8a2cbdc0ce2..d7d3f1669d4c 100644 --- a/drivers/acpi/x86/utils.c +++ b/drivers/acpi/x86/utils.c @@ -219,6 +219,12 @@ static const struct dmi_system_id force_storage_d3_dmi[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 14 7425 2-in-1"), } }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 16 5625"), + } + }, {} }; From 07c0d131cc0fe1f3981a42958fc52d573d303d89 Mon Sep 17 00:00:00 2001 From: Chen Zhongjin Date: Tue, 25 Oct 2022 10:41:46 +0800 Subject: [PATCH 0171/1823] RDMA/core: Fix null-ptr-deref in ib_core_cleanup() KASAN reported a null-ptr-deref error: KASAN: null-ptr-deref in range [0x0000000000000118-0x000000000000011f] CPU: 1 PID: 379 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996) RIP: 0010:destroy_workqueue+0x2f/0x740 RSP: 0018:ffff888016137df8 EFLAGS: 00000202 ... Call Trace: ib_core_cleanup+0xa/0xa1 [ib_core] __do_sys_delete_module.constprop.0+0x34f/0x5b0 do_syscall_64+0x3a/0x90 entry_SYSCALL_64_after_hwframe+0x63/0xcd RIP: 0033:0x7fa1a0d221b7 ... It is because the fail of roce_gid_mgmt_init() is ignored: ib_core_init() roce_gid_mgmt_init() gid_cache_wq = alloc_ordered_workqueue # fail ... ib_core_cleanup() roce_gid_mgmt_cleanup() destroy_workqueue(gid_cache_wq) # destroy an unallocated wq Fix this by catching the fail of roce_gid_mgmt_init() in ib_core_init(). Fixes: 03db3a2d81e6 ("IB/core: Add RoCE GID table management") Signed-off-by: Chen Zhongjin Link: https://lore.kernel.org/r/20221025024146.109137-1-chenzhongjin@huawei.com Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/device.c | 10 +++++++++- drivers/infiniband/core/nldev.c | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index ae60c73babcc..b69e2c4e4d2a 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -2815,10 +2815,18 @@ static int __init ib_core_init(void) nldev_init(); rdma_nl_register(RDMA_NL_LS, ibnl_ls_cb_table); - roce_gid_mgmt_init(); + ret = roce_gid_mgmt_init(); + if (ret) { + pr_warn("Couldn't init RoCE GID management\n"); + goto err_parent; + } return 0; +err_parent: + rdma_nl_unregister(RDMA_NL_LS); + nldev_exit(); + unregister_pernet_device(&rdma_dev_net_ops); err_compat: unregister_blocking_lsm_notifier(&ibdev_lsm_nb); err_sa: diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c index b92358f606d0..12dc97067ed2 100644 --- a/drivers/infiniband/core/nldev.c +++ b/drivers/infiniband/core/nldev.c @@ -2537,7 +2537,7 @@ void __init nldev_init(void) rdma_nl_register(RDMA_NL_NLDEV, nldev_cb_table); } -void __exit nldev_exit(void) +void nldev_exit(void) { rdma_nl_unregister(RDMA_NL_NLDEV); } From 7a47e077e503feb73d56e491ce89aa73b67a3972 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 25 Oct 2022 18:32:32 +0300 Subject: [PATCH 0172/1823] RDMA/qedr: clean up work queue on failure in qedr_alloc_resources() Add a check for if create_singlethread_workqueue() fails and also destroy the work queue on failure paths. Fixes: e411e0587e0d ("RDMA/qedr: Add iWARP connection management functions") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/Y1gBkDucQhhWj5YM@kili Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/qedr/main.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/qedr/main.c b/drivers/infiniband/hw/qedr/main.c index 5152f10d2e6d..ba0c3e4c07d8 100644 --- a/drivers/infiniband/hw/qedr/main.c +++ b/drivers/infiniband/hw/qedr/main.c @@ -344,6 +344,10 @@ static int qedr_alloc_resources(struct qedr_dev *dev) if (IS_IWARP(dev)) { xa_init(&dev->qps); dev->iwarp_wq = create_singlethread_workqueue("qedr_iwarpq"); + if (!dev->iwarp_wq) { + rc = -ENOMEM; + goto err1; + } } /* Allocate Status blocks for CNQ */ @@ -351,7 +355,7 @@ static int qedr_alloc_resources(struct qedr_dev *dev) GFP_KERNEL); if (!dev->sb_array) { rc = -ENOMEM; - goto err1; + goto err_destroy_wq; } dev->cnq_array = kcalloc(dev->num_cnq, @@ -402,6 +406,9 @@ err3: kfree(dev->cnq_array); err2: kfree(dev->sb_array); +err_destroy_wq: + if (IS_IWARP(dev)) + destroy_workqueue(dev->iwarp_wq); err1: kfree(dev->sgid_tbl); return rc; From 6a564338a23cefcfc29c4a535b98402d13efdda6 Mon Sep 17 00:00:00 2001 From: Maarten Zanders Date: Fri, 28 Oct 2022 16:11:28 +0200 Subject: [PATCH 0173/1823] ASoC: fsl_asrc fsl_esai fsl_sai: allow CONFIG_PM=N When CONFIG_PM=N, pm_runtime_put_sync() returns -ENOSYS which breaks the probe function of these drivers. Other users of pm_runtime_put_sync() typically don't check the return value. In order to keep the program flow as intended, check for -ENOSYS. This commit is similar to commit 0434d3f (omap-mailbox.c). Fixes: cab04ab5900f ("ASoC: fsl_asrc: Don't use devm_regmap_init_mmio_clk") Fixes: 203773e39347 ("ASoC: fsl_esai: Don't use devm_regmap_init_mmio_clk") Fixes: 2277e7e36b4b ("ASoC: fsl_sai: Don't use devm_regmap_init_mmio_clk") Signed-off-by: Maarten Zanders Reviewed-by: Daniel Baluta Link: https://lore.kernel.org/r/20221028141129.100702-1-maarten.zanders@mind.be Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_asrc.c | 2 +- sound/soc/fsl/fsl_esai.c | 2 +- sound/soc/fsl/fsl_sai.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index 936aef5d2767..e16e7b3fa96c 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -1232,7 +1232,7 @@ static int fsl_asrc_probe(struct platform_device *pdev) } ret = pm_runtime_put_sync(&pdev->dev); - if (ret < 0) + if (ret < 0 && ret != -ENOSYS) goto err_pm_get_sync; ret = devm_snd_soc_register_component(&pdev->dev, &fsl_asrc_component, diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index 5c21fc490fce..17fefd27ec90 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -1069,7 +1069,7 @@ static int fsl_esai_probe(struct platform_device *pdev) regmap_write(esai_priv->regmap, REG_ESAI_RSMB, 0); ret = pm_runtime_put_sync(&pdev->dev); - if (ret < 0) + if (ret < 0 && ret != -ENOSYS) goto err_pm_get_sync; /* diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 81f89f6767a2..e60c7b344562 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -1446,7 +1446,7 @@ static int fsl_sai_probe(struct platform_device *pdev) } ret = pm_runtime_put_sync(dev); - if (ret < 0) + if (ret < 0 && ret != -ENOSYS) goto err_pm_get_sync; /* From f11a74b45d330ad1ab986852b099747161052526 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 27 Oct 2022 15:52:31 +0200 Subject: [PATCH 0174/1823] efi: efivars: Fix variable writes with unsupported query_variable_store() Commit 8a254d90a775 ("efi: efivars: Fix variable writes without query_variable_store()") addressed an issue that was introduced during the EFI variable store refactor, where alternative implementations of the efivars layer that lacked query_variable_store() would no longer work. Unfortunately, there is another case to consider here, which was missed: if the efivars layer is backed by the EFI runtime services as usual, but the EFI implementation predates the introduction of QueryVariableInfo(), we will return EFI_UNSUPPORTED, and this is no longer being dealt with correctly. So let's fix this, and while at it, clean up the code a bit, by merging the check_var_size() routines as well as their callers. Cc: # v6.0 Fixes: bbc6d2c6ef22 ("efi: vars: Switch to new wrapper layer") Signed-off-by: Ard Biesheuvel Tested-by: Aditya Garg --- drivers/firmware/efi/vars.c | 68 +++++++++++-------------------------- 1 file changed, 20 insertions(+), 48 deletions(-) diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c index 433b61587139..0ba9f18312f5 100644 --- a/drivers/firmware/efi/vars.c +++ b/drivers/firmware/efi/vars.c @@ -21,29 +21,22 @@ static struct efivars *__efivars; static DEFINE_SEMAPHORE(efivars_lock); -static efi_status_t check_var_size(u32 attributes, unsigned long size) +static efi_status_t check_var_size(bool nonblocking, u32 attributes, + unsigned long size) { const struct efivar_operations *fops; + efi_status_t status; fops = __efivars->ops; if (!fops->query_variable_store) + status = EFI_UNSUPPORTED; + else + status = fops->query_variable_store(attributes, size, + nonblocking); + if (status == EFI_UNSUPPORTED) return (size <= SZ_64K) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES; - - return fops->query_variable_store(attributes, size, false); -} - -static -efi_status_t check_var_size_nonblocking(u32 attributes, unsigned long size) -{ - const struct efivar_operations *fops; - - fops = __efivars->ops; - - if (!fops->query_variable_store) - return (size <= SZ_64K) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES; - - return fops->query_variable_store(attributes, size, true); + return status; } /** @@ -195,26 +188,6 @@ efi_status_t efivar_get_next_variable(unsigned long *name_size, } EXPORT_SYMBOL_NS_GPL(efivar_get_next_variable, EFIVAR); -/* - * efivar_set_variable_blocking() - local helper function for set_variable - * - * Must be called with efivars_lock held. - */ -static efi_status_t -efivar_set_variable_blocking(efi_char16_t *name, efi_guid_t *vendor, - u32 attr, unsigned long data_size, void *data) -{ - efi_status_t status; - - if (data_size > 0) { - status = check_var_size(attr, data_size + - ucs2_strsize(name, 1024)); - if (status != EFI_SUCCESS) - return status; - } - return __efivars->ops->set_variable(name, vendor, attr, data_size, data); -} - /* * efivar_set_variable_locked() - set a variable identified by name/vendor * @@ -228,23 +201,21 @@ efi_status_t efivar_set_variable_locked(efi_char16_t *name, efi_guid_t *vendor, efi_set_variable_t *setvar; efi_status_t status; - if (!nonblocking) - return efivar_set_variable_blocking(name, vendor, attr, - data_size, data); + if (data_size > 0) { + status = check_var_size(nonblocking, attr, + data_size + ucs2_strsize(name, 1024)); + if (status != EFI_SUCCESS) + return status; + } /* * If no _nonblocking variant exists, the ordinary one * is assumed to be non-blocking. */ - setvar = __efivars->ops->set_variable_nonblocking ?: - __efivars->ops->set_variable; + setvar = __efivars->ops->set_variable_nonblocking; + if (!setvar || !nonblocking) + setvar = __efivars->ops->set_variable; - if (data_size > 0) { - status = check_var_size_nonblocking(attr, data_size + - ucs2_strsize(name, 1024)); - if (status != EFI_SUCCESS) - return status; - } return setvar(name, vendor, attr, data_size, data); } EXPORT_SYMBOL_NS_GPL(efivar_set_variable_locked, EFIVAR); @@ -264,7 +235,8 @@ efi_status_t efivar_set_variable(efi_char16_t *name, efi_guid_t *vendor, if (efivar_lock()) return EFI_ABORTED; - status = efivar_set_variable_blocking(name, vendor, attr, data_size, data); + status = efivar_set_variable_locked(name, vendor, attr, data_size, + data, false); efivar_unlock(); return status; } From ae4b3c1252f0fd0951d2f072a02ba46cac8d6c92 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Wed, 26 Oct 2022 16:56:33 +0100 Subject: [PATCH 0175/1823] spi: tegra210-quad: Don't initialise DMA if not supported The following error messages are observed on boot for Tegra234 ... ERR KERN tegra-qspi 3270000.spi: cannot use DMA: -19 ERR KERN tegra-qspi 3270000.spi: falling back to PIO Tegra234 does not support DMA for the QSPI and so initialising the DMA is expected to fail. The above error messages are misleading for devices that don't support DMA and so fix this by skipping the DMA initialisation for devices that don't support DMA. Signed-off-by: Jon Hunter Acked-by: Thierry Reding Link: https://lore.kernel.org/r/20221026155633.141792-1-jonathanh@nvidia.com Signed-off-by: Mark Brown --- drivers/spi/spi-tegra210-quad.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/spi/spi-tegra210-quad.c b/drivers/spi/spi-tegra210-quad.c index 904972606bd4..10f0c5a6e0dc 100644 --- a/drivers/spi/spi-tegra210-quad.c +++ b/drivers/spi/spi-tegra210-quad.c @@ -720,6 +720,9 @@ static int tegra_qspi_start_cpu_based_transfer(struct tegra_qspi *qspi, struct s static void tegra_qspi_deinit_dma(struct tegra_qspi *tqspi) { + if (!tqspi->soc_data->has_dma) + return; + if (tqspi->tx_dma_buf) { dma_free_coherent(tqspi->dev, tqspi->dma_buf_size, tqspi->tx_dma_buf, tqspi->tx_dma_phys); @@ -750,6 +753,9 @@ static int tegra_qspi_init_dma(struct tegra_qspi *tqspi) u32 *dma_buf; int err; + if (!tqspi->soc_data->has_dma) + return 0; + dma_chan = dma_request_chan(tqspi->dev, "rx"); if (IS_ERR(dma_chan)) { err = PTR_ERR(dma_chan); From 39a654f39efb035b961359db91f15a347e8d7fd8 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 28 Oct 2022 21:30:29 +0530 Subject: [PATCH 0176/1823] MAINTAINERS: Add Manivannan Sadhasivam as Qcom PCIe RC maintainer Stan is moving out of mm-sol and decided not to carry on the maintainership duties of the Qcom PCIe RC driver. Since I'm already maintaining the Qcom PCIe EP driver, I'm volunteering myself to maintain the RC driver also. Link: https://lore.kernel.org/r/20221028160029.44483-1-manivannan.sadhasivam@linaro.org Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Cc: Stanimir Varbanov --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index cf0f18502372..05ca3a7f6967 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16036,7 +16036,7 @@ F: Documentation/devicetree/bindings/pci/microchip* F: drivers/pci/controller/*microchip* PCIE DRIVER FOR QUALCOMM MSM -M: Stanimir Varbanov +M: Manivannan Sadhasivam L: linux-pci@vger.kernel.org L: linux-arm-msm@vger.kernel.org S: Maintained From cadaa773bcf161184fa428180516bae33a7bc667 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 27 Oct 2022 13:57:45 +0200 Subject: [PATCH 0177/1823] arm64: dts: qcom: sm8250: Disable the not yet supported cluster idle state To support the deeper cluster idle state for sm8250 platforms, some additional synchronization is needed between the rpmh-rsc device and the CPU cluster PM domain. Until that is supported, let's disable the cluster idle state. This fixes a problem that has been reported for the Qcom RB5 platform (see below), but most likely other sm8250 platforms suffers from similar issues, so let's make the fix generic for sm8250. vreg_l11c_3p3: failed to enable: -ETIMEDOUT qcom-rpmh-regulator 18200000.rsc:pm8150l-rpmh-regulators: ldo11: devm_regulator_register() failed, ret=-110 qcom-rpmh-regulator: probe of 18200000.rsc:pm8150l-rpmh-regulators failed with error -110 Reported-by: Amit Pundir Fixes: 32bc936d7321 ("arm64: dts: qcom: sm8250: Add cpuidle states") Signed-off-by: Ulf Hansson Tested-by: Amit Pundir Reviewed-by: Sudeep Holla Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20221027115745.240516-1-ulf.hansson@linaro.org --- arch/arm64/boot/dts/qcom/sm8250.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/qcom/sm8250.dtsi b/arch/arm64/boot/dts/qcom/sm8250.dtsi index a5b62cadb129..e276eed1f8e2 100644 --- a/arch/arm64/boot/dts/qcom/sm8250.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8250.dtsi @@ -334,6 +334,7 @@ exit-latency-us = <6562>; min-residency-us = <9987>; local-timer-stop; + status = "disabled"; }; }; }; From bfc3b0f05653a28c8d41067a2aa3875d1f982e3e Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Sun, 9 Oct 2022 20:29:36 +0200 Subject: [PATCH 0178/1823] tools/nolibc: Fix missing strlen() definition and infinite loop with gcc-12 When built at -Os, gcc-12 recognizes an strlen() pattern in nolibc_strlen() and replaces it with a jump to strlen(), which is not defined as a symbol and breaks compilation. Worse, when the function is called strlen(), the function is simply replaced with a jump to itself, hence becomes an infinite loop. One way to avoid this is to always set -ffreestanding, but the calling code doesn't know this and there's no way (either via attributes or pragmas) to globally enable it from include files, effectively leaving a painful situation for the caller. Alexey suggested to place an empty asm() statement inside the loop to stop gcc from recognizing a well-known pattern, which happens to work pretty fine. At least it allows us to make sure our local definition is not replaced with a self jump. The function only needs to be renamed back to strlen() so that the symbol exists, which implies that nolibc_strlen() which is used on variable strings has to be declared as a macro that points back to it before the strlen() macro is redifined. It was verified to produce valid code with gcc 3.4 to 12.1 at different optimization levels, and both with constant and variable strings. In case this problem surfaces again in the future, an alternate approach consisting in adding an optimize("no-tree-loop-distribute-patterns") function attribute for gcc>=12 worked as well but is less pretty. Reported-by: kernel test robot Link: https://lore.kernel.org/r/202210081618.754a77db-yujie.liu@intel.com Fixes: 66b6f755ad45 ("rcutorture: Import a copy of nolibc") Fixes: 96980b833a21 ("tools/nolibc/string: do not use __builtin_strlen() at -O0") Cc: "Paul E. McKenney" Cc: Alexey Dobriyan Signed-off-by: Willy Tarreau Signed-off-by: Paul E. McKenney --- tools/include/nolibc/string.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tools/include/nolibc/string.h b/tools/include/nolibc/string.h index bef35bee9c44..718a405ffbc3 100644 --- a/tools/include/nolibc/string.h +++ b/tools/include/nolibc/string.h @@ -125,14 +125,18 @@ char *strcpy(char *dst, const char *src) } /* this function is only used with arguments that are not constants or when - * it's not known because optimizations are disabled. + * it's not known because optimizations are disabled. Note that gcc 12 + * recognizes an strlen() pattern and replaces it with a jump to strlen(), + * thus itself, hence the asm() statement below that's meant to disable this + * confusing practice. */ static __attribute__((unused)) -size_t nolibc_strlen(const char *str) +size_t strlen(const char *str) { size_t len; - for (len = 0; str[len]; len++); + for (len = 0; str[len]; len++) + asm(""); return len; } @@ -140,13 +144,12 @@ size_t nolibc_strlen(const char *str) * the two branches, then will rely on an external definition of strlen(). */ #if defined(__OPTIMIZE__) +#define nolibc_strlen(x) strlen(x) #define strlen(str) ({ \ __builtin_constant_p((str)) ? \ __builtin_strlen((str)) : \ nolibc_strlen((str)); \ }) -#else -#define strlen(str) nolibc_strlen((str)) #endif static __attribute__((unused)) From b3f4f51ea68a495f8a5956064c33dce711a2df91 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Fri, 21 Oct 2022 08:01:53 +0200 Subject: [PATCH 0179/1823] tools/nolibc/string: Fix memcmp() implementation The C standard says that memcmp() must treat the buffers as consisting of "unsigned chars". If char happens to be unsigned, the casts are ok, but then obviously the c1 variable can never contain a negative value. And when char is signed, the casts are wrong, and there's still a problem with using an 8-bit quantity to hold the difference, because that can range from -255 to +255. For example, assuming char is signed, comparing two 1-byte buffers, one containing 0x00 and another 0x80, the current implementation would return -128 for both memcmp(a, b, 1) and memcmp(b, a, 1), whereas one of those should of course return something positive. Signed-off-by: Rasmus Villemoes Fixes: 66b6f755ad45 ("rcutorture: Import a copy of nolibc") Cc: stable@vger.kernel.org # v5.0+ Signed-off-by: Willy Tarreau Signed-off-by: Paul E. McKenney --- tools/include/nolibc/string.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/include/nolibc/string.h b/tools/include/nolibc/string.h index 718a405ffbc3..ad97c0d522b8 100644 --- a/tools/include/nolibc/string.h +++ b/tools/include/nolibc/string.h @@ -19,9 +19,9 @@ static __attribute__((unused)) int memcmp(const void *s1, const void *s2, size_t n) { size_t ofs = 0; - char c1 = 0; + int c1 = 0; - while (ofs < n && !(c1 = ((char *)s1)[ofs] - ((char *)s2)[ofs])) { + while (ofs < n && !(c1 = ((unsigned char *)s1)[ofs] - ((unsigned char *)s2)[ofs])) { ofs++; } return c1; From 8bc8824d30193eb7755043d5bb65fa7f0d11a595 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Wed, 12 Oct 2022 13:09:28 +0200 Subject: [PATCH 0180/1823] riscv: dts: sifive unleashed: Add PWM controlled LEDs This adds the 4 PWM controlled green LEDs to the HiFive Unleashed device tree. The schematic doesn't specify any special function for the LEDs, so they're added here without any default triggers and named d1, d2, d3 and d4 just like in the schematic. Signed-off-by: Emil Renner Berthing Reviewed-by: Conor Dooley Tested-by: Conor Dooley Link: https://lore.kernel.org/r/20221012110928.352910-1-emil.renner.berthing@canonical.com Signed-off-by: Palmer Dabbelt --- .../boot/dts/sifive/hifive-unleashed-a00.dts | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts b/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts index ced0d4e47938..900a50526d77 100644 --- a/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts +++ b/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts @@ -3,6 +3,8 @@ #include "fu540-c000.dtsi" #include +#include +#include /* Clock frequency (in Hz) of the PCB crystal for rtcclk */ #define RTCCLK_FREQ 1000000 @@ -42,6 +44,42 @@ compatible = "gpio-restart"; gpios = <&gpio 10 GPIO_ACTIVE_LOW>; }; + + led-controller { + compatible = "pwm-leds"; + + led-d1 { + pwms = <&pwm0 0 7812500 PWM_POLARITY_INVERTED>; + active-low; + color = ; + max-brightness = <255>; + label = "d1"; + }; + + led-d2 { + pwms = <&pwm0 1 7812500 PWM_POLARITY_INVERTED>; + active-low; + color = ; + max-brightness = <255>; + label = "d2"; + }; + + led-d3 { + pwms = <&pwm0 2 7812500 PWM_POLARITY_INVERTED>; + active-low; + color = ; + max-brightness = <255>; + label = "d3"; + }; + + led-d4 { + pwms = <&pwm0 3 7812500 PWM_POLARITY_INVERTED>; + active-low; + color = ; + max-brightness = <255>; + label = "d4"; + }; + }; }; &uart0 { From 6f7630b1b5bc672b54c1285ee6aba752b446672c Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 28 Oct 2022 15:32:07 -0700 Subject: [PATCH 0181/1823] fortify: Capture __bos() results in const temp vars In two recent run-time memcpy() bound checking bug reports (NFS[1] and JFS[2]), the _detection_ was working correctly (in the sense that the requested copy size was larger than the destination field size), but the _warning text_ was showing the destination field size as SIZE_MAX ("unknown size"). This should be impossible, since the detection function will explicitly give up if the destination field size is unknown. For example, the JFS warning was: memcpy: detected field-spanning write (size 132) of single field "ip->i_link" at fs/jfs/namei.c:950 (size 18446744073709551615) Other cases of this warning (e.g.[3]) have reported correctly, and the reproducer only happens under GCC (at least 10.2 and 12.1), so this currently appears to be a GCC bug. Explicitly capturing the __builtin_object_size() results in const temporary variables fixes the report. For example, the JFS reproducer now correctly reports the field size (128): memcpy: detected field-spanning write (size 132) of single field "ip->i_link" at fs/jfs/namei.c:950 (size 128) Examination of the .text delta (which is otherwise identical), shows the literal value used in the report changing: - mov $0xffffffffffffffff,%rcx + mov $0x80,%ecx [1] https://lore.kernel.org/lkml/Y0zEzZwhOxTDcBTB@codemonkey.org.uk/ [2] https://syzkaller.appspot.com/bug?id=23d613df5259b977dac1696bec77f61a85890e3d [3] https://lore.kernel.org/all/202210110948.26b43120-yujie.liu@intel.com/ Cc: "Dr. David Alan Gilbert" Cc: llvm@lists.linux.dev Cc: linux-hardening@vger.kernel.org Signed-off-by: Kees Cook --- include/linux/fortify-string.h | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/include/linux/fortify-string.h b/include/linux/fortify-string.h index 4029fe368a4f..0f00a551939a 100644 --- a/include/linux/fortify-string.h +++ b/include/linux/fortify-string.h @@ -441,13 +441,18 @@ __FORTIFY_INLINE bool fortify_memcpy_chk(__kernel_size_t size, #define __fortify_memcpy_chk(p, q, size, p_size, q_size, \ p_size_field, q_size_field, op) ({ \ - size_t __fortify_size = (size_t)(size); \ - WARN_ONCE(fortify_memcpy_chk(__fortify_size, p_size, q_size, \ - p_size_field, q_size_field, #op), \ + const size_t __fortify_size = (size_t)(size); \ + const size_t __p_size = (p_size); \ + const size_t __q_size = (q_size); \ + const size_t __p_size_field = (p_size_field); \ + const size_t __q_size_field = (q_size_field); \ + WARN_ONCE(fortify_memcpy_chk(__fortify_size, __p_size, \ + __q_size, __p_size_field, \ + __q_size_field, #op), \ #op ": detected field-spanning write (size %zu) of single %s (size %zu)\n", \ __fortify_size, \ "field \"" #p "\" at " __FILE__ ":" __stringify(__LINE__), \ - p_size_field); \ + __p_size_field); \ __underlying_##op(p, q, __fortify_size); \ }) From 7354c9024f2835f6122ed9612e21ab379df050f9 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 27 Oct 2022 14:21:07 -0700 Subject: [PATCH 0182/1823] netlink: hide validation union fields from kdoc Mark the validation fields as private, users shouldn't set them directly and they are too complicated to explain in a more succinct way (there's already a long explanation in the comment above). The strict_start_type field is set directly and has a dedicated comment so move that above the "private" section. Link: https://lore.kernel.org/r/20221027212107.2639255-1-kuba@kernel.org Signed-off-by: Jakub Kicinski --- include/net/netlink.h | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/include/net/netlink.h b/include/net/netlink.h index 4418b1981e31..7db13b3261fc 100644 --- a/include/net/netlink.h +++ b/include/net/netlink.h @@ -317,19 +317,10 @@ struct nla_policy { u8 validation_type; u16 len; union { - const u32 bitfield32_valid; - const u32 mask; - const char *reject_message; - const struct nla_policy *nested_policy; - struct netlink_range_validation *range; - struct netlink_range_validation_signed *range_signed; - struct { - s16 min, max; - u8 network_byte_order:1; - }; - int (*validate)(const struct nlattr *attr, - struct netlink_ext_ack *extack); - /* This entry is special, and used for the attribute at index 0 + /** + * @strict_start_type: first attribute to validate strictly + * + * This entry is special, and used for the attribute at index 0 * only, and specifies special data about the policy, namely it * specifies the "boundary type" where strict length validation * starts for any attribute types >= this value, also, strict @@ -348,6 +339,20 @@ struct nla_policy { * was added to enforce strict validation from thereon. */ u16 strict_start_type; + + /* private: use NLA_POLICY_*() to set */ + const u32 bitfield32_valid; + const u32 mask; + const char *reject_message; + const struct nla_policy *nested_policy; + struct netlink_range_validation *range; + struct netlink_range_validation_signed *range_signed; + struct { + s16 min, max; + u8 network_byte_order:1; + }; + int (*validate)(const struct nlattr *attr, + struct netlink_ext_ack *extack); }; }; From e4ba4554209f626c52e2e57f26cba49a62663c8b Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 27 Oct 2022 20:25:01 -0700 Subject: [PATCH 0183/1823] net: openvswitch: add missing .resv_start_op I missed one of the families in OvS when annotating .resv_start_op. This triggers the warning added in commit ce48ebdd5651 ("genetlink: limit the use of validation workarounds to old ops"). Reported-by: syzbot+40eb8c0447c0e47a7e9b@syzkaller.appspotmail.com Fixes: 9c5d03d36251 ("genetlink: start to validate reserved header bytes") Link: https://lore.kernel.org/r/20221028032501.2724270-1-kuba@kernel.org Signed-off-by: Jakub Kicinski --- net/openvswitch/datapath.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 155263e73512..8b84869eb2ac 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -2544,6 +2544,7 @@ struct genl_family dp_vport_genl_family __ro_after_init = { .parallel_ops = true, .small_ops = dp_vport_genl_ops, .n_small_ops = ARRAY_SIZE(dp_vport_genl_ops), + .resv_start_op = OVS_VPORT_CMD_SET + 1, .mcgrps = &ovs_dp_vport_multicast_group, .n_mcgrps = 1, .module = THIS_MODULE, From 1208b93dd901bafe2526fa9db005bbc30e7ae83a Mon Sep 17 00:00:00 2001 From: Govindarajulu Varadarajan Date: Thu, 27 Oct 2022 21:21:59 -0700 Subject: [PATCH 0184/1823] enic: MAINTAINERS: Update enic maintainers Update enic maintainers. Signed-off-by: Govindarajulu Varadarajan Link: https://lore.kernel.org/r/20221028042159.735670-1-govind.varadar@gmail.com Signed-off-by: Jakub Kicinski --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 10c1344b4473..9e437612dd81 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5038,7 +5038,7 @@ F: drivers/scsi/snic/ CISCO VIC ETHERNET NIC DRIVER M: Christian Benvenuti -M: Govindarajulu Varadarajan <_govind@gmx.com> +M: Satish Kharat S: Supported F: drivers/net/ethernet/cisco/enic/ From 8f279fb00bb29def9ac79e28c5d6d8e07d21f3fb Mon Sep 17 00:00:00 2001 From: Pavel Begunkov Date: Thu, 27 Oct 2022 00:25:56 +0100 Subject: [PATCH 0185/1823] udp: advertise ipv6 udp support for msghdr::ubuf_info Mark udp ipv6 as supporting msghdr::ubuf_info. In the original commit SOCK_SUPPORT_ZC was supposed to be set by a udp_init_sock() call from udp6_init_sock(), but d38afeec26ed4 ("tcp/udp: Call inet6_destroy_sock() in IPv6 ...") removed it and so ipv6 udp misses the flag. Cc: # 6.0 Fixes: e993ffe3da4bc ("net: flag sockets supporting msghdr originated zerocopy") Signed-off-by: Pavel Begunkov Signed-off-by: Jakub Kicinski --- net/ipv6/udp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 129ec5a9b0eb..bc65e5b7195b 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -66,6 +66,7 @@ int udpv6_init_sock(struct sock *sk) { skb_queue_head_init(&udp_sk(sk)->reader_queue); sk->sk_destruct = udpv6_destruct_sock; + set_bit(SOCK_SUPPORT_ZC, &sk->sk_socket->flags); return 0; } From fee9ac06647e59a69fb7aec58f25267c134264b4 Mon Sep 17 00:00:00 2001 From: Pavel Begunkov Date: Thu, 27 Oct 2022 00:25:57 +0100 Subject: [PATCH 0186/1823] net: remove SOCK_SUPPORT_ZC from sockmap sockmap replaces ->sk_prot with its own callbacks, we should remove SOCK_SUPPORT_ZC as the new proto doesn't support msghdr::ubuf_info. Cc: # 6.0 Reported-by: Jakub Kicinski Fixes: e993ffe3da4bc ("net: flag sockets supporting msghdr originated zerocopy") Signed-off-by: Pavel Begunkov Signed-off-by: Jakub Kicinski --- include/net/sock.h | 7 +++++++ net/ipv4/tcp_bpf.c | 4 ++-- net/ipv4/udp_bpf.c | 4 ++-- net/unix/unix_bpf.c | 8 ++++---- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/include/net/sock.h b/include/net/sock.h index 22f8bab583dd..5db02546941c 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1889,6 +1889,13 @@ void sock_kfree_s(struct sock *sk, void *mem, int size); void sock_kzfree_s(struct sock *sk, void *mem, int size); void sk_send_sigurg(struct sock *sk); +static inline void sock_replace_proto(struct sock *sk, struct proto *proto) +{ + if (sk->sk_socket) + clear_bit(SOCK_SUPPORT_ZC, &sk->sk_socket->flags); + WRITE_ONCE(sk->sk_prot, proto); +} + struct sockcm_cookie { u64 transmit_time; u32 mark; diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c index a1626afe87a1..c501c329b1db 100644 --- a/net/ipv4/tcp_bpf.c +++ b/net/ipv4/tcp_bpf.c @@ -607,7 +607,7 @@ int tcp_bpf_update_proto(struct sock *sk, struct sk_psock *psock, bool restore) } else { sk->sk_write_space = psock->saved_write_space; /* Pairs with lockless read in sk_clone_lock() */ - WRITE_ONCE(sk->sk_prot, psock->sk_proto); + sock_replace_proto(sk, psock->sk_proto); } return 0; } @@ -620,7 +620,7 @@ int tcp_bpf_update_proto(struct sock *sk, struct sk_psock *psock, bool restore) } /* Pairs with lockless read in sk_clone_lock() */ - WRITE_ONCE(sk->sk_prot, &tcp_bpf_prots[family][config]); + sock_replace_proto(sk, &tcp_bpf_prots[family][config]); return 0; } EXPORT_SYMBOL_GPL(tcp_bpf_update_proto); diff --git a/net/ipv4/udp_bpf.c b/net/ipv4/udp_bpf.c index ff15918b7bdc..e5dc91d0e079 100644 --- a/net/ipv4/udp_bpf.c +++ b/net/ipv4/udp_bpf.c @@ -141,14 +141,14 @@ int udp_bpf_update_proto(struct sock *sk, struct sk_psock *psock, bool restore) if (restore) { sk->sk_write_space = psock->saved_write_space; - WRITE_ONCE(sk->sk_prot, psock->sk_proto); + sock_replace_proto(sk, psock->sk_proto); return 0; } if (sk->sk_family == AF_INET6) udp_bpf_check_v6_needs_rebuild(psock->sk_proto); - WRITE_ONCE(sk->sk_prot, &udp_bpf_prots[family]); + sock_replace_proto(sk, &udp_bpf_prots[family]); return 0; } EXPORT_SYMBOL_GPL(udp_bpf_update_proto); diff --git a/net/unix/unix_bpf.c b/net/unix/unix_bpf.c index 7cf14c6b1725..e9bf15513961 100644 --- a/net/unix/unix_bpf.c +++ b/net/unix/unix_bpf.c @@ -145,12 +145,12 @@ int unix_dgram_bpf_update_proto(struct sock *sk, struct sk_psock *psock, bool re if (restore) { sk->sk_write_space = psock->saved_write_space; - WRITE_ONCE(sk->sk_prot, psock->sk_proto); + sock_replace_proto(sk, psock->sk_proto); return 0; } unix_dgram_bpf_check_needs_rebuild(psock->sk_proto); - WRITE_ONCE(sk->sk_prot, &unix_dgram_bpf_prot); + sock_replace_proto(sk, &unix_dgram_bpf_prot); return 0; } @@ -158,12 +158,12 @@ int unix_stream_bpf_update_proto(struct sock *sk, struct sk_psock *psock, bool r { if (restore) { sk->sk_write_space = psock->saved_write_space; - WRITE_ONCE(sk->sk_prot, psock->sk_proto); + sock_replace_proto(sk, psock->sk_proto); return 0; } unix_stream_bpf_check_needs_rebuild(psock->sk_proto); - WRITE_ONCE(sk->sk_prot, &unix_stream_bpf_prot); + sock_replace_proto(sk, &unix_stream_bpf_prot); return 0; } From e276d62dcfdee6582486e8b8344dd869518e14be Mon Sep 17 00:00:00 2001 From: Pavel Begunkov Date: Thu, 27 Oct 2022 00:25:58 +0100 Subject: [PATCH 0187/1823] net/ulp: remove SOCK_SUPPORT_ZC from tls sockets Remove SOCK_SUPPORT_ZC when we're setting ulp as it might not support msghdr::ubuf_info, e.g. like TLS replacing ->sk_prot with a new set of handlers. Cc: # 6.0 Reported-by: Jakub Kicinski Fixes: e993ffe3da4bc ("net: flag sockets supporting msghdr originated zerocopy") Signed-off-by: Pavel Begunkov Signed-off-by: Jakub Kicinski --- net/ipv4/tcp_ulp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv4/tcp_ulp.c b/net/ipv4/tcp_ulp.c index 7c27aa629af1..9ae50b1bd844 100644 --- a/net/ipv4/tcp_ulp.c +++ b/net/ipv4/tcp_ulp.c @@ -136,6 +136,9 @@ static int __tcp_set_ulp(struct sock *sk, const struct tcp_ulp_ops *ulp_ops) if (icsk->icsk_ulp_ops) goto out_err; + if (sk->sk_socket) + clear_bit(SOCK_SUPPORT_ZC, &sk->sk_socket->flags); + err = ulp_ops->init(sk); if (err) goto out_err; From 71b7786ea478f3c4611deff4d2b9676b0c17c56b Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 27 Oct 2022 00:25:59 +0100 Subject: [PATCH 0188/1823] net: also flag accepted sockets supporting msghdr originated zerocopy Without this only the client initiated tcp sockets have SOCK_SUPPORT_ZC. The listening socket on the server also has it, but the accepted connections didn't, which meant IORING_OP_SEND[MSG]_ZC will always fails with -EOPNOTSUPP. Fixes: e993ffe3da4b ("net: flag sockets supporting msghdr originated zerocopy") Cc: # 6.0 CC: Jens Axboe Link: https://lore.kernel.org/io-uring/20221024141503.22b4e251@kernel.org/T/#m38aa19b0b825758fb97860a38ad13122051f9dda Signed-off-by: Stefan Metzmacher Signed-off-by: Pavel Begunkov Signed-off-by: Jakub Kicinski --- net/ipv4/af_inet.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 3dd02396517d..4728087c42a5 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -754,6 +754,8 @@ int inet_accept(struct socket *sock, struct socket *newsock, int flags, (TCPF_ESTABLISHED | TCPF_SYN_RECV | TCPF_CLOSE_WAIT | TCPF_CLOSE))); + if (test_bit(SOCK_SUPPORT_ZC, &sock->flags)) + set_bit(SOCK_SUPPORT_ZC, &newsock->flags); sock_graft(sk2, newsock); newsock->state = SS_CONNECTED; From 21ce2c121fa07b00b0906bd781590ea362e82ea2 Mon Sep 17 00:00:00 2001 From: Alexandru Tachici Date: Thu, 27 Oct 2022 12:56:55 +0300 Subject: [PATCH 0189/1823] net: ethernet: adi: adin1110: Fix notifiers ADIN1110 was registering netdev_notifiers on each device probe. This leads to warnings/probe failures because of double registration of the same notifier when to adin1110/2111 devices are connected to the same system. Move the registration of netdev_notifiers in module init call, in this way multiple driver instances can use the same notifiers. Fixes: bc93e19d088b ("net: ethernet: adi: Add ADIN1110 support") Signed-off-by: Alexandru Tachici Link: https://lore.kernel.org/r/20221027095655.89890-2-alexandru.tachici@analog.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/adi/adin1110.c | 38 ++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/adi/adin1110.c b/drivers/net/ethernet/adi/adin1110.c index 1744d623999d..606c97610808 100644 --- a/drivers/net/ethernet/adi/adin1110.c +++ b/drivers/net/ethernet/adi/adin1110.c @@ -1512,16 +1512,15 @@ static struct notifier_block adin1110_switchdev_notifier = { .notifier_call = adin1110_switchdev_event, }; -static void adin1110_unregister_notifiers(void *data) +static void adin1110_unregister_notifiers(void) { unregister_switchdev_blocking_notifier(&adin1110_switchdev_blocking_notifier); unregister_switchdev_notifier(&adin1110_switchdev_notifier); unregister_netdevice_notifier(&adin1110_netdevice_nb); } -static int adin1110_setup_notifiers(struct adin1110_priv *priv) +static int adin1110_setup_notifiers(void) { - struct device *dev = &priv->spidev->dev; int ret; ret = register_netdevice_notifier(&adin1110_netdevice_nb); @@ -1536,13 +1535,14 @@ static int adin1110_setup_notifiers(struct adin1110_priv *priv) if (ret < 0) goto err_sdev; - return devm_add_action_or_reset(dev, adin1110_unregister_notifiers, NULL); + return 0; err_sdev: unregister_switchdev_notifier(&adin1110_switchdev_notifier); err_netdev: unregister_netdevice_notifier(&adin1110_netdevice_nb); + return ret; } @@ -1613,10 +1613,6 @@ static int adin1110_probe_netdevs(struct adin1110_priv *priv) if (ret < 0) return ret; - ret = adin1110_setup_notifiers(priv); - if (ret < 0) - return ret; - for (i = 0; i < priv->cfg->ports_nr; i++) { ret = devm_register_netdev(dev, priv->ports[i]->netdev); if (ret < 0) { @@ -1693,7 +1689,31 @@ static struct spi_driver adin1110_driver = { .probe = adin1110_probe, .id_table = adin1110_spi_id, }; -module_spi_driver(adin1110_driver); + +static int __init adin1110_driver_init(void) +{ + int ret; + + ret = adin1110_setup_notifiers(); + if (ret < 0) + return ret; + + ret = spi_register_driver(&adin1110_driver); + if (ret < 0) { + adin1110_unregister_notifiers(); + return ret; + } + + return 0; +} + +static void __exit adin1110_exit(void) +{ + adin1110_unregister_notifiers(); + spi_unregister_driver(&adin1110_driver); +} +module_init(adin1110_driver_init); +module_exit(adin1110_exit); MODULE_DESCRIPTION("ADIN1110 Network driver"); MODULE_AUTHOR("Alexandru Tachici "); From a2c65a9d0568b6737c02b54f00b80716a53fac61 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Thu, 27 Oct 2022 17:54:39 +0300 Subject: [PATCH 0190/1823] net: dsa: fall back to default tagger if we can't load the one from DT DSA tagging protocol drivers can be changed at runtime through sysfs and at probe time through the device tree (support for the latter was added later). When changing through sysfs, it is assumed that the module for the new tagging protocol was already loaded into the kernel (in fact this is only a concern for Ocelot/Felix switches, where we have tag_ocelot.ko and tag_ocelot_8021q.ko; for every other switch, the default and alternative protocols are compiled within the same .ko, so there is nothing for the user to load). The kernel cannot currently call request_module(), because it has no way of constructing the modalias name of the tagging protocol driver ("dsa_tag-%d", where the number is one of DSA_TAG_PROTO_*_VALUE). The device tree only contains the string name of the tagging protocol ("ocelot-8021q"), and the only mapping between the string and the DSA_TAG_PROTO_OCELOT_8021Q_VALUE is present in tag_ocelot_8021q.ko. So this is a chicken-and-egg situation and dsa_core.ko has nothing based on which it can automatically request the insertion of the module. As a consequence, if CONFIG_NET_DSA_TAG_OCELOT_8021Q is built as module, the switch will forever defer probing. The long-term solution is to make DSA call request_module() somehow, but that probably needs some refactoring. What we can do to keep operating with existing device tree blobs is to cancel the attempt to change the tagging protocol with the one specified there, and to remain operating with the default one. Depending on the situation, the default protocol might still allow some functionality (in the case of ocelot, it does), and it's better to have that than to fail to probe. Fixes: deff710703d8 ("net: dsa: Allow default tag protocol to be overridden from DT") Link: https://lore.kernel.org/lkml/20221027113248.420216-1-michael@walle.cc/ Reported-by: Heiko Thiery Reported-by: Michael Walle Signed-off-by: Vladimir Oltean Tested-by: Michael Walle Reviewed-by: Florian Fainelli Link: https://lore.kernel.org/r/20221027145439.3086017-1-vladimir.oltean@nxp.com Signed-off-by: Jakub Kicinski --- net/dsa/dsa2.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index af0e2c0394ac..e504a18fc125 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -1409,9 +1409,9 @@ static enum dsa_tag_protocol dsa_get_tag_protocol(struct dsa_port *dp, static int dsa_port_parse_cpu(struct dsa_port *dp, struct net_device *master, const char *user_protocol) { + const struct dsa_device_ops *tag_ops = NULL; struct dsa_switch *ds = dp->ds; struct dsa_switch_tree *dst = ds->dst; - const struct dsa_device_ops *tag_ops; enum dsa_tag_protocol default_proto; /* Find out which protocol the switch would prefer. */ @@ -1434,10 +1434,17 @@ static int dsa_port_parse_cpu(struct dsa_port *dp, struct net_device *master, } tag_ops = dsa_find_tagger_by_name(user_protocol); - } else { - tag_ops = dsa_tag_driver_get(default_proto); + if (IS_ERR(tag_ops)) { + dev_warn(ds->dev, + "Failed to find a tagging driver for protocol %s, using default\n", + user_protocol); + tag_ops = NULL; + } } + if (!tag_ops) + tag_ops = dsa_tag_driver_get(default_proto); + if (IS_ERR(tag_ops)) { if (PTR_ERR(tag_ops) == -ENOPROTOOPT) return -EPROBE_DEFER; From 2f321fd6d89ad1e9525f5aa1f2be9202c2f3e724 Mon Sep 17 00:00:00 2001 From: Max Krummenacher Date: Thu, 22 Sep 2022 18:29:18 +0200 Subject: [PATCH 0191/1823] arm64: dts: verdin-imx8mp: fix ctrl_sleep_moci The GPIO signaling ctrl_sleep_moci is currently handled as a gpio hog. But the gpio-hog node is made a child of the wrong gpio controller. Move it to the node representing gpio4 so that it actually works. Without this carrier board components jumpered to use the signal are unconditionally switched off. Fixes: a39ed23bdf6e ("arm64: dts: freescale: add initial support for verdin imx8m plus") Signed-off-by: Max Krummenacher Signed-off-by: Marcel Ziswiler Signed-off-by: Shawn Guo --- .../boot/dts/freescale/imx8mp-verdin.dtsi | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mp-verdin.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-verdin.dtsi index 7b712d1888ea..5dcd1de586b5 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-verdin.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp-verdin.dtsi @@ -354,16 +354,6 @@ "SODIMM_82", "SODIMM_70", "SODIMM_72"; - - ctrl-sleep-moci-hog { - gpio-hog; - /* Verdin CTRL_SLEEP_MOCI# (SODIMM 256) */ - gpios = <29 GPIO_ACTIVE_HIGH>; - line-name = "CTRL_SLEEP_MOCI#"; - output-high; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_ctrl_sleep_moci>; - }; }; &gpio3 { @@ -432,6 +422,16 @@ "SODIMM_256", "SODIMM_48", "SODIMM_44"; + + ctrl-sleep-moci-hog { + gpio-hog; + /* Verdin CTRL_SLEEP_MOCI# (SODIMM 256) */ + gpios = <29 GPIO_ACTIVE_HIGH>; + line-name = "CTRL_SLEEP_MOCI#"; + output-high; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ctrl_sleep_moci>; + }; }; /* On-module I2C */ From e1ec45b9a8127d9d31bb9fc1d802571a2ba8dd89 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Mon, 26 Sep 2022 19:45:32 +0800 Subject: [PATCH 0192/1823] arm64: dts: imx8mm: remove otg1/2 power domain dependency on hsio pgc_otg1/2 are independent power domain of hsio, they for usb phy, so remove hsio power domain dependency from its node. Fixes: d39d4bb15310 ("arm64: dts: imx8mm: add GPC node") Signed-off-by: Li Jun Signed-off-by: Shawn Guo --- arch/arm64/boot/dts/freescale/imx8mm.dtsi | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mm.dtsi b/arch/arm64/boot/dts/freescale/imx8mm.dtsi index afb90f59c83c..41204b871f4f 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mm.dtsi @@ -674,13 +674,11 @@ pgc_otg1: power-domain@2 { #power-domain-cells = <0>; reg = ; - power-domains = <&pgc_hsiomix>; }; pgc_otg2: power-domain@3 { #power-domain-cells = <0>; reg = ; - power-domains = <&pgc_hsiomix>; }; pgc_gpumix: power-domain@4 { From 4585c79ff477f9517b7f384a4fce351417e8fa36 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Mon, 26 Sep 2022 19:45:33 +0800 Subject: [PATCH 0193/1823] arm64: dts: imx8mm: correct usb power domains pgc_otg1/2 is actual the power domain of usb PHY, usb controller is in hsio power domain, and pgc_otg1/2 is required to be powered up to detect usb remote wakeup, so move the pgc_otg1/2 power domain to the usb phy node. Fixes: 01df28d80859 ("arm64: dts: imx8mm: put USB controllers into power-domains") Signed-off-by: Li Jun Signed-off-by: Shawn Guo --- arch/arm64/boot/dts/freescale/imx8mm.dtsi | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mm.dtsi b/arch/arm64/boot/dts/freescale/imx8mm.dtsi index 41204b871f4f..dabd94dc30c4 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mm.dtsi @@ -276,6 +276,7 @@ assigned-clocks = <&clk IMX8MM_CLK_USB_PHY_REF>; assigned-clock-parents = <&clk IMX8MM_SYS_PLL1_100M>; clock-names = "main_clk"; + power-domains = <&pgc_otg1>; }; usbphynop2: usbphynop2 { @@ -285,6 +286,7 @@ assigned-clocks = <&clk IMX8MM_CLK_USB_PHY_REF>; assigned-clock-parents = <&clk IMX8MM_SYS_PLL1_100M>; clock-names = "main_clk"; + power-domains = <&pgc_otg2>; }; soc: soc@0 { @@ -1184,7 +1186,7 @@ assigned-clock-parents = <&clk IMX8MM_SYS_PLL2_500M>; phys = <&usbphynop1>; fsl,usbmisc = <&usbmisc1 0>; - power-domains = <&pgc_otg1>; + power-domains = <&pgc_hsiomix>; status = "disabled"; }; @@ -1204,7 +1206,7 @@ assigned-clock-parents = <&clk IMX8MM_SYS_PLL2_500M>; phys = <&usbphynop2>; fsl,usbmisc = <&usbmisc2 0>; - power-domains = <&pgc_otg2>; + power-domains = <&pgc_hsiomix>; status = "disabled"; }; From 9e0bbb7a5218d856f1ccf8f1bf38c8869572b464 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Mon, 26 Sep 2022 19:45:34 +0800 Subject: [PATCH 0194/1823] arm64: dts: imx8mn: remove otg1 power domain dependency on hsio pgc_otg1 is an independent power domain of hsio, it's for usb phy, so remove hsio power domain from its node. Fixes: 8b8ebec67360 ("arm64: dts: imx8mn: add GPC node") Signed-off-by: Li Jun Signed-off-by: Shawn Guo --- arch/arm64/boot/dts/freescale/imx8mn.dtsi | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mn.dtsi b/arch/arm64/boot/dts/freescale/imx8mn.dtsi index cb2836bfbd95..950f432627fe 100644 --- a/arch/arm64/boot/dts/freescale/imx8mn.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mn.dtsi @@ -662,7 +662,6 @@ pgc_otg1: power-domain@1 { #power-domain-cells = <0>; reg = ; - power-domains = <&pgc_hsiomix>; }; pgc_gpumix: power-domain@2 { From ee895139a761bdb7869f9f5b9ccc19a064d0d740 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Mon, 26 Sep 2022 19:45:35 +0800 Subject: [PATCH 0195/1823] arm64: dts: imx8mn: Correct the usb power domain pgc_otg1 is actual the power domain of usb PHY, usb controller is in hsio power domain, and pgc_otg1 is required to be powered up to detect usb remote wakeup, so move the pgc_otg1 power domain to the usb phy node. Fixes: ea2b5af58ab2 ("arm64: dts: imx8mn: put USB controller into power-domains") Signed-off-by: Li Jun Signed-off-by: Shawn Guo --- arch/arm64/boot/dts/freescale/imx8mn.dtsi | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mn.dtsi b/arch/arm64/boot/dts/freescale/imx8mn.dtsi index 950f432627fe..ad0b99adf691 100644 --- a/arch/arm64/boot/dts/freescale/imx8mn.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mn.dtsi @@ -1075,7 +1075,7 @@ assigned-clock-parents = <&clk IMX8MN_SYS_PLL2_500M>; phys = <&usbphynop1>; fsl,usbmisc = <&usbmisc1 0>; - power-domains = <&pgc_otg1>; + power-domains = <&pgc_hsiomix>; status = "disabled"; }; @@ -1174,5 +1174,6 @@ assigned-clocks = <&clk IMX8MN_CLK_USB_PHY_REF>; assigned-clock-parents = <&clk IMX8MN_SYS_PLL1_100M>; clock-names = "main_clk"; + power-domains = <&pgc_otg1>; }; }; From bb5ad73941dc3f4e3c2241348f385da6501d50ea Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Thu, 29 Sep 2022 12:52:22 -0700 Subject: [PATCH 0196/1823] ARM: dts: imx6qdl-gw59{10,13}: fix user pushbutton GPIO offset The GW5910 and GW5913 have a user pushbutton that is tied to the Gateworks System Controller GPIO offset 2. Fix the invalid offset of 0. Fixes: 64bf0a0af18d ("ARM: dts: imx6qdl-gw: add Gateworks System Controller support") Signed-off-by: Tim Harvey Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx6qdl-gw5910.dtsi | 2 +- arch/arm/boot/dts/imx6qdl-gw5913.dtsi | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/imx6qdl-gw5910.dtsi b/arch/arm/boot/dts/imx6qdl-gw5910.dtsi index 68e5ab2e27e2..6bb4855d13ce 100644 --- a/arch/arm/boot/dts/imx6qdl-gw5910.dtsi +++ b/arch/arm/boot/dts/imx6qdl-gw5910.dtsi @@ -29,7 +29,7 @@ user-pb { label = "user_pb"; - gpios = <&gsc_gpio 0 GPIO_ACTIVE_LOW>; + gpios = <&gsc_gpio 2 GPIO_ACTIVE_LOW>; linux,code = ; }; diff --git a/arch/arm/boot/dts/imx6qdl-gw5913.dtsi b/arch/arm/boot/dts/imx6qdl-gw5913.dtsi index 8e23cec7149e..696427b487f0 100644 --- a/arch/arm/boot/dts/imx6qdl-gw5913.dtsi +++ b/arch/arm/boot/dts/imx6qdl-gw5913.dtsi @@ -26,7 +26,7 @@ user-pb { label = "user_pb"; - gpios = <&gsc_gpio 0 GPIO_ACTIVE_LOW>; + gpios = <&gsc_gpio 2 GPIO_ACTIVE_LOW>; linux,code = ; }; From 5e67d47d0b010f0704aca469d6d27637b1dcb2ce Mon Sep 17 00:00:00 2001 From: Petr Benes Date: Tue, 4 Oct 2022 17:39:20 +0200 Subject: [PATCH 0197/1823] ARM: dts: imx6dl-yapp4: Do not allow PM to switch PU regulator off on Q/QP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix our design flaw in supply voltage distribution on the Quad and QuadPlus based boards. The problem is that we supply the SoC cache (VDD_CACHE_CAP) from VDD_PU instead of VDD_SOC. The VDD_PU internal regulator can be disabled by PM if VPU or GPU is not used. If that happens the system freezes. To prevent that configure the reg_pu regulator to be always on. Fixes: 0de4ab81ab26 ("ARM: dts: imx6dl-yapp4: Add Y Soft IOTA Crux/Crux+ board") Cc: petrben@gmail.com Cc: stable@vger.kernel.org Signed-off-by: Petr Benes Signed-off-by: Michal Vokáč Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx6q-yapp4-crux.dts | 4 ++++ arch/arm/boot/dts/imx6qp-yapp4-crux-plus.dts | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/arch/arm/boot/dts/imx6q-yapp4-crux.dts b/arch/arm/boot/dts/imx6q-yapp4-crux.dts index 15f4824a5142..bddf3822ebf7 100644 --- a/arch/arm/boot/dts/imx6q-yapp4-crux.dts +++ b/arch/arm/boot/dts/imx6q-yapp4-crux.dts @@ -33,6 +33,10 @@ status = "okay"; }; +®_pu { + regulator-always-on; +}; + ®_usb_h1_vbus { status = "okay"; }; diff --git a/arch/arm/boot/dts/imx6qp-yapp4-crux-plus.dts b/arch/arm/boot/dts/imx6qp-yapp4-crux-plus.dts index cea165f2161a..afaf4a6759d4 100644 --- a/arch/arm/boot/dts/imx6qp-yapp4-crux-plus.dts +++ b/arch/arm/boot/dts/imx6qp-yapp4-crux-plus.dts @@ -33,6 +33,10 @@ status = "okay"; }; +®_pu { + regulator-always-on; +}; + ®_usb_h1_vbus { status = "okay"; }; From 06acb824d7d00a30e9400f67eee481b218371b5a Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Mon, 10 Oct 2022 18:07:47 +0800 Subject: [PATCH 0198/1823] arm64: dts: imx8: correct clock order Per bindings/mmc/fsl-imx-esdhc.yaml, the clock order is ipg, ahb, per, otherwise warning: " mmc@5b020000: clock-names:1: 'ahb' was expected mmc@5b020000: clock-names:2: 'per' was expected " Fixes: 16c4ea7501b1 ("arm64: dts: imx8: switch to new lpcg clock binding") Signed-off-by: Peng Fan Signed-off-by: Shawn Guo --- .../arm64/boot/dts/freescale/imx8-ss-conn.dtsi | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx8-ss-conn.dtsi b/arch/arm64/boot/dts/freescale/imx8-ss-conn.dtsi index 82a1c4488378..10370d1a6c6d 100644 --- a/arch/arm64/boot/dts/freescale/imx8-ss-conn.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8-ss-conn.dtsi @@ -38,9 +38,9 @@ conn_subsys: bus@5b000000 { interrupts = ; reg = <0x5b010000 0x10000>; clocks = <&sdhc0_lpcg IMX_LPCG_CLK_4>, - <&sdhc0_lpcg IMX_LPCG_CLK_5>, - <&sdhc0_lpcg IMX_LPCG_CLK_0>; - clock-names = "ipg", "per", "ahb"; + <&sdhc0_lpcg IMX_LPCG_CLK_0>, + <&sdhc0_lpcg IMX_LPCG_CLK_5>; + clock-names = "ipg", "ahb", "per"; power-domains = <&pd IMX_SC_R_SDHC_0>; status = "disabled"; }; @@ -49,9 +49,9 @@ conn_subsys: bus@5b000000 { interrupts = ; reg = <0x5b020000 0x10000>; clocks = <&sdhc1_lpcg IMX_LPCG_CLK_4>, - <&sdhc1_lpcg IMX_LPCG_CLK_5>, - <&sdhc1_lpcg IMX_LPCG_CLK_0>; - clock-names = "ipg", "per", "ahb"; + <&sdhc1_lpcg IMX_LPCG_CLK_0>, + <&sdhc1_lpcg IMX_LPCG_CLK_5>; + clock-names = "ipg", "ahb", "per"; power-domains = <&pd IMX_SC_R_SDHC_1>; fsl,tuning-start-tap = <20>; fsl,tuning-step = <2>; @@ -62,9 +62,9 @@ conn_subsys: bus@5b000000 { interrupts = ; reg = <0x5b030000 0x10000>; clocks = <&sdhc2_lpcg IMX_LPCG_CLK_4>, - <&sdhc2_lpcg IMX_LPCG_CLK_5>, - <&sdhc2_lpcg IMX_LPCG_CLK_0>; - clock-names = "ipg", "per", "ahb"; + <&sdhc2_lpcg IMX_LPCG_CLK_0>, + <&sdhc2_lpcg IMX_LPCG_CLK_5>; + clock-names = "ipg", "ahb", "per"; power-domains = <&pd IMX_SC_R_SDHC_2>; status = "disabled"; }; From ef370d8ceec62322dee24c960af8ca67a749f34d Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Mon, 10 Oct 2022 18:09:58 +0800 Subject: [PATCH 0199/1823] dt-bindings: power: gpcv2: add power-domains property Some pgc power-domain requires a parent power domain, so add an optional power-domains property, otherwise there will be dt check warning: gpc@303a0000: pgc:power-domain@1: 'power-domains' does not match any of the regexes: 'pinctrl-[0-9]+' Fixes: 30af8513bdb5 ("dt-bindings: power: add defines for i.MX8MM power domains") Signed-off-by: Peng Fan Acked-by: Krzysztof Kozlowski Signed-off-by: Shawn Guo --- Documentation/devicetree/bindings/power/fsl,imx-gpcv2.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/power/fsl,imx-gpcv2.yaml b/Documentation/devicetree/bindings/power/fsl,imx-gpcv2.yaml index 58022ae7d5dd..dfdb8dfb6b65 100644 --- a/Documentation/devicetree/bindings/power/fsl,imx-gpcv2.yaml +++ b/Documentation/devicetree/bindings/power/fsl,imx-gpcv2.yaml @@ -81,6 +81,9 @@ properties: power-supply: true + power-domains: + maxItems: 1 + resets: description: | A number of phandles to resets that need to be asserted during From 82ce591967517b733c6e6e6882b5096d239b3afe Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Thu, 20 Oct 2022 18:16:56 +0800 Subject: [PATCH 0200/1823] arm64: dts: imx93: correct s4mu interrupt names Per binding doc, interrupt names should be tx and rx. Fixes: 0dfb380d2492 ("arm64: dts: imx93: add s4 mu node") Signed-off-by: Peng Fan Signed-off-by: Shawn Guo --- arch/arm64/boot/dts/freescale/imx93.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/freescale/imx93.dtsi b/arch/arm64/boot/dts/freescale/imx93.dtsi index 3a5713bb4880..55a70ee6f91a 100644 --- a/arch/arm64/boot/dts/freescale/imx93.dtsi +++ b/arch/arm64/boot/dts/freescale/imx93.dtsi @@ -501,7 +501,7 @@ reg = <0x47520000 0x10000>; interrupts = , ; - interrupt-names = "txirq", "rxirq"; + interrupt-names = "tx", "rx"; #mbox-cells = <2>; }; From d92a110130d492bd5eab81827ce3730581dc933a Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Thu, 20 Oct 2022 18:16:57 +0800 Subject: [PATCH 0201/1823] arm64: dts: imx93: correct gpio-ranges Per imx93-pinfunc.h and pinctrl-imx93.c, correct gpio-ranges. Fixes: ec8b5b5058ea ("arm64: dts: freescale: Add i.MX93 dtsi support") Reported-by: David Wolfe Reviewed-by: Haibo Chen Reviewed-by: Jacky Bai Signed-off-by: Peng Fan Signed-off-by: Shawn Guo --- arch/arm64/boot/dts/freescale/imx93.dtsi | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx93.dtsi b/arch/arm64/boot/dts/freescale/imx93.dtsi index 55a70ee6f91a..0247866fc86b 100644 --- a/arch/arm64/boot/dts/freescale/imx93.dtsi +++ b/arch/arm64/boot/dts/freescale/imx93.dtsi @@ -451,7 +451,7 @@ clocks = <&clk IMX93_CLK_GPIO2_GATE>, <&clk IMX93_CLK_GPIO2_GATE>; clock-names = "gpio", "port"; - gpio-ranges = <&iomuxc 0 32 32>; + gpio-ranges = <&iomuxc 0 4 30>; }; gpio3: gpio@43820080 { @@ -465,7 +465,8 @@ clocks = <&clk IMX93_CLK_GPIO3_GATE>, <&clk IMX93_CLK_GPIO3_GATE>; clock-names = "gpio", "port"; - gpio-ranges = <&iomuxc 0 64 32>; + gpio-ranges = <&iomuxc 0 84 8>, <&iomuxc 8 66 18>, + <&iomuxc 26 34 2>, <&iomuxc 28 0 4>; }; gpio4: gpio@43830080 { @@ -479,7 +480,7 @@ clocks = <&clk IMX93_CLK_GPIO4_GATE>, <&clk IMX93_CLK_GPIO4_GATE>; clock-names = "gpio", "port"; - gpio-ranges = <&iomuxc 0 96 32>; + gpio-ranges = <&iomuxc 0 38 28>, <&iomuxc 28 36 2>; }; gpio1: gpio@47400080 { @@ -493,7 +494,7 @@ clocks = <&clk IMX93_CLK_GPIO1_GATE>, <&clk IMX93_CLK_GPIO1_GATE>; clock-names = "gpio", "port"; - gpio-ranges = <&iomuxc 0 0 32>; + gpio-ranges = <&iomuxc 0 92 16>; }; s4muap: mailbox@47520000 { From 0ba7b623f15d52fa056eca26573d8cf1b9c29fd1 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 22 Oct 2022 08:08:55 +0200 Subject: [PATCH 0202/1823] soc: imx: imx93-pd: Fix the error handling path of imx93_pd_probe() In imx93_pd_probe(); if an error occurs, some resources need to be released as done in the remove function. Fixes: 0a0f7cc25d4a ("soc: imx: add i.MX93 SRC power domain driver") Signed-off-by: Christophe JAILLET Signed-off-by: Shawn Guo --- drivers/soc/imx/imx93-pd.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/soc/imx/imx93-pd.c b/drivers/soc/imx/imx93-pd.c index 1f3d7039c1de..4d235c8c4924 100644 --- a/drivers/soc/imx/imx93-pd.c +++ b/drivers/soc/imx/imx93-pd.c @@ -135,11 +135,24 @@ static int imx93_pd_probe(struct platform_device *pdev) ret = pm_genpd_init(&domain->genpd, NULL, domain->init_off); if (ret) - return ret; + goto err_clk_unprepare; platform_set_drvdata(pdev, domain); - return of_genpd_add_provider_simple(np, &domain->genpd); + ret = of_genpd_add_provider_simple(np, &domain->genpd); + if (ret) + goto err_genpd_remove; + + return 0; + +err_genpd_remove: + pm_genpd_remove(&domain->genpd); + +err_clk_unprepare: + if (!domain->init_off) + clk_bulk_disable_unprepare(domain->num_clks, domain->clks); + + return ret; } static const struct of_device_id imx93_pd_ids[] = { From 46cf1954de3f324dc7f9472c12c3bd03b268a11b Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Sat, 29 Oct 2022 16:20:01 +0800 Subject: [PATCH 0203/1823] ALSA: arm: pxa: pxa2xx-ac97-lib: fix return value check of platform_get_irq() platform_get_irq() returns negative error number on failure, fix the return value check in pxa2xx_ac97_hw_probe() and assign the error code to 'ret'. Fixes: 2548e6c76ebf ("ARM: pxa: pxa2xx-ac97-lib: use IRQ resource") Signed-off-by: Yang Yingliang Link: https://lore.kernel.org/r/20221029082001.3207380-1-yangyingliang@huawei.com Signed-off-by: Takashi Iwai --- sound/arm/pxa2xx-ac97-lib.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/arm/pxa2xx-ac97-lib.c b/sound/arm/pxa2xx-ac97-lib.c index e55c0421718b..2ca33fd5a575 100644 --- a/sound/arm/pxa2xx-ac97-lib.c +++ b/sound/arm/pxa2xx-ac97-lib.c @@ -402,8 +402,10 @@ int pxa2xx_ac97_hw_probe(struct platform_device *dev) goto err_clk2; irq = platform_get_irq(dev, 0); - if (!irq) + if (irq < 0) { + ret = irq; goto err_irq; + } ret = request_irq(irq, pxa2xx_ac97_irq, 0, "AC97", NULL); if (ret < 0) From 57572cacd36e6d4be7722d7770d23f4430219827 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 2 Oct 2022 15:41:33 +0100 Subject: [PATCH 0204/1823] iio: accel: bma400: Ensure VDDIO is enable defore reading the chip ID. The regulator enables were after the check on the chip variant, which was very unlikely to return a correct value when not powered. Presumably all the device anyone is testing on have a regulator that is already powered up when this code runs for reasons beyond the scope of this driver. Move the read call down a few lines. Fixes: 3cf7ded15e40 ("iio: accel: bma400: basic regulator support") Signed-off-by: Jonathan Cameron Reviewed-by: Dan Robertson Cc: Link: https://lore.kernel.org/r/20221002144133.3771029-1-jic23@kernel.org --- drivers/iio/accel/bma400_core.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c index ad8fce3e08cd..490c342ef72a 100644 --- a/drivers/iio/accel/bma400_core.c +++ b/drivers/iio/accel/bma400_core.c @@ -869,18 +869,6 @@ static int bma400_init(struct bma400_data *data) unsigned int val; int ret; - /* Try to read chip_id register. It must return 0x90. */ - ret = regmap_read(data->regmap, BMA400_CHIP_ID_REG, &val); - if (ret) { - dev_err(data->dev, "Failed to read chip id register\n"); - return ret; - } - - if (val != BMA400_ID_REG_VAL) { - dev_err(data->dev, "Chip ID mismatch\n"); - return -ENODEV; - } - data->regulators[BMA400_VDD_REGULATOR].supply = "vdd"; data->regulators[BMA400_VDDIO_REGULATOR].supply = "vddio"; ret = devm_regulator_bulk_get(data->dev, @@ -906,6 +894,18 @@ static int bma400_init(struct bma400_data *data) if (ret) return ret; + /* Try to read chip_id register. It must return 0x90. */ + ret = regmap_read(data->regmap, BMA400_CHIP_ID_REG, &val); + if (ret) { + dev_err(data->dev, "Failed to read chip id register\n"); + return ret; + } + + if (val != BMA400_ID_REG_VAL) { + dev_err(data->dev, "Chip ID mismatch\n"); + return -ENODEV; + } + ret = bma400_get_power_mode(data); if (ret) { dev_err(data->dev, "Failed to get the initial power-mode\n"); From 17f442e7e47579d3881fc4d47354eaef09302e6f Mon Sep 17 00:00:00 2001 From: Mitja Spes Date: Fri, 21 Oct 2022 15:58:20 +0200 Subject: [PATCH 0205/1823] iio: pressure: ms5611: fixed value compensation bug When using multiple instances of this driver the compensation PROM was overwritten by the last initialized sensor. Now each sensor has own PROM storage. Signed-off-by: Mitja Spes Fixes: 9690d81a02dc ("iio: pressure: ms5611: add support for MS5607 temperature and pressure sensor") Link: https://lore.kernel.org/r/20221021135827.1444793-2-mitja@lxnav.com Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/ms5611.h | 12 +++---- drivers/iio/pressure/ms5611_core.c | 51 ++++++++++++++++-------------- 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/drivers/iio/pressure/ms5611.h b/drivers/iio/pressure/ms5611.h index cbc9349c342a..550b75b7186f 100644 --- a/drivers/iio/pressure/ms5611.h +++ b/drivers/iio/pressure/ms5611.h @@ -25,13 +25,6 @@ enum { MS5607, }; -struct ms5611_chip_info { - u16 prom[MS5611_PROM_WORDS_NB]; - - int (*temp_and_pressure_compensate)(struct ms5611_chip_info *chip_info, - s32 *temp, s32 *pressure); -}; - /* * OverSampling Rate descriptor. * Warning: cmd MUST be kept aligned on a word boundary (see @@ -50,12 +43,15 @@ struct ms5611_state { const struct ms5611_osr *pressure_osr; const struct ms5611_osr *temp_osr; + u16 prom[MS5611_PROM_WORDS_NB]; + int (*reset)(struct ms5611_state *st); int (*read_prom_word)(struct ms5611_state *st, int index, u16 *word); int (*read_adc_temp_and_pressure)(struct ms5611_state *st, s32 *temp, s32 *pressure); - struct ms5611_chip_info *chip_info; + int (*compensate_temp_and_pressure)(struct ms5611_state *st, s32 *temp, + s32 *pressure); struct regulator *vdd; }; diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c index 717521de66c4..c564a1d6cafe 100644 --- a/drivers/iio/pressure/ms5611_core.c +++ b/drivers/iio/pressure/ms5611_core.c @@ -85,7 +85,7 @@ static int ms5611_read_prom(struct iio_dev *indio_dev) struct ms5611_state *st = iio_priv(indio_dev); for (i = 0; i < MS5611_PROM_WORDS_NB; i++) { - ret = st->read_prom_word(st, i, &st->chip_info->prom[i]); + ret = st->read_prom_word(st, i, &st->prom[i]); if (ret < 0) { dev_err(&indio_dev->dev, "failed to read prom at %d\n", i); @@ -93,7 +93,7 @@ static int ms5611_read_prom(struct iio_dev *indio_dev) } } - if (!ms5611_prom_is_valid(st->chip_info->prom, MS5611_PROM_WORDS_NB)) { + if (!ms5611_prom_is_valid(st->prom, MS5611_PROM_WORDS_NB)) { dev_err(&indio_dev->dev, "PROM integrity check failed\n"); return -ENODEV; } @@ -114,21 +114,20 @@ static int ms5611_read_temp_and_pressure(struct iio_dev *indio_dev, return ret; } - return st->chip_info->temp_and_pressure_compensate(st->chip_info, - temp, pressure); + return st->compensate_temp_and_pressure(st, temp, pressure); } -static int ms5611_temp_and_pressure_compensate(struct ms5611_chip_info *chip_info, +static int ms5611_temp_and_pressure_compensate(struct ms5611_state *st, s32 *temp, s32 *pressure) { s32 t = *temp, p = *pressure; s64 off, sens, dt; - dt = t - (chip_info->prom[5] << 8); - off = ((s64)chip_info->prom[2] << 16) + ((chip_info->prom[4] * dt) >> 7); - sens = ((s64)chip_info->prom[1] << 15) + ((chip_info->prom[3] * dt) >> 8); + dt = t - (st->prom[5] << 8); + off = ((s64)st->prom[2] << 16) + ((st->prom[4] * dt) >> 7); + sens = ((s64)st->prom[1] << 15) + ((st->prom[3] * dt) >> 8); - t = 2000 + ((chip_info->prom[6] * dt) >> 23); + t = 2000 + ((st->prom[6] * dt) >> 23); if (t < 2000) { s64 off2, sens2, t2; @@ -154,17 +153,17 @@ static int ms5611_temp_and_pressure_compensate(struct ms5611_chip_info *chip_inf return 0; } -static int ms5607_temp_and_pressure_compensate(struct ms5611_chip_info *chip_info, +static int ms5607_temp_and_pressure_compensate(struct ms5611_state *st, s32 *temp, s32 *pressure) { s32 t = *temp, p = *pressure; s64 off, sens, dt; - dt = t - (chip_info->prom[5] << 8); - off = ((s64)chip_info->prom[2] << 17) + ((chip_info->prom[4] * dt) >> 6); - sens = ((s64)chip_info->prom[1] << 16) + ((chip_info->prom[3] * dt) >> 7); + dt = t - (st->prom[5] << 8); + off = ((s64)st->prom[2] << 17) + ((st->prom[4] * dt) >> 6); + sens = ((s64)st->prom[1] << 16) + ((st->prom[3] * dt) >> 7); - t = 2000 + ((chip_info->prom[6] * dt) >> 23); + t = 2000 + ((st->prom[6] * dt) >> 23); if (t < 2000) { s64 off2, sens2, t2, tmp; @@ -342,15 +341,6 @@ static int ms5611_write_raw(struct iio_dev *indio_dev, static const unsigned long ms5611_scan_masks[] = {0x3, 0}; -static struct ms5611_chip_info chip_info_tbl[] = { - [MS5611] = { - .temp_and_pressure_compensate = ms5611_temp_and_pressure_compensate, - }, - [MS5607] = { - .temp_and_pressure_compensate = ms5607_temp_and_pressure_compensate, - } -}; - static const struct iio_chan_spec ms5611_channels[] = { { .type = IIO_PRESSURE, @@ -433,7 +423,20 @@ int ms5611_probe(struct iio_dev *indio_dev, struct device *dev, struct ms5611_state *st = iio_priv(indio_dev); mutex_init(&st->lock); - st->chip_info = &chip_info_tbl[type]; + + switch (type) { + case MS5611: + st->compensate_temp_and_pressure = + ms5611_temp_and_pressure_compensate; + break; + case MS5607: + st->compensate_temp_and_pressure = + ms5607_temp_and_pressure_compensate; + break; + default: + return -EINVAL; + } + st->temp_osr = &ms5611_avail_temp_osr[ARRAY_SIZE(ms5611_avail_temp_osr) - 1]; st->pressure_osr = From 741cec30cc52058d1c10d415f3b98319887e4f73 Mon Sep 17 00:00:00 2001 From: Mitja Spes Date: Fri, 21 Oct 2022 15:58:21 +0200 Subject: [PATCH 0206/1823] iio: pressure: ms5611: changed hardcoded SPI speed to value limited Don't hardcode the ms5611 SPI speed, limit it instead. Signed-off-by: Mitja Spes Fixes: c0644160a8b5 ("iio: pressure: add support for MS5611 pressure and temperature sensor") Link: https://lore.kernel.org/r/20221021135827.1444793-3-mitja@lxnav.com Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/ms5611_spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/pressure/ms5611_spi.c b/drivers/iio/pressure/ms5611_spi.c index 432e912096f4..a0a7205c9c3a 100644 --- a/drivers/iio/pressure/ms5611_spi.c +++ b/drivers/iio/pressure/ms5611_spi.c @@ -91,7 +91,7 @@ static int ms5611_spi_probe(struct spi_device *spi) spi_set_drvdata(spi, indio_dev); spi->mode = SPI_MODE_0; - spi->max_speed_hz = 20000000; + spi->max_speed_hz = min(spi->max_speed_hz, 20000000U); spi->bits_per_word = 8; ret = spi_setup(spi); if (ret < 0) From 1eb20332a082fa801fb89c347c5e62de916a4001 Mon Sep 17 00:00:00 2001 From: Saravanan Sekar Date: Sat, 29 Oct 2022 11:29:53 +0200 Subject: [PATCH 0207/1823] iio: adc: mp2629: fix wrong comparison of channel Input voltage channel enum is compared against iio address instead of the channel. Fixes: 7abd9fb64682 ("iio: adc: mp2629: Add support for mp2629 ADC driver") Signed-off-by: Saravanan Sekar Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20221029093000.45451-2-sravanhome@gmail.com Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mp2629_adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/mp2629_adc.c b/drivers/iio/adc/mp2629_adc.c index 30a31f185d08..f7af9af1665d 100644 --- a/drivers/iio/adc/mp2629_adc.c +++ b/drivers/iio/adc/mp2629_adc.c @@ -74,7 +74,7 @@ static int mp2629_read_raw(struct iio_dev *indio_dev, if (ret) return ret; - if (chan->address == MP2629_INPUT_VOLT) + if (chan->channel == MP2629_INPUT_VOLT) rval &= GENMASK(6, 0); *val = rval; return IIO_VAL_INT; From c126a0abc5dadd7df236f20aae6d8c3d103f095c Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Tue, 25 Oct 2022 17:41:15 +0300 Subject: [PATCH 0208/1823] arm64: dts: lx2160a: specify clock frequencies for the MDIO controllers Up until now, the external MDIO controller frequency values relied either on the default ones out of reset or on those setup by u-boot. Let's just properly specify the MDC frequency in the DTS so that even without u-boot's intervention Linux can drive the MDIO bus. Fixes: 6e1b8fae892d ("arm64: dts: lx2160a: add emdio1 node") Fixes: 5705b9dcda57 ("arm64: dts: lx2160a: add emdio2 node") Signed-off-by: Ioana Ciornei Signed-off-by: Shawn Guo --- arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi b/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi index 6680fb2a6dc9..8c76d86cb756 100644 --- a/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi @@ -1385,6 +1385,9 @@ #address-cells = <1>; #size-cells = <0>; little-endian; + clock-frequency = <2500000>; + clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL + QORIQ_CLK_PLL_DIV(2)>; status = "disabled"; }; @@ -1395,6 +1398,9 @@ little-endian; #address-cells = <1>; #size-cells = <0>; + clock-frequency = <2500000>; + clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL + QORIQ_CLK_PLL_DIV(2)>; status = "disabled"; }; From d78a57426e64fc4c61e6189e450a0432d24536ca Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Tue, 25 Oct 2022 17:41:16 +0300 Subject: [PATCH 0209/1823] arm64: dts: ls1088a: specify clock frequencies for the MDIO controllers Up until now, the external MDIO controller frequency values relied either on the default ones out of reset or on those setup by u-boot. Let's just properly specify the MDC frequency in the DTS so that even without u-boot's intervention Linux can drive the MDIO bus. Fixes: bbe75af7b092 ("arm64: dts: ls1088a: add external MDIO device nodes") Signed-off-by: Ioana Ciornei Signed-off-by: Shawn Guo --- arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi index 421d879013d7..260d045dbd9a 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-ls1088a.dtsi @@ -779,6 +779,9 @@ little-endian; #address-cells = <1>; #size-cells = <0>; + clock-frequency = <2500000>; + clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL + QORIQ_CLK_PLL_DIV(1)>; status = "disabled"; }; @@ -788,6 +791,9 @@ little-endian; #address-cells = <1>; #size-cells = <0>; + clock-frequency = <2500000>; + clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL + QORIQ_CLK_PLL_DIV(1)>; status = "disabled"; }; From d5c921a53c80dfa942f6dff36253db5a50775a5f Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Tue, 25 Oct 2022 17:41:17 +0300 Subject: [PATCH 0210/1823] arm64: dts: ls208xa: specify clock frequencies for the MDIO controllers Up until now, the external MDIO controller frequency values relied either on the default ones out of reset or on those setup by u-boot. Let's just properly specify the MDC frequency in the DTS so that even without u-boot's intervention Linux can drive the MDIO bus. Fixes: 0420dde30a90 ("arm64: dts: ls208xa: add the external MDIO nodes") Signed-off-by: Ioana Ciornei Signed-off-by: Shawn Guo --- arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi index f1b9cc8714dc..348d9e3a9125 100644 --- a/arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi @@ -532,6 +532,9 @@ little-endian; #address-cells = <1>; #size-cells = <0>; + clock-frequency = <2500000>; + clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL + QORIQ_CLK_PLL_DIV(2)>; status = "disabled"; }; @@ -541,6 +544,9 @@ little-endian; #address-cells = <1>; #size-cells = <0>; + clock-frequency = <2500000>; + clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL + QORIQ_CLK_PLL_DIV(2)>; status = "disabled"; }; From bfab00b94bd8569cdb84a6511d6615e6a8104e9c Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Mon, 26 Sep 2022 22:37:52 +0200 Subject: [PATCH 0211/1823] drm/rockchip: dw_hdmi: filter regulator -EPROBE_DEFER error messages When the avdd-0v9 or avdd-1v8 supply are not yet available, EPROBE_DEFER is returned by rockchip_hdmi_parse_dt(). This causes the following error message to be printed multiple times: dwhdmi-rockchip fe0a0000.hdmi: [drm:dw_hdmi_rockchip_bind [rockchipdrm]] *ERROR* Unable to parse OF data Fix that by not printing the message when rockchip_hdmi_parse_dt() returns -EPROBE_DEFER. Fixes: ca80c4eb4b01 ("drm/rockchip: dw_hdmi: add regulator support") Signed-off-by: Aurelien Jarno Reviewed-by: Dmitry Osipenko Signed-off-by: Heiko Stuebner Link: https://patchwork.freedesktop.org/patch/msgid/20220926203752.5430-1-aurelien@aurel32.net --- drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index c14f88893868..2f4b8f64cbad 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -565,7 +565,8 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, ret = rockchip_hdmi_parse_dt(hdmi); if (ret) { - DRM_DEV_ERROR(hdmi->dev, "Unable to parse OF data\n"); + if (ret != -EPROBE_DEFER) + DRM_DEV_ERROR(hdmi->dev, "Unable to parse OF data\n"); return ret; } From 0be67e0556e469c57100ffe3c90df90abc796f3b Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Wed, 19 Oct 2022 17:03:48 -0700 Subject: [PATCH 0212/1823] drm/rockchip: dsi: Clean up 'usage_mode' when failing to attach If we fail to attach the first time (especially: EPROBE_DEFER), we fail to clean up 'usage_mode', and thus will fail to attach on any subsequent attempts, with "dsi controller already in use". Re-set to DW_DSI_USAGE_IDLE on attach failure. This is especially common to hit when enabling asynchronous probe on a duel-DSI system (such as RK3399 Gru/Scarlet), such that we're more likely to fail dw_mipi_dsi_rockchip_find_second() the first time. Fixes: 71f68fe7f121 ("drm/rockchip: dsi: add ability to work as a phy instead of full dsi") Cc: Signed-off-by: Brian Norris Signed-off-by: Heiko Stuebner Link: https://patchwork.freedesktop.org/patch/msgid/20221019170255.1.Ia68dfb27b835d31d22bfe23812baf366ee1c6eac@changeid --- drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c index bf6948125b84..d222c6811207 100644 --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c @@ -1051,23 +1051,31 @@ static int dw_mipi_dsi_rockchip_host_attach(void *priv_data, if (ret) { DRM_DEV_ERROR(dsi->dev, "Failed to register component: %d\n", ret); - return ret; + goto out; } second = dw_mipi_dsi_rockchip_find_second(dsi); - if (IS_ERR(second)) - return PTR_ERR(second); + if (IS_ERR(second)) { + ret = PTR_ERR(second); + goto out; + } if (second) { ret = component_add(second, &dw_mipi_dsi_rockchip_ops); if (ret) { DRM_DEV_ERROR(second, "Failed to register component: %d\n", ret); - return ret; + goto out; } } return 0; + +out: + mutex_lock(&dsi->usage_mutex); + dsi->usage_mode = DW_DSI_USAGE_IDLE; + mutex_unlock(&dsi->usage_mutex); + return ret; } static int dw_mipi_dsi_rockchip_host_detach(void *priv_data, From 81e592f86f7afdb76d655e7fbd7803d7b8f985d8 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Wed, 19 Oct 2022 17:03:49 -0700 Subject: [PATCH 0213/1823] drm/rockchip: dsi: Force synchronous probe We can't safely probe a dual-DSI display asynchronously (driver_async_probe='*' or driver_async_probe='dw-mipi-dsi-rockchip' cmdline), because dw_mipi_dsi_rockchip_find_second() pokes one DSI device's drvdata from the other device without any locking. Request synchronous probe, at least until this driver learns some appropriate locking for dual-DSI initialization. Cc: Signed-off-by: Brian Norris Signed-off-by: Heiko Stuebner Link: https://patchwork.freedesktop.org/patch/msgid/20221019170255.2.I6b985b0ca372b7e35c6d9ea970b24bcb262d4fc1@changeid --- drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c index d222c6811207..528ddce144e5 100644 --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c @@ -1689,5 +1689,11 @@ struct platform_driver dw_mipi_dsi_rockchip_driver = { .of_match_table = dw_mipi_dsi_rockchip_dt_ids, .pm = &dw_mipi_dsi_rockchip_pm_ops, .name = "dw-mipi-dsi-rockchip", + /* + * For dual-DSI display, one DSI pokes at the other DSI's + * drvdata in dw_mipi_dsi_rockchip_find_second(). This is not + * safe for asynchronous probe. + */ + .probe_type = PROBE_FORCE_SYNCHRONOUS, }, }; From ab78c74cfc5a3caa2bbb7627cb8f3bca40bb5fb0 Mon Sep 17 00:00:00 2001 From: John Keeping Date: Thu, 20 Oct 2022 19:12:47 +0100 Subject: [PATCH 0214/1823] drm/rockchip: fix fbdev on non-IOMMU devices When switching to the generic fbdev infrastructure, it was missed that framebuffers were created with the alloc_kmap parameter to rockchip_gem_create_object() set to true. The generic infrastructure calls this via the .dumb_create() driver operation and thus creates a buffer without an associated kmap. alloc_kmap only makes a difference on devices without an IOMMU, but when it is missing rockchip_gem_prime_vmap() fails and the framebuffer cannot be used. Detect the case where a buffer is being allocated for the framebuffer and ensure a kernel mapping is created in this case. Fixes: 24af7c34b290 ("drm/rockchip: use generic fbdev setup") Reported-by: Johan Jonker Cc: Thomas Zimmermann Signed-off-by: John Keeping Signed-off-by: Heiko Stuebner Link: https://patchwork.freedesktop.org/patch/msgid/20221020181248.2497065-1-john@metanate.com --- drivers/gpu/drm/rockchip/rockchip_drm_gem.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c index 614e97aaac80..da8a69953706 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c @@ -364,9 +364,12 @@ rockchip_gem_create_with_handle(struct drm_file *file_priv, { struct rockchip_gem_object *rk_obj; struct drm_gem_object *obj; + bool is_framebuffer; int ret; - rk_obj = rockchip_gem_create_object(drm, size, false); + is_framebuffer = drm->fb_helper && file_priv == drm->fb_helper->client.file; + + rk_obj = rockchip_gem_create_object(drm, size, is_framebuffer); if (IS_ERR(rk_obj)) return ERR_CAST(rk_obj); From 553c5a429aee26c9cfaf37ae158a8915540270fe Mon Sep 17 00:00:00 2001 From: Ondrej Jirman Date: Sun, 23 Oct 2022 18:07:47 +0200 Subject: [PATCH 0215/1823] drm/rockchip: dsi: Fix VOP selection on SoCs that support it lcdsel_grf_reg is defined as u32, so "< 0" comaprison is always false, which breaks VOP selection on eg. RK3399. Compare against 0. Fixes: f3aaa6125b6f ("drm/rockchip: dsi: add rk3568 support") Signed-off-by: Ondrej Jirman Tested-by: Chris Morgan Signed-off-by: Heiko Stuebner Link: https://patchwork.freedesktop.org/patch/msgid/20221023160747.607943-1-megi@xff.cz --- drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c index 528ddce144e5..f4df9820b295 100644 --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c @@ -752,7 +752,7 @@ static void dw_mipi_dsi_rockchip_config(struct dw_mipi_dsi_rockchip *dsi) static void dw_mipi_dsi_rockchip_set_lcdsel(struct dw_mipi_dsi_rockchip *dsi, int mux) { - if (dsi->cdata->lcdsel_grf_reg < 0) + if (dsi->cdata->lcdsel_grf_reg) regmap_write(dsi->grf_regmap, dsi->cdata->lcdsel_grf_reg, mux ? dsi->cdata->lcdsel_lit : dsi->cdata->lcdsel_big); } @@ -1643,7 +1643,6 @@ static const struct rockchip_dw_dsi_chip_data rk3399_chip_data[] = { static const struct rockchip_dw_dsi_chip_data rk3568_chip_data[] = { { .reg = 0xfe060000, - .lcdsel_grf_reg = -1, .lanecfg1_grf_reg = RK3568_GRF_VO_CON2, .lanecfg1 = HIWORD_UPDATE(0, RK3568_DSI0_SKEWCALHS | RK3568_DSI0_FORCETXSTOPMODE | @@ -1653,7 +1652,6 @@ static const struct rockchip_dw_dsi_chip_data rk3568_chip_data[] = { }, { .reg = 0xfe070000, - .lcdsel_grf_reg = -1, .lanecfg1_grf_reg = RK3568_GRF_VO_CON3, .lanecfg1 = HIWORD_UPDATE(0, RK3568_DSI1_SKEWCALHS | RK3568_DSI1_FORCETXSTOPMODE | From 471bf2406c043491b1a8288e5f04bc278f7d7ca1 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Fri, 28 Oct 2022 11:52:05 +0200 Subject: [PATCH 0216/1823] drm/rockchip: vop2: fix null pointer in plane_atomic_disable If the vop2_plane_atomic_disable function is called with NULL as a state, accessing the old_pstate runs into a null pointer exception. However, the drm_atomic_helper_disable_planes_on_crtc function calls the atomic_disable callback with state NULL. Allow to disable a plane without passing a plane state by checking the old_pstate only if a state is passed. Signed-off-by: Michael Tretter Signed-off-by: Heiko Stuebner Link: https://patchwork.freedesktop.org/patch/msgid/20221028095206.2136601-2-m.tretter@pengutronix.de --- drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index aac20be5ac08..26f8a8489ded 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -996,13 +996,15 @@ static int vop2_plane_atomic_check(struct drm_plane *plane, static void vop2_plane_atomic_disable(struct drm_plane *plane, struct drm_atomic_state *state) { - struct drm_plane_state *old_pstate = drm_atomic_get_old_plane_state(state, plane); + struct drm_plane_state *old_pstate = NULL; struct vop2_win *win = to_vop2_win(plane); struct vop2 *vop2 = win->vop2; drm_dbg(vop2->drm, "%s disable\n", win->data->name); - if (!old_pstate->crtc) + if (state) + old_pstate = drm_atomic_get_old_plane_state(state, plane); + if (old_pstate && !old_pstate->crtc) return; vop2_win_disable(win); From 447fb14bf07905b880c9ed1ea92c53d6dd0649d7 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Fri, 28 Oct 2022 11:52:06 +0200 Subject: [PATCH 0217/1823] drm/rockchip: vop2: disable planes when disabling the crtc The vop2 driver needs to explicitly disable the planes if the crtc is disabled. Unless the planes are explicitly disabled, the address of the last framebuffer is kept in the registers of the VOP2. When re-enabling the encoder after it has been disabled by the driver, the VOP2 will start and read the framebuffer that has been freed but is still pointed to by the register. The iommu will catch these read accesses and print errors. Explicitly disable the planes when the crtc is disabled to reset the registers. Signed-off-by: Michael Tretter Signed-off-by: Heiko Stuebner Link: https://patchwork.freedesktop.org/patch/msgid/20221028095206.2136601-3-m.tretter@pengutronix.de --- drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 26f8a8489ded..105a548d0abe 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -877,10 +877,14 @@ static void vop2_crtc_atomic_disable(struct drm_crtc *crtc, { struct vop2_video_port *vp = to_vop2_video_port(crtc); struct vop2 *vop2 = vp->vop2; + struct drm_crtc_state *old_crtc_state; int ret; vop2_lock(vop2); + old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc); + drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false); + drm_crtc_vblank_off(crtc); /* From 88e1f16ba58665e9edfce437ea487da2fa759af9 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Thu, 1 Sep 2022 03:10:07 +0000 Subject: [PATCH 0218/1823] ksefltests: pidfd: Fix wait_states: Test terminated by timeout 0Day/LKP observed that the kselftest blocks forever since one of the pidfd_wait doesn't terminate in 1 of 30 runs. After digging into the source, we found that it blocks at: ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WCONTINUED, NULL), 0); wait_states has below testing flow: CHILD PARENT ---------------+-------------- 1 STOP itself 2 WAIT for CHILD STOPPED 3 SIGNAL CHILD to CONT 4 CONT 5 STOP itself 5' WAIT for CHILD CONT 6 WAIT for CHILD STOPPED The problem is that the kernel cannot ensure the order of 5 and 5', once 5 goes first, the test will fail. we can reproduce it by: $ while true; do make run_tests -C pidfd; done Introduce a blocking read in child process to make sure the parent can check its WCONTINUED. CC: Philip Li Reported-by: kernel test robot Signed-off-by: Li Zhijian Reviewed-by: Christian Brauner (Microsoft) Signed-off-by: Shuah Khan --- tools/testing/selftests/pidfd/pidfd_wait.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tools/testing/selftests/pidfd/pidfd_wait.c b/tools/testing/selftests/pidfd/pidfd_wait.c index 070c1c876df1..c3e2a3041f55 100644 --- a/tools/testing/selftests/pidfd/pidfd_wait.c +++ b/tools/testing/selftests/pidfd/pidfd_wait.c @@ -95,20 +95,28 @@ TEST(wait_states) .flags = CLONE_PIDFD | CLONE_PARENT_SETTID, .exit_signal = SIGCHLD, }; + int pfd[2]; pid_t pid; siginfo_t info = { .si_signo = 0, }; + ASSERT_EQ(pipe(pfd), 0); pid = sys_clone3(&args); ASSERT_GE(pid, 0); if (pid == 0) { + char buf[2]; + + close(pfd[1]); kill(getpid(), SIGSTOP); + ASSERT_EQ(read(pfd[0], buf, 1), 1); + close(pfd[0]); kill(getpid(), SIGSTOP); exit(EXIT_SUCCESS); } + close(pfd[0]); ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WSTOPPED, NULL), 0); ASSERT_EQ(info.si_signo, SIGCHLD); ASSERT_EQ(info.si_code, CLD_STOPPED); @@ -117,6 +125,8 @@ TEST(wait_states) ASSERT_EQ(sys_pidfd_send_signal(pidfd, SIGCONT, NULL, 0), 0); ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WCONTINUED, NULL), 0); + ASSERT_EQ(write(pfd[1], "C", 1), 1); + close(pfd[1]); ASSERT_EQ(info.si_signo, SIGCHLD); ASSERT_EQ(info.si_code, CLD_CONTINUED); ASSERT_EQ(info.si_pid, parent_tid); From 3d982441308ebdf713771c8a85c23d9b8b66b4d4 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Thu, 1 Sep 2022 03:17:34 +0000 Subject: [PATCH 0219/1823] selftests: pidfd: Fix compling warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix warnings and enable Wall. pidfd_wait.c: In function ‘wait_nonblock’: pidfd_wait.c:150:13: warning: unused variable ‘status’ [-Wunused-variable] 150 | int pidfd, status = 0; | ^~~~~~ ... pidfd_test.c: In function ‘child_poll_exec_test’: pidfd_test.c:438:1: warning: no return statement in function returning non-void [-Wreturn-type] 438 | } | ^ Signed-off-by: Li Zhijian v2: fix mistake assignment to pidfd Signed-off-by: Shuah Khan --- tools/testing/selftests/pidfd/Makefile | 2 +- tools/testing/selftests/pidfd/pidfd_test.c | 2 ++ tools/testing/selftests/pidfd/pidfd_wait.c | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/pidfd/Makefile b/tools/testing/selftests/pidfd/Makefile index f4a2f28f926b..778b6cdc8aed 100644 --- a/tools/testing/selftests/pidfd/Makefile +++ b/tools/testing/selftests/pidfd/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -CFLAGS += -g -I../../../../usr/include/ -pthread +CFLAGS += -g -I../../../../usr/include/ -pthread -Wall TEST_GEN_PROGS := pidfd_test pidfd_fdinfo_test pidfd_open_test \ pidfd_poll_test pidfd_wait pidfd_getfd_test pidfd_setns_test diff --git a/tools/testing/selftests/pidfd/pidfd_test.c b/tools/testing/selftests/pidfd/pidfd_test.c index 9a2d64901d59..d36654265b7a 100644 --- a/tools/testing/selftests/pidfd/pidfd_test.c +++ b/tools/testing/selftests/pidfd/pidfd_test.c @@ -435,6 +435,8 @@ static int child_poll_exec_test(void *args) */ while (1) sleep(1); + + return 0; } static void test_pidfd_poll_exec(int use_waitpid) diff --git a/tools/testing/selftests/pidfd/pidfd_wait.c b/tools/testing/selftests/pidfd/pidfd_wait.c index c3e2a3041f55..0dcb8365ddc3 100644 --- a/tools/testing/selftests/pidfd/pidfd_wait.c +++ b/tools/testing/selftests/pidfd/pidfd_wait.c @@ -148,7 +148,7 @@ TEST(wait_states) TEST(wait_nonblock) { - int pidfd, status = 0; + int pidfd; unsigned int flags = 0; pid_t parent_tid = -1; struct clone_args args = { From 67a9aeef44e42b1ac2becf5e61eae0880f48d9db Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Mon, 24 Oct 2022 18:55:44 +0200 Subject: [PATCH 0220/1823] arm64: dts: rockchip: fix node name for hym8563 rtc Fix the node name for hym8563 in all arm64 rockchip devicetrees. Signed-off-by: Sebastian Reichel Link: https://lore.kernel.org/r/20221024165549.74574-2-sebastian.reichel@collabora.com Signed-off-by: Heiko Stuebner --- arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts | 2 +- arch/arm64/boot/dts/rockchip/rk3368-r88.dts | 2 +- arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts | 2 +- arch/arm64/boot/dts/rockchip/rk3399pro-vmarc-som.dtsi | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts b/arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts index 7f5bba0c6001..0e88e9592c1c 100644 --- a/arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts +++ b/arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts @@ -208,7 +208,7 @@ vin-supply = <&vcc_sys>; }; - hym8563: hym8563@51 { + hym8563: rtc@51 { compatible = "haoyu,hym8563"; reg = <0x51>; #clock-cells = <0>; diff --git a/arch/arm64/boot/dts/rockchip/rk3368-r88.dts b/arch/arm64/boot/dts/rockchip/rk3368-r88.dts index 38d757c00548..e147d6f8b43e 100644 --- a/arch/arm64/boot/dts/rockchip/rk3368-r88.dts +++ b/arch/arm64/boot/dts/rockchip/rk3368-r88.dts @@ -192,7 +192,7 @@ vin-supply = <&vcc_sys>; }; - hym8563: hym8563@51 { + hym8563: rtc@51 { compatible = "haoyu,hym8563"; reg = <0x51>; #clock-cells = <0>; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts b/arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts index 5a2661ae0131..18b5050c6cd3 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts +++ b/arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts @@ -98,7 +98,7 @@ }; &i2c0 { - hym8563: hym8563@51 { + hym8563: rtc@51 { compatible = "haoyu,hym8563"; reg = <0x51>; interrupt-parent = <&gpio0>; diff --git a/arch/arm64/boot/dts/rockchip/rk3399pro-vmarc-som.dtsi b/arch/arm64/boot/dts/rockchip/rk3399pro-vmarc-som.dtsi index 935b8c68a71d..6c168566321b 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399pro-vmarc-som.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399pro-vmarc-som.dtsi @@ -297,7 +297,7 @@ clock-frequency = <400000>; status = "okay"; - hym8563: hym8563@51 { + hym8563: rtc@51 { compatible = "haoyu,hym8563"; reg = <0x51>; #clock-cells = <0>; From 2af5bbe32f50d196dd680478a889d12429b3e8cf Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Mon, 24 Oct 2022 18:55:45 +0200 Subject: [PATCH 0221/1823] arm64: dts: rockchip: remove clock-frequency from rtc 'clock-frequency' is not part of the DT binding and not supported by the Linux driver. Signed-off-by: Sebastian Reichel Link: https://lore.kernel.org/r/20221024165549.74574-3-sebastian.reichel@collabora.com Signed-off-by: Heiko Stuebner --- arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts | 1 - arch/arm64/boot/dts/rockchip/rk3368-r88.dts | 1 - arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts | 1 - arch/arm64/boot/dts/rockchip/rk3399pro-vmarc-som.dtsi | 1 - arch/arm64/boot/dts/rockchip/rk3568-bpi-r2-pro.dts | 1 - arch/arm64/boot/dts/rockchip/rk3568-rock-3a.dts | 1 - 6 files changed, 6 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts b/arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts index 0e88e9592c1c..81d1064fdb21 100644 --- a/arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts +++ b/arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts @@ -212,7 +212,6 @@ compatible = "haoyu,hym8563"; reg = <0x51>; #clock-cells = <0>; - clock-frequency = <32768>; clock-output-names = "xin32k"; /* rtc_int is not connected */ }; diff --git a/arch/arm64/boot/dts/rockchip/rk3368-r88.dts b/arch/arm64/boot/dts/rockchip/rk3368-r88.dts index e147d6f8b43e..5589f3db6b36 100644 --- a/arch/arm64/boot/dts/rockchip/rk3368-r88.dts +++ b/arch/arm64/boot/dts/rockchip/rk3368-r88.dts @@ -196,7 +196,6 @@ compatible = "haoyu,hym8563"; reg = <0x51>; #clock-cells = <0>; - clock-frequency = <32768>; clock-output-names = "xin32k"; /* rtc_int is not connected */ }; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts b/arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts index 18b5050c6cd3..7ba1c28f70a9 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts +++ b/arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts @@ -104,7 +104,6 @@ interrupt-parent = <&gpio0>; interrupts = ; #clock-cells = <0>; - clock-frequency = <32768>; clock-output-names = "xin32k"; pinctrl-names = "default"; pinctrl-0 = <&hym8563_int>; diff --git a/arch/arm64/boot/dts/rockchip/rk3399pro-vmarc-som.dtsi b/arch/arm64/boot/dts/rockchip/rk3399pro-vmarc-som.dtsi index 6c168566321b..bf9eb0405b62 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399pro-vmarc-som.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399pro-vmarc-som.dtsi @@ -301,7 +301,6 @@ compatible = "haoyu,hym8563"; reg = <0x51>; #clock-cells = <0>; - clock-frequency = <32768>; clock-output-names = "hym8563"; pinctrl-names = "default"; pinctrl-0 = <&hym8563_int>; diff --git a/arch/arm64/boot/dts/rockchip/rk3568-bpi-r2-pro.dts b/arch/arm64/boot/dts/rockchip/rk3568-bpi-r2-pro.dts index c282f6e79960..26d7fda275ed 100644 --- a/arch/arm64/boot/dts/rockchip/rk3568-bpi-r2-pro.dts +++ b/arch/arm64/boot/dts/rockchip/rk3568-bpi-r2-pro.dts @@ -500,7 +500,6 @@ interrupt-parent = <&gpio0>; interrupts = ; #clock-cells = <0>; - clock-frequency = <32768>; clock-output-names = "rtcic_32kout"; pinctrl-names = "default"; pinctrl-0 = <&hym8563_int>; diff --git a/arch/arm64/boot/dts/rockchip/rk3568-rock-3a.dts b/arch/arm64/boot/dts/rockchip/rk3568-rock-3a.dts index fb87a168fe96..539ef8cc7792 100644 --- a/arch/arm64/boot/dts/rockchip/rk3568-rock-3a.dts +++ b/arch/arm64/boot/dts/rockchip/rk3568-rock-3a.dts @@ -509,7 +509,6 @@ interrupt-parent = <&gpio0>; interrupts = ; #clock-cells = <0>; - clock-frequency = <32768>; clock-output-names = "rtcic_32kout"; pinctrl-names = "default"; pinctrl-0 = <&hym8563_int>; From 17b57beafccb4569accbfc8c11390744cf59c021 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Mon, 24 Oct 2022 18:55:46 +0200 Subject: [PATCH 0222/1823] arm: dts: rockchip: fix node name for hym8563 rtc Fix the node name for hym8563 in all arm rockchip devicetrees. Signed-off-by: Sebastian Reichel Link: https://lore.kernel.org/r/20221024165549.74574-4-sebastian.reichel@collabora.com Signed-off-by: Heiko Stuebner --- arch/arm/boot/dts/rk3036-evb.dts | 2 +- arch/arm/boot/dts/rk3288-evb-act8846.dts | 2 +- arch/arm/boot/dts/rk3288-firefly.dtsi | 2 +- arch/arm/boot/dts/rk3288-miqi.dts | 2 +- arch/arm/boot/dts/rk3288-rock2-square.dts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/arm/boot/dts/rk3036-evb.dts b/arch/arm/boot/dts/rk3036-evb.dts index 9fd4d9db9f8f..89b0927ce162 100644 --- a/arch/arm/boot/dts/rk3036-evb.dts +++ b/arch/arm/boot/dts/rk3036-evb.dts @@ -35,7 +35,7 @@ &i2c1 { status = "okay"; - hym8563: hym8563@51 { + hym8563: rtc@51 { compatible = "haoyu,hym8563"; reg = <0x51>; #clock-cells = <0>; diff --git a/arch/arm/boot/dts/rk3288-evb-act8846.dts b/arch/arm/boot/dts/rk3288-evb-act8846.dts index be695b8c1f67..8a635c243127 100644 --- a/arch/arm/boot/dts/rk3288-evb-act8846.dts +++ b/arch/arm/boot/dts/rk3288-evb-act8846.dts @@ -54,7 +54,7 @@ vin-supply = <&vcc_sys>; }; - hym8563@51 { + rtc@51 { compatible = "haoyu,hym8563"; reg = <0x51>; diff --git a/arch/arm/boot/dts/rk3288-firefly.dtsi b/arch/arm/boot/dts/rk3288-firefly.dtsi index 052afe5543e2..9267857beccb 100644 --- a/arch/arm/boot/dts/rk3288-firefly.dtsi +++ b/arch/arm/boot/dts/rk3288-firefly.dtsi @@ -233,7 +233,7 @@ vin-supply = <&vcc_sys>; }; - hym8563: hym8563@51 { + hym8563: rtc@51 { compatible = "haoyu,hym8563"; reg = <0x51>; #clock-cells = <0>; diff --git a/arch/arm/boot/dts/rk3288-miqi.dts b/arch/arm/boot/dts/rk3288-miqi.dts index 713f55e143c6..e3d5644f2915 100644 --- a/arch/arm/boot/dts/rk3288-miqi.dts +++ b/arch/arm/boot/dts/rk3288-miqi.dts @@ -162,7 +162,7 @@ vin-supply = <&vcc_sys>; }; - hym8563: hym8563@51 { + hym8563: rtc@51 { compatible = "haoyu,hym8563"; reg = <0x51>; #clock-cells = <0>; diff --git a/arch/arm/boot/dts/rk3288-rock2-square.dts b/arch/arm/boot/dts/rk3288-rock2-square.dts index 80e0f07c8e87..07a3a52753d2 100644 --- a/arch/arm/boot/dts/rk3288-rock2-square.dts +++ b/arch/arm/boot/dts/rk3288-rock2-square.dts @@ -165,7 +165,7 @@ }; &i2c0 { - hym8563: hym8563@51 { + hym8563: rtc@51 { compatible = "haoyu,hym8563"; reg = <0x51>; #clock-cells = <0>; From 6122f3be70d90a1b2a1188d8910256fc218376a9 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Mon, 24 Oct 2022 18:55:47 +0200 Subject: [PATCH 0223/1823] arm: dts: rockchip: remove clock-frequency from rtc 'clock-frequency' is not part of the DT binding and not supported by the Linux driver. Signed-off-by: Sebastian Reichel Link: https://lore.kernel.org/r/20221024165549.74574-5-sebastian.reichel@collabora.com Signed-off-by: Heiko Stuebner --- arch/arm/boot/dts/rk3036-evb.dts | 1 - arch/arm/boot/dts/rk3288-firefly.dtsi | 1 - arch/arm/boot/dts/rk3288-miqi.dts | 1 - arch/arm/boot/dts/rk3288-rock2-square.dts | 1 - arch/arm/boot/dts/rk3288-vmarc-som.dtsi | 1 - 5 files changed, 5 deletions(-) diff --git a/arch/arm/boot/dts/rk3036-evb.dts b/arch/arm/boot/dts/rk3036-evb.dts index 89b0927ce162..becdc0b664bf 100644 --- a/arch/arm/boot/dts/rk3036-evb.dts +++ b/arch/arm/boot/dts/rk3036-evb.dts @@ -39,7 +39,6 @@ compatible = "haoyu,hym8563"; reg = <0x51>; #clock-cells = <0>; - clock-frequency = <32768>; clock-output-names = "xin32k"; }; }; diff --git a/arch/arm/boot/dts/rk3288-firefly.dtsi b/arch/arm/boot/dts/rk3288-firefly.dtsi index 9267857beccb..3836c61cfb76 100644 --- a/arch/arm/boot/dts/rk3288-firefly.dtsi +++ b/arch/arm/boot/dts/rk3288-firefly.dtsi @@ -237,7 +237,6 @@ compatible = "haoyu,hym8563"; reg = <0x51>; #clock-cells = <0>; - clock-frequency = <32768>; clock-output-names = "xin32k"; interrupt-parent = <&gpio7>; interrupts = ; diff --git a/arch/arm/boot/dts/rk3288-miqi.dts b/arch/arm/boot/dts/rk3288-miqi.dts index e3d5644f2915..db1eb648e0e1 100644 --- a/arch/arm/boot/dts/rk3288-miqi.dts +++ b/arch/arm/boot/dts/rk3288-miqi.dts @@ -166,7 +166,6 @@ compatible = "haoyu,hym8563"; reg = <0x51>; #clock-cells = <0>; - clock-frequency = <32768>; clock-output-names = "xin32k"; }; diff --git a/arch/arm/boot/dts/rk3288-rock2-square.dts b/arch/arm/boot/dts/rk3288-rock2-square.dts index 07a3a52753d2..13cfdaa95cc7 100644 --- a/arch/arm/boot/dts/rk3288-rock2-square.dts +++ b/arch/arm/boot/dts/rk3288-rock2-square.dts @@ -169,7 +169,6 @@ compatible = "haoyu,hym8563"; reg = <0x51>; #clock-cells = <0>; - clock-frequency = <32768>; clock-output-names = "xin32k"; interrupt-parent = <&gpio0>; interrupts = ; diff --git a/arch/arm/boot/dts/rk3288-vmarc-som.dtsi b/arch/arm/boot/dts/rk3288-vmarc-som.dtsi index 0ae2bd150e37..793951655b73 100644 --- a/arch/arm/boot/dts/rk3288-vmarc-som.dtsi +++ b/arch/arm/boot/dts/rk3288-vmarc-som.dtsi @@ -241,7 +241,6 @@ interrupt-parent = <&gpio5>; interrupts = ; #clock-cells = <0>; - clock-frequency = <32768>; clock-output-names = "hym8563"; pinctrl-names = "default"; pinctrl-0 = <&hym8563_int>; From 942b35de22efeb4f9ded83f1ea7747f3fe5a3bb2 Mon Sep 17 00:00:00 2001 From: Johan Jonker Date: Thu, 27 Oct 2022 10:37:32 +0200 Subject: [PATCH 0224/1823] ARM: dts: rockchip: fix adc-keys sub node names Fix adc-keys sub node names on Rockchip boards, so that they match with regex: '^button-' Signed-off-by: Johan Jonker Link: https://lore.kernel.org/r/7a0013b1-3a55-a344-e9ea-eacb4b49433c@gmail.com Signed-off-by: Heiko Stuebner --- arch/arm/boot/dts/rk3066a-mk808.dts | 2 +- arch/arm/boot/dts/rk3288-evb.dtsi | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm/boot/dts/rk3066a-mk808.dts b/arch/arm/boot/dts/rk3066a-mk808.dts index cfa318a506eb..2db5ba706208 100644 --- a/arch/arm/boot/dts/rk3066a-mk808.dts +++ b/arch/arm/boot/dts/rk3066a-mk808.dts @@ -32,7 +32,7 @@ keyup-threshold-microvolt = <2500000>; poll-interval = <100>; - recovery { + button-recovery { label = "recovery"; linux,code = ; press-threshold-microvolt = <0>; diff --git a/arch/arm/boot/dts/rk3288-evb.dtsi b/arch/arm/boot/dts/rk3288-evb.dtsi index 399d6b9c5fd4..382d2839cf47 100644 --- a/arch/arm/boot/dts/rk3288-evb.dtsi +++ b/arch/arm/boot/dts/rk3288-evb.dtsi @@ -28,19 +28,19 @@ press-threshold-microvolt = <300000>; }; - menu { + button-menu { label = "Menu"; linux,code = ; press-threshold-microvolt = <640000>; }; - esc { + button-esc { label = "Esc"; linux,code = ; press-threshold-microvolt = <1000000>; }; - home { + button-home { label = "Home"; linux,code = ; press-threshold-microvolt = <1300000>; From f2bd2e76d6ea13e12849975adae46145375532a4 Mon Sep 17 00:00:00 2001 From: Johan Jonker Date: Thu, 27 Oct 2022 10:38:35 +0200 Subject: [PATCH 0225/1823] arm64: dts: rockchip: fix adc-keys sub node names Fix adc-keys sub node names on Rockchip boards, so that they match with regex: '^button-' Signed-off-by: Johan Jonker Link: https://lore.kernel.org/r/6a6a3603-5540-cacc-2672-c015af1ec684@gmail.com Signed-off-by: Heiko Stuebner --- arch/arm64/boot/dts/rockchip/px30-evb.dts | 10 +++++----- arch/arm64/boot/dts/rockchip/rk3308-evb.dts | 12 ++++++------ arch/arm64/boot/dts/rockchip/rk3318-a95x-z2.dts | 2 +- arch/arm64/boot/dts/rockchip/rk3399-khadas-edge.dtsi | 2 +- arch/arm64/boot/dts/rockchip/rk3399-nanopc-t4.dts | 2 +- arch/arm64/boot/dts/rockchip/rk3399-nanopi-m4b.dts | 2 +- arch/arm64/boot/dts/rockchip/rk3399-orangepi.dts | 4 ++-- arch/arm64/boot/dts/rockchip/rk3399-roc-pc.dtsi | 2 +- .../boot/dts/rockchip/rk3399-sapphire-excavator.dts | 4 ++-- arch/arm64/boot/dts/rockchip/rk3566-pinenote.dtsi | 2 +- 10 files changed, 21 insertions(+), 21 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/px30-evb.dts b/arch/arm64/boot/dts/rockchip/px30-evb.dts index 07008d84434c..c1bbd555f5f5 100644 --- a/arch/arm64/boot/dts/rockchip/px30-evb.dts +++ b/arch/arm64/boot/dts/rockchip/px30-evb.dts @@ -30,31 +30,31 @@ keyup-threshold-microvolt = <1800000>; poll-interval = <100>; - esc-key { + button-esc { label = "esc"; linux,code = ; press-threshold-microvolt = <1310000>; }; - home-key { + button-home { label = "home"; linux,code = ; press-threshold-microvolt = <624000>; }; - menu-key { + button-menu { label = "menu"; linux,code = ; press-threshold-microvolt = <987000>; }; - vol-down-key { + button-down { label = "volume down"; linux,code = ; press-threshold-microvolt = <300000>; }; - vol-up-key { + button-up { label = "volume up"; linux,code = ; press-threshold-microvolt = <17000>; diff --git a/arch/arm64/boot/dts/rockchip/rk3308-evb.dts b/arch/arm64/boot/dts/rockchip/rk3308-evb.dts index 9fe9b0d11003..184b84fdde07 100644 --- a/arch/arm64/boot/dts/rockchip/rk3308-evb.dts +++ b/arch/arm64/boot/dts/rockchip/rk3308-evb.dts @@ -23,7 +23,7 @@ poll-interval = <100>; keyup-threshold-microvolt = <1800000>; - func-key { + button-func { linux,code = ; label = "function"; press-threshold-microvolt = <18000>; @@ -37,31 +37,31 @@ poll-interval = <100>; keyup-threshold-microvolt = <1800000>; - esc-key { + button-esc { linux,code = ; label = "micmute"; press-threshold-microvolt = <1130000>; }; - home-key { + button-home { linux,code = ; label = "mode"; press-threshold-microvolt = <901000>; }; - menu-key { + button-menu { linux,code = ; label = "play"; press-threshold-microvolt = <624000>; }; - vol-down-key { + button-down { linux,code = ; label = "volume down"; press-threshold-microvolt = <300000>; }; - vol-up-key { + button-up { linux,code = ; label = "volume up"; press-threshold-microvolt = <18000>; diff --git a/arch/arm64/boot/dts/rockchip/rk3318-a95x-z2.dts b/arch/arm64/boot/dts/rockchip/rk3318-a95x-z2.dts index 43c928ac98f0..1deef53a4c94 100644 --- a/arch/arm64/boot/dts/rockchip/rk3318-a95x-z2.dts +++ b/arch/arm64/boot/dts/rockchip/rk3318-a95x-z2.dts @@ -25,7 +25,7 @@ keyup-threshold-microvolt = <1800000>; poll-interval = <100>; - recovery { + button-recovery { label = "recovery"; linux,code = ; press-threshold-microvolt = <17000>; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-khadas-edge.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-khadas-edge.dtsi index 2a332763c35c..9d9297bc5f04 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-khadas-edge.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399-khadas-edge.dtsi @@ -123,7 +123,7 @@ keyup-threshold-microvolt = <1800000>; poll-interval = <100>; - recovery { + button-recovery { label = "Recovery"; linux,code = ; press-threshold-microvolt = <18000>; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-nanopc-t4.dts b/arch/arm64/boot/dts/rockchip/rk3399-nanopc-t4.dts index 452728b82e42..3bf8f959e42c 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-nanopc-t4.dts +++ b/arch/arm64/boot/dts/rockchip/rk3399-nanopc-t4.dts @@ -39,7 +39,7 @@ keyup-threshold-microvolt = <1800000>; poll-interval = <100>; - recovery { + button-recovery { label = "Recovery"; linux,code = ; press-threshold-microvolt = <18000>; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-nanopi-m4b.dts b/arch/arm64/boot/dts/rockchip/rk3399-nanopi-m4b.dts index 72182c58cc46..65cb21837b0c 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-nanopi-m4b.dts +++ b/arch/arm64/boot/dts/rockchip/rk3399-nanopi-m4b.dts @@ -19,7 +19,7 @@ keyup-threshold-microvolt = <1500000>; poll-interval = <100>; - recovery { + button-recovery { label = "Recovery"; linux,code = ; press-threshold-microvolt = <18000>; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-orangepi.dts b/arch/arm64/boot/dts/rockchip/rk3399-orangepi.dts index 9e2e246e0bab..dba4d03bfc2b 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-orangepi.dts +++ b/arch/arm64/boot/dts/rockchip/rk3399-orangepi.dts @@ -52,13 +52,13 @@ press-threshold-microvolt = <300000>; }; - back { + button-back { label = "Back"; linux,code = ; press-threshold-microvolt = <985000>; }; - menu { + button-menu { label = "Menu"; linux,code = ; press-threshold-microvolt = <1314000>; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-roc-pc.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-roc-pc.dtsi index 2f4b1b2e3ac7..bbf1e3f24585 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-roc-pc.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399-roc-pc.dtsi @@ -41,7 +41,7 @@ keyup-threshold-microvolt = <1500000>; poll-interval = <100>; - recovery { + button-recovery { label = "Recovery"; linux,code = ; press-threshold-microvolt = <18000>; diff --git a/arch/arm64/boot/dts/rockchip/rk3399-sapphire-excavator.dts b/arch/arm64/boot/dts/rockchip/rk3399-sapphire-excavator.dts index 13927e7d0724..dbec2b7173a0 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-sapphire-excavator.dts +++ b/arch/arm64/boot/dts/rockchip/rk3399-sapphire-excavator.dts @@ -33,13 +33,13 @@ press-threshold-microvolt = <300000>; }; - back { + button-back { label = "Back"; linux,code = ; press-threshold-microvolt = <985000>; }; - menu { + button-menu { label = "Menu"; linux,code = ; press-threshold-microvolt = <1314000>; diff --git a/arch/arm64/boot/dts/rockchip/rk3566-pinenote.dtsi b/arch/arm64/boot/dts/rockchip/rk3566-pinenote.dtsi index 0d45868132b9..8d61f824c12d 100644 --- a/arch/arm64/boot/dts/rockchip/rk3566-pinenote.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3566-pinenote.dtsi @@ -23,7 +23,7 @@ io-channel-names = "buttons"; keyup-threshold-microvolt = <1750000>; - recovery { + button-recovery { label = "recovery"; linux,code = ; press-threshold-microvolt = <0>; From dd847fe34cdf1e89afed1af24986359f13082bfb Mon Sep 17 00:00:00 2001 From: Johan Jonker Date: Thu, 27 Oct 2022 10:58:22 +0200 Subject: [PATCH 0226/1823] ARM: dts: rockchip: fix ir-receiver node names Fix ir-receiver node names on Rockchip boards, so that they match with regex: '^ir(-receiver)?(@[a-f0-9]+)?$' Signed-off-by: Johan Jonker Link: https://lore.kernel.org/r/ea5af279-f44c-afea-023d-bb37f5a0d58d@gmail.com Signed-off-by: Heiko Stuebner --- arch/arm/boot/dts/rk3188-radxarock.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/rk3188-radxarock.dts b/arch/arm/boot/dts/rk3188-radxarock.dts index e7cf18823558..118deacd38c4 100644 --- a/arch/arm/boot/dts/rk3188-radxarock.dts +++ b/arch/arm/boot/dts/rk3188-radxarock.dts @@ -71,7 +71,7 @@ #sound-dai-cells = <0>; }; - ir_recv: gpio-ir-receiver { + ir_recv: ir-receiver { compatible = "gpio-ir-receiver"; gpios = <&gpio0 RK_PB2 GPIO_ACTIVE_LOW>; pinctrl-names = "default"; From de0d04b9780a23eb928aedfb6f981285f78d58e5 Mon Sep 17 00:00:00 2001 From: Johan Jonker Date: Thu, 27 Oct 2022 10:59:10 +0200 Subject: [PATCH 0227/1823] arm64: dts: rockchip: fix ir-receiver node names Fix ir-receiver node names on Rockchip boards, so that they match with regex: '^ir(-receiver)?(@[a-f0-9]+)?$' Signed-off-by: Johan Jonker Link: https://lore.kernel.org/r/e9764253-8ce8-150b-4820-41f03f845469@gmail.com Signed-off-by: Heiko Stuebner --- arch/arm64/boot/dts/rockchip/rk3308-roc-cc.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3308-roc-cc.dts b/arch/arm64/boot/dts/rockchip/rk3308-roc-cc.dts index ea6820902ede..7ea48167747c 100644 --- a/arch/arm64/boot/dts/rockchip/rk3308-roc-cc.dts +++ b/arch/arm64/boot/dts/rockchip/rk3308-roc-cc.dts @@ -19,7 +19,7 @@ stdout-path = "serial2:1500000n8"; }; - ir_rx { + ir-receiver { compatible = "gpio-ir-receiver"; gpios = <&gpio0 RK_PC0 GPIO_ACTIVE_HIGH>; pinctrl-names = "default"; From 11871e20bcb23c00966e785a124fb72bc8340af4 Mon Sep 17 00:00:00 2001 From: Johan Jonker Date: Thu, 27 Oct 2022 01:31:37 +0200 Subject: [PATCH 0228/1823] ARM: dts: rockchip: rk3188: fix lcdc1-rgb24 node name The lcdc1-rgb24 node name is out of line with the rest of the rk3188 lcdc1 node, so fix it. Signed-off-by: Johan Jonker Link: https://lore.kernel.org/r/7b9c0a6f-626b-07e8-ae74-7e0f08b8d241@gmail.com Signed-off-by: Heiko Stuebner --- arch/arm/boot/dts/rk3188.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/rk3188.dtsi b/arch/arm/boot/dts/rk3188.dtsi index cdd4a0bd5133..b8f34bef0efa 100644 --- a/arch/arm/boot/dts/rk3188.dtsi +++ b/arch/arm/boot/dts/rk3188.dtsi @@ -379,7 +379,7 @@ rockchip,pins = <2 RK_PD3 1 &pcfg_pull_none>; }; - lcdc1_rgb24: ldcd1-rgb24 { + lcdc1_rgb24: lcdc1-rgb24 { rockchip,pins = <2 RK_PA0 1 &pcfg_pull_none>, <2 RK_PA1 1 &pcfg_pull_none>, <2 RK_PA2 1 &pcfg_pull_none>, From 8e4aae6b8ca76afb1fb64dcb24be44ba814e7f8a Mon Sep 17 00:00:00 2001 From: Shang XiaoJing Date: Thu, 27 Oct 2022 22:03:29 +0800 Subject: [PATCH 0229/1823] nfc: fdp: Fix potential memory leak in fdp_nci_send() fdp_nci_send() will call fdp_nci_i2c_write that will not free skb in the function. As a result, when fdp_nci_i2c_write() finished, the skb will memleak. fdp_nci_send() should free skb after fdp_nci_i2c_write() finished. Fixes: a06347c04c13 ("NFC: Add Intel Fields Peak NFC solution driver") Signed-off-by: Shang XiaoJing Signed-off-by: David S. Miller --- drivers/nfc/fdp/fdp.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/nfc/fdp/fdp.c b/drivers/nfc/fdp/fdp.c index c6b3334f24c9..f12f903a9dd1 100644 --- a/drivers/nfc/fdp/fdp.c +++ b/drivers/nfc/fdp/fdp.c @@ -249,11 +249,19 @@ static int fdp_nci_close(struct nci_dev *ndev) static int fdp_nci_send(struct nci_dev *ndev, struct sk_buff *skb) { struct fdp_nci_info *info = nci_get_drvdata(ndev); + int ret; if (atomic_dec_and_test(&info->data_pkt_counter)) info->data_pkt_counter_cb(ndev); - return info->phy_ops->write(info->phy, skb); + ret = info->phy_ops->write(info->phy, skb); + if (ret < 0) { + kfree_skb(skb); + return ret; + } + + consume_skb(skb); + return 0; } static int fdp_nci_request_firmware(struct nci_dev *ndev) From 7bf1ed6aff0f70434bd0cdd45495e83f1dffb551 Mon Sep 17 00:00:00 2001 From: Shang XiaoJing Date: Thu, 27 Oct 2022 22:03:30 +0800 Subject: [PATCH 0230/1823] nfc: nxp-nci: Fix potential memory leak in nxp_nci_send() nxp_nci_send() will call nxp_nci_i2c_write(), and only free skb when nxp_nci_i2c_write() failed. However, even if the nxp_nci_i2c_write() run succeeds, the skb will not be freed in nxp_nci_i2c_write(). As the result, the skb will memleak. nxp_nci_send() should also free the skb when nxp_nci_i2c_write() succeeds. Fixes: dece45855a8b ("NFC: nxp-nci: Add support for NXP NCI chips") Signed-off-by: Shang XiaoJing Signed-off-by: David S. Miller --- drivers/nfc/nxp-nci/core.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/nfc/nxp-nci/core.c b/drivers/nfc/nxp-nci/core.c index 7c93d484dc1b..580cb6ecffee 100644 --- a/drivers/nfc/nxp-nci/core.c +++ b/drivers/nfc/nxp-nci/core.c @@ -80,10 +80,13 @@ static int nxp_nci_send(struct nci_dev *ndev, struct sk_buff *skb) return -EINVAL; r = info->phy_ops->write(info->phy_id, skb); - if (r < 0) + if (r < 0) { kfree_skb(skb); + return r; + } - return r; + consume_skb(skb); + return 0; } static int nxp_nci_rf_pll_unlocked_ntf(struct nci_dev *ndev, From 3a146b7e3099dc7cf3114f627d9b79291e2d2203 Mon Sep 17 00:00:00 2001 From: Shang XiaoJing Date: Thu, 27 Oct 2022 22:03:31 +0800 Subject: [PATCH 0231/1823] nfc: s3fwrn5: Fix potential memory leak in s3fwrn5_nci_send() s3fwrn5_nci_send() will call s3fwrn5_i2c_write() or s3fwrn82_uart_write(), and free the skb if write() failed. However, even if the write() run succeeds, the skb will not be freed in write(). As the result, the skb will memleak. s3fwrn5_nci_send() should also free the skb when write() succeeds. Fixes: c04c674fadeb ("nfc: s3fwrn5: Add driver for Samsung S3FWRN5 NFC Chip") Signed-off-by: Shang XiaoJing Signed-off-by: David S. Miller --- drivers/nfc/s3fwrn5/core.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/nfc/s3fwrn5/core.c b/drivers/nfc/s3fwrn5/core.c index 1c412007fabb..0270e05b68df 100644 --- a/drivers/nfc/s3fwrn5/core.c +++ b/drivers/nfc/s3fwrn5/core.c @@ -110,11 +110,15 @@ static int s3fwrn5_nci_send(struct nci_dev *ndev, struct sk_buff *skb) } ret = s3fwrn5_write(info, skb); - if (ret < 0) + if (ret < 0) { kfree_skb(skb); + mutex_unlock(&info->mutex); + return ret; + } + consume_skb(skb); mutex_unlock(&info->mutex); - return ret; + return 0; } static int s3fwrn5_nci_post_setup(struct nci_dev *ndev) From 93d904a734a74c54d945a9884b4962977f1176cd Mon Sep 17 00:00:00 2001 From: Shang XiaoJing Date: Thu, 27 Oct 2022 22:03:32 +0800 Subject: [PATCH 0232/1823] nfc: nfcmrvl: Fix potential memory leak in nfcmrvl_i2c_nci_send() nfcmrvl_i2c_nci_send() will be called by nfcmrvl_nci_send(), and skb should be freed in nfcmrvl_i2c_nci_send(). However, nfcmrvl_nci_send() will only free skb when i2c_master_send() return >=0, which means skb will memleak when i2c_master_send() failed. Free skb no matter whether i2c_master_send() succeeds. Fixes: b5b3e23e4cac ("NFC: nfcmrvl: add i2c driver") Signed-off-by: Shang XiaoJing Signed-off-by: David S. Miller --- drivers/nfc/nfcmrvl/i2c.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/nfc/nfcmrvl/i2c.c b/drivers/nfc/nfcmrvl/i2c.c index acef0cfd76af..24436c9e54c9 100644 --- a/drivers/nfc/nfcmrvl/i2c.c +++ b/drivers/nfc/nfcmrvl/i2c.c @@ -132,10 +132,15 @@ static int nfcmrvl_i2c_nci_send(struct nfcmrvl_private *priv, ret = -EREMOTEIO; } else ret = 0; - kfree_skb(skb); } - return ret; + if (ret) { + kfree_skb(skb); + return ret; + } + + consume_skb(skb); + return 0; } static void nfcmrvl_i2c_nci_update_config(struct nfcmrvl_private *priv, From 2f646696147e02166d3c60de8d4d944e134d9145 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Sun, 11 Apr 2021 23:04:31 +0200 Subject: [PATCH 0233/1823] leds: ledtrig-pattern: Use last_repeat when applying hw pattern `last_repeat` holds the actual value requested by the user whereas `repeat` is a software iteration variable that is unused in hardware patterns. Furthermore `last_repeat` is the field returned to the user when reading the `repeat` sysfs property. This field is initialized to `-1` which is - together with `1` - the only valid value in the upcoming Qualcomm LPG driver. It is thus unexpected when `repeat` with an initialization value of `0` is passed into the the driver, when the sysfs property clearly presents a value of `-1`. Signed-off-by: Marijn Suijten --- drivers/leds/trigger/ledtrig-pattern.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/leds/trigger/ledtrig-pattern.c b/drivers/leds/trigger/ledtrig-pattern.c index 43a265dc4696..a11d68143757 100644 --- a/drivers/leds/trigger/ledtrig-pattern.c +++ b/drivers/leds/trigger/ledtrig-pattern.c @@ -126,7 +126,8 @@ static int pattern_trig_start_pattern(struct led_classdev *led_cdev) if (data->is_hw_pattern) { return led_cdev->pattern_set(led_cdev, data->patterns, - data->npatterns, data->repeat); + data->npatterns, + data->last_repeat); } /* At least 2 tuples for software pattern. */ From 1a7ce19c97757be2c9d8e2dac05f952b15c6b8ac Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 4 Aug 2021 16:39:35 +0200 Subject: [PATCH 0234/1823] iommu/arm-smmu-qcom: Allow choosing a custom bypass emulation context It cannot be taken for granted that the last IOMMU context is free and available for us to use it to emulate bypass streams and, at least on MSM8998's lpass iommu, using the last one will produce a crash; please note that this may not be only dependant on the SoC, but also on the firmware version. To overcome to this issue, allow specifying a different context for bypass emulation with the optional DT property "qcom,bypass-cbndx": if this property is not found this means that we are either booting with ACPI instead or that we don't want to specify a custom cb because the default one (the last context bank) is fine. Signed-off-by: AngeloGioacchino Del Regno (JAMI: fixup for v6.0-rc1) --- drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c index b2708de25ea3..fe7f7478aaa1 100644 --- a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c +++ b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c @@ -285,7 +285,8 @@ static int qcom_smmu_cfg_probe(struct arm_smmu_device *smmu) reg = arm_smmu_gr0_read(smmu, last_s2cr); if (FIELD_GET(ARM_SMMU_S2CR_TYPE, reg) != S2CR_TYPE_BYPASS) { qsmmu->bypass_quirk = true; - qsmmu->bypass_cbndx = smmu->num_context_banks - 1; + if (qsmmu->bypass_cbndx == 0xff) + qsmmu->bypass_cbndx = smmu->num_context_banks - 1; set_bit(qsmmu->bypass_cbndx, smmu->context_map); @@ -407,6 +408,7 @@ static const struct arm_smmu_impl qcom_adreno_smmu_impl = { static struct arm_smmu_device *qcom_smmu_create(struct arm_smmu_device *smmu, const struct arm_smmu_impl *impl) { + const struct device_node *np = smmu->dev->of_node; struct qcom_smmu *qsmmu; /* Check to make sure qcom_scm has finished probing */ @@ -419,6 +421,16 @@ static struct arm_smmu_device *qcom_smmu_create(struct arm_smmu_device *smmu, qsmmu->smmu.impl = impl; qsmmu->cfg = qcom_smmu_impl_data(smmu); + qsmmu->bypass_cbndx = 0xff; + + if (np != NULL) { + /* + * This property is optional and we expect to fail finding it if: + * - Using the default bypass_cbndx (in the .cfg_probe cb) is fine; or + * - We are booting on ACPI + */ + of_property_read_u8(np, "qcom,bypass-cbndx", &qsmmu->bypass_cbndx); + } return &qsmmu->smmu; } From 047a452f3f529b43ff0caef00251a789c2d42828 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Fri, 6 Aug 2021 13:14:41 +0200 Subject: [PATCH 0235/1823] iommu/arm-smmu: Allow skipping context bank disable at reset time On some SoCs some IOMMU context banks are actively used from TZ during system boot, or some hypervisor configurations will trigger a system reset upon disabling some protected/secured CBs. Allow skipping the disablement of such contexts at IOMMU reset time during initialization with a new implementation detail to work around this quirk. Signed-off-by: AngeloGioacchino Del Regno --- drivers/iommu/arm/arm-smmu/arm-smmu.c | 10 ++++++++++ drivers/iommu/arm/arm-smmu/arm-smmu.h | 1 + 2 files changed, 11 insertions(+) diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.c b/drivers/iommu/arm/arm-smmu/arm-smmu.c index 30dab1418e3f..3b8859d13503 100644 --- a/drivers/iommu/arm/arm-smmu/arm-smmu.c +++ b/drivers/iommu/arm/arm-smmu/arm-smmu.c @@ -1600,6 +1600,16 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu) /* Make sure all context banks are disabled and clear CB_FSR */ for (i = 0; i < smmu->num_context_banks; ++i) { + /* + * Some context banks cannot be disabled due to hypervisor + * configuration on some systems; if this is the case, + * skip disabling and writing FAULT on the CB FSR in order + * to avoid a system crash. + */ + if (smmu->impl && smmu->impl->reset_cb_nodisable && + smmu->impl->reset_cb_nodisable(smmu, i)) { + continue; + } arm_smmu_write_context_bank(smmu, i); arm_smmu_cb_write(smmu, i, ARM_SMMU_CB_FSR, ARM_SMMU_FSR_FAULT); } diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.h b/drivers/iommu/arm/arm-smmu/arm-smmu.h index 703fd5817ec1..2f1364b1a968 100644 --- a/drivers/iommu/arm/arm-smmu/arm-smmu.h +++ b/drivers/iommu/arm/arm-smmu/arm-smmu.h @@ -430,6 +430,7 @@ struct arm_smmu_impl { u64 val); int (*cfg_probe)(struct arm_smmu_device *smmu); int (*reset)(struct arm_smmu_device *smmu); + bool (*reset_cb_nodisable)(struct arm_smmu_device *smmu, int cbndx); int (*init_context)(struct arm_smmu_domain *smmu_domain, struct io_pgtable_cfg *cfg, struct device *dev); void (*tlb_sync)(struct arm_smmu_device *smmu, int page, int sync, From fb0b12c66df3dcb671170d9dbce7969e368660b5 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Fri, 6 Aug 2021 13:19:14 +0200 Subject: [PATCH 0236/1823] iommu/arm-smmu-qcom: Avoid disabling secured context banks Some Qualcomm SoCs' TZ/hypervisor configuration is disallowing the disablement of some context banks, being them used for tzapps and/or remote processors; any attempt to disable such CBs will result in triggering a fault and the system will freeze and/or reset. For this reason, get a list of context banks that should never get disabled during smmu initialization through a DT array property `qcom,reset-nodisable-cbs`. It was chosen to not hardcode the CBs as this is dependant on the SoC's firmware, which may vary on different boards. Signed-off-by: AngeloGioacchino Del Regno (JAMI: fixup for v6.0-rc1) --- drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c | 30 ++++++++++++++++++++++ drivers/iommu/arm/arm-smmu/arm-smmu-qcom.h | 1 + 2 files changed, 31 insertions(+) diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c index fe7f7478aaa1..825d6f253ebb 100644 --- a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c +++ b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c @@ -387,11 +387,20 @@ static int qcom_smmu500_reset(struct arm_smmu_device *smmu) return 0; } +static bool qcom_smmu500_reset_cb_nodisable(struct arm_smmu_device *smmu, + int cbndx) +{ + struct qcom_smmu *qsmmu = to_qcom_smmu(smmu); + + return test_bit(cbndx, qsmmu->reset_cb_nodisable_mask); +} + static const struct arm_smmu_impl qcom_smmu_impl = { .init_context = qcom_smmu_init_context, .cfg_probe = qcom_smmu_cfg_probe, .def_domain_type = qcom_smmu_def_domain_type, .reset = qcom_smmu500_reset, + .reset_cb_nodisable = qcom_smmu500_reset_cb_nodisable, .write_s2cr = qcom_smmu_write_s2cr, .tlb_sync = qcom_smmu_tlb_sync, }; @@ -400,6 +409,7 @@ static const struct arm_smmu_impl qcom_adreno_smmu_impl = { .init_context = qcom_adreno_smmu_init_context, .def_domain_type = qcom_smmu_def_domain_type, .reset = qcom_smmu500_reset, + .reset_cb_nodisable = qcom_smmu500_reset_cb_nodisable, .alloc_context_bank = qcom_adreno_smmu_alloc_context_bank, .write_sctlr = qcom_adreno_smmu_write_sctlr, .tlb_sync = qcom_smmu_tlb_sync, @@ -410,6 +420,8 @@ static struct arm_smmu_device *qcom_smmu_create(struct arm_smmu_device *smmu, { const struct device_node *np = smmu->dev->of_node; struct qcom_smmu *qsmmu; + u8 reset_nodisable_cbs[ARM_SMMU_MAX_CBS]; + int i, sz; /* Check to make sure qcom_scm has finished probing */ if (!qcom_scm_is_available()) @@ -422,6 +434,7 @@ static struct arm_smmu_device *qcom_smmu_create(struct arm_smmu_device *smmu, qsmmu->smmu.impl = impl; qsmmu->cfg = qcom_smmu_impl_data(smmu); qsmmu->bypass_cbndx = 0xff; + bitmap_zero(qsmmu->reset_cb_nodisable_mask, ARM_SMMU_MAX_CBS); if (np != NULL) { /* @@ -430,6 +443,23 @@ static struct arm_smmu_device *qcom_smmu_create(struct arm_smmu_device *smmu, * - We are booting on ACPI */ of_property_read_u8(np, "qcom,bypass-cbndx", &qsmmu->bypass_cbndx); + + /* + * Some context banks may not be disabled because they are + * secured: read from DT a list of secured contexts that cannot + * be disabled without crashing the system. + * This list is optional, as not all firmware configurations do + * require us skipping disablement of context banks. + */ + sz = of_property_read_variable_u8_array(np, "qcom,reset-nodisable-cbs", + reset_nodisable_cbs, + 1, ARM_SMMU_MAX_CBS); + if (sz > 0) { + for (i = 0; i < sz; i++) { + __set_bit(reset_nodisable_cbs[i], + qsmmu->reset_cb_nodisable_mask); + } + } } return &qsmmu->smmu; diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.h b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.h index 99ec8f8629a0..b22763b91775 100644 --- a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.h +++ b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.h @@ -9,6 +9,7 @@ struct qcom_smmu { struct arm_smmu_device smmu; const struct qcom_smmu_config *cfg; + DECLARE_BITMAP(reset_cb_nodisable_mask, ARM_SMMU_MAX_CBS); bool bypass_quirk; u8 bypass_cbndx; u32 stall_enabled; From 1cfedf9384b67986bb33bb7e5731f42dc08f9ac9 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Fri, 6 Aug 2021 15:15:33 +0200 Subject: [PATCH 0237/1823] iommu/arm-smmu-qcom: Don't modify sACR on hypervisor secured iommus Avoid modifying the contents of the secure Auxiliary Control Register on some Qualcomm SoCs: due to a hypervisor configuration on some firmware versions, this would result in a system crash. Signed-off-by: AngeloGioacchino Del Regno --- drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c index 825d6f253ebb..e71706e2964c 100644 --- a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c +++ b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c @@ -378,8 +378,15 @@ static int qcom_sdm845_smmu500_reset(struct arm_smmu_device *smmu) static int qcom_smmu500_reset(struct arm_smmu_device *smmu) { const struct device_node *np = smmu->dev->of_node; + struct qcom_smmu *qsmmu = to_qcom_smmu(smmu); - arm_mmu500_reset(smmu); + /* + * Execute the mmu-500 reset implementation detail only if there + * are no secured untouchable contexts in this iommu, otherwise + * the system will crash. + */ + if (bitmap_empty(qsmmu->reset_cb_nodisable_mask, ARM_SMMU_MAX_CBS)) + arm_mmu500_reset(smmu); if (of_device_is_compatible(np, "qcom,sdm845-smmu-500")) return qcom_sdm845_smmu500_reset(smmu); From 2970b7e726083412ad12ed8aff8fa00659b82e62 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 15 Dec 2020 01:06:27 +0100 Subject: [PATCH 0238/1823] cpufreq: Add MSM8998 to cpufreq-dt-platdev blocklist Add the MSM8998 to the blocklist since the CPU scaling is handled out of this. Signed-off-by: AngeloGioacchino Del Regno --- drivers/cpufreq/cpufreq-dt-platdev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c index 6ac3800db450..48a355ece763 100644 --- a/drivers/cpufreq/cpufreq-dt-platdev.c +++ b/drivers/cpufreq/cpufreq-dt-platdev.c @@ -138,6 +138,7 @@ static const struct of_device_id blocklist[] __initconst = { { .compatible = "qcom,apq8096", }, { .compatible = "qcom,msm8996", }, + { .compatible = "qcom,msm8998", }, { .compatible = "qcom,qcs404", }, { .compatible = "qcom,sa8155p" }, { .compatible = "qcom,sa8540p" }, From 00f981b1fa8825a770db2042c9fe3c368e0fde55 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 20 Oct 2020 21:09:43 +0530 Subject: [PATCH 0239/1823] dt-bindings: arm: cpus: Document 'qcom,freq-domain' property Add devicetree documentation for 'qcom,freq-domain' property specific to Qualcomm CPUs. This property is used to reference the CPUFREQ node along with Domain ID (0/1). Signed-off-by: Manivannan Sadhasivam Signed-off-by: AngeloGioacchino Del Regno --- Documentation/devicetree/bindings/arm/cpus.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/cpus.yaml b/Documentation/devicetree/bindings/arm/cpus.yaml index 5c13b73e4d57..326d60bbc537 100644 --- a/Documentation/devicetree/bindings/arm/cpus.yaml +++ b/Documentation/devicetree/bindings/arm/cpus.yaml @@ -319,6 +319,12 @@ properties: * arm/msm/qcom,kpss-acc.txt + qcom,freq-domain: + $ref: '/schemas/types.yaml#/definitions/phandle-array' + description: | + CPUs supporting freq-domain must set their "qcom,freq-domain" property + with phandle to a cpufreq_hw node followed by the Domain ID(0/1). + rockchip,pmu: $ref: '/schemas/types.yaml#/definitions/phandle' description: | From 67e553ce4b8e9aeab2ff5b45db713cf352c69167 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Thu, 17 Jun 2021 22:35:49 +0200 Subject: [PATCH 0240/1823] cpufreq: qcom-hw: Add kerneldoc to some functions Some functions may not be very straightforward to understand: add kerneldoc to some ones in order to improve readability. Signed-off-by: AngeloGioacchino Del Regno --- drivers/cpufreq/qcom-cpufreq-hw.c | 54 +++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index 833589bc95e4..693957cc1934 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -64,6 +64,13 @@ struct qcom_cpufreq_data { static unsigned long cpu_hw_rate, xo_rate; static bool icc_scaling_enabled; +/** + * qcom_cpufreq_set_bw() - Set interconnect bandwidth + * @policy: CPUFreq policy structure + * @freq_khz: CPU Frequency in KHz + * + * Returns: Zero for success, otherwise negative value on errors + */ static int qcom_cpufreq_set_bw(struct cpufreq_policy *policy, unsigned long freq_khz) { @@ -85,6 +92,20 @@ static int qcom_cpufreq_set_bw(struct cpufreq_policy *policy, return ret; } +/** + * qcom_cpufreq_update_opp() - Update CPU OPP tables + * @policy: CPUFreq policy structure + * @freq_khz: CPU Frequency for OPP entry in KHz + * @volt: CPU Voltage for OPP entry in microvolts + * + * The CPU frequencies and voltages are being read from the Operating + * State Manager (OSM) and the related OPPs, read from DT, need to be + * updated to reflect what the hardware will set for each p-state. + * If there is no OPP table specified in DT, then this function will + * add dynamic ones. + * + * Returns: Zero for success, otherwise negative value on errors + */ static int qcom_cpufreq_update_opp(struct device *cpu_dev, unsigned long freq_khz, unsigned long volt) @@ -105,6 +126,17 @@ static int qcom_cpufreq_update_opp(struct device *cpu_dev, return dev_pm_opp_enable(cpu_dev, freq_hz); } +/** + * qcom_cpufreq_hw_target_index() - Set frequency/voltage + * @policy: CPUFreq policy structure + * @index: Performance state index to be set + * + * This function sends a request to the Operating State Manager + * to set a Performance State index, so, to set frequency and + * voltage for the target CPU/cluster. + * + * Returns: Always zero + */ static int qcom_cpufreq_hw_target_index(struct cpufreq_policy *policy, unsigned int index) { @@ -125,6 +157,12 @@ static int qcom_cpufreq_hw_target_index(struct cpufreq_policy *policy, return 0; } +/** + * qcom_cpufreq_hw_get() - Get current Performance State from OSM + * @cpu: CPU number + * + * Returns: Current CPU/Cluster frequency or zero + */ static unsigned int qcom_cpufreq_hw_get(unsigned int cpu) { struct qcom_cpufreq_data *data; @@ -163,6 +201,17 @@ static unsigned int qcom_cpufreq_hw_fast_switch(struct cpufreq_policy *policy, return policy->freq_table[index].frequency; } +/** + * qcom_cpufreq_hw_read_lut() - Read Lookup Table from the OSM + * @cpu_dev: CPU device + * @policy: CPUFreq policy structure + * + * The Operating State Manager Lookup Table can always be read, even + * in case it was pre-programmed by the bootloader or by TrustZone. + * Read the LUT from it in order to build OPPs containing DVFS info. + * + * Returns: Zero for success, otherwise negative number on errors. + */ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, struct cpufreq_policy *policy) { @@ -263,6 +312,11 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, return 0; } +/* + * qcom_get_related_cpus - Get mask of CPUs in the same frequency domain + * @index: CPU number + * @m: Returned CPU mask + */ static void qcom_get_related_cpus(int index, struct cpumask *m) { struct device_node *cpu_np; From 1c851361688c29cd833374e44687f91820fb212e Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Thu, 17 Jun 2021 22:42:25 +0200 Subject: [PATCH 0241/1823] cpufreq: qcom-hw: Implement CPRh aware OSM programming On new SoCs (SDM845 onwards) the Operating State Manager (OSM) is being programmed in the bootloader and write-protected by the hypervisor, leaving to the OS read-only access to some of its registers (in order to read the Lookup Tables and also some status registers) and write access to the p-state register, for for the OS to request a specific performance state to trigger a DVFS switch on the CPU through the OSM hardware. On old SoCs though (MSM8998, SDM630/660 and variants), the bootloader will *not* initialize the OSM (and the CPRh, as it is a requirement for it) before booting the OS, making any request to trigger a performance state change ineffective, as the hardware doesn't have any Lookup Table, nor is storing any parameter to trigger a DVFS switch. In this case, basically all of the OSM registers are *not* write protected for the OS, even though some are - but write access is granted through SCM calls. This commit introduces support for OSM programming, which has to be done on these old SoCs that were distributed (almost?) always with a bootloader that does not do any CPRh nor OSM init before booting the kernel. In order to program the OSM on these SoCs, it is necessary to fullfill a "special" requirement: the Core Power Reduction Hardened (CPRh) hardware block must be initialized, as the OSM is "talking" to it in order to perform the Voltage part of DVFS; here, we are calling initialization of this through Linux generic power domains, specifically by requesting a genpd attach from the qcom-cpufreq-hw driver, which will give back voltages associated to each CPU frequency that has been declared in the OPPs, scaled and interpolated with the previous one, and will also give us parameters for the Array Power Mux (APM) and mem-acc, in order for this driver to be then able to generate the Lookup Tables that will be finally programmed to the OSM hardware. After writing the parameters to the OSM and enabling it, all the programming work will never happen anymore until a OS reboot, so all of the allocations and "the rest" will be disposed-of: this is done mainly to leave the code that was referred only to the new SoCs intact, as to also emphasize on the fact that the OSM HW is, in the end, the exact same; apart some register offsets that are slightly different, the entire logic is the same. This also adds the parameters to support CPU scaling on SDM630 and MSM8998. Signed-off-by: AngeloGioacchino Del Regno [Fixup for 5.18 & 6.0-rc1 by Jami] --- drivers/cpufreq/qcom-cpufreq-hw.c | 1078 ++++++++++++++++++++++++++++- 1 file changed, 1057 insertions(+), 21 deletions(-) diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index 693957cc1934..feb6c64e3293 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -1,43 +1,266 @@ // SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * OSM hardware initial programming + * Copyright (C) 2020, AngeloGioacchino Del Regno + * */ #include #include +#include #include #include #include +#include #include #include #include #include +#include #include #include #include #include #include +#include +#include #define LUT_MAX_ENTRIES 40U -#define LUT_SRC GENMASK(31, 30) +#define LUT_SRC_845 GENMASK(31, 30) +#define LUT_SRC_8998 GENMASK(27, 26) +#define LUT_PLL_DIV GENMASK(25, 24) #define LUT_L_VAL GENMASK(7, 0) #define LUT_CORE_COUNT GENMASK(18, 16) +#define LUT_VOLT_VC GENMASK(21, 16) #define LUT_VOLT GENMASK(11, 0) -#define CLK_HW_DIV 2 #define LUT_TURBO_IND 1 +#define OSM_BOOT_TIME_US 5 + +#define CYCLE_COUNTER_CLK_RATIO GENMASK(5, 1) +#define OSM_XO_RATIO_VAL (10 - 1) +#define CYCLE_COUNTER_USE_XO_EDGE BIT(8) + +/* FSM Boost Control */ +#define CC_BOOST_EN BIT(0) +#define PS_BOOST_EN BIT(1) +#define DCVS_BOOST_EN BIT(2) +#define BOOST_TIMER_REG_HI GENMASK(31, 16) +#define BOOST_TIMER_REG_LO GENMASK(15, 0) + +#define PLL_WAIT_LOCK_TIME_NS 2000 +#define SAFE_FREQ_WAIT_NS 1000 +#define DEXT_DECREMENT_WAIT_NS 200 + +#define BOOST_SYNC_DELAY 5 + +#define HYSTERESIS_UP_MASK GENMASK(31, 16) +#define HYSTERESIS_DN_MASK GENMASK(15, 0) +#define HYSTERESIS_CC_NS 200 +#define HYSTERESIS_LLM_NS 65535 + +/* FSM Droop Control */ +#define PC_RET_EXIT_DROOP_EN BIT(3) +#define WFX_DROOP_EN BIT(4) +#define DCVS_DROOP_EN BIT(5) +#define DROOP_TIMER1 GENMASK(31, 16) +#define DROOP_TIMER0 GENMASK(15, 0) +#define DROOP_CTRL_VAL (BIT(3) | BIT(17) | BIT(31)) +#define DROOP_TIMER_NS 100 +#define DROOP_WAIT_RELEASE_TIMER_NS 50 +#define DROOP_RELEASE_TIMER_NS 1 + +/* PLL Override Control */ +#define PLL_OVERRIDE_DROOP_EN BIT(0) + +/* Sequencer */ +#define SEQUENCER_REG(base, n) (base + (n * 4)) +#define SEQ_APM_THRESH_VC 15 +#define SEQ_APM_THRESH_PREVC 31 +#define SEQ_MEM_ACC_LVAL 32 +#define SEQ_MEM_ACC_0 55 +#define SEQ_APM_CROSSOVER_VC 72 +#define SEQ_APM_PARAM 76 +#define SEQ_MEM_ACC_CROSSOVER_VC 88 +#define SEQ_MEM_ACC_MAX_LEVELS 4 +#define SEQ_MEMACC_REG(base, n) SEQUENCER_REG(base, SEQ_MEM_ACC_0 + n) + +/* ACD */ +#define ACD_WRITE_CTL_UPDATE_EN BIT(0) +#define ACD_WRITE_CTL_SELECT_SHIFT 1 + +/** + * struct qcom_cpufreq_soc_setup_data - Register offsets for OSM setup + * + * @reg_osm_sequencer: OSM Sequencer (used to get physical address) + * @reg_override: Override parameters + * @reg_spare: Spare parameters (MEMACC-to-VC) + * @reg_cc_zero_behav: Virtual Corner for cluster power collapse + * @reg_spm_cc_hyst: DCVS-CC Wait time for frequency inc/decrement + * @reg_spm_cc_dcvs_dis: DCVS-CC en/disable control + * @reg_spm_core_ret_map: Treat cores in retention as active/inactive + * @reg_llm_freq_vote_hyst: DCVS-LLM Wait time for frequency inc/decrement + * @reg_llm_volt_vote_hyst: DCVS-LLM Wait time for voltage inc/decrement + * @reg_llm_intf_dcvs_dis: DCVS-LLM en/disable control + * @reg_seq1: Sequencer extra register + * @reg_pdn_fsm_ctrl: Boost and Droop FSMs en/disable control + * @reg_cc_boost_timer: CC-Boost FSM wait first timer register + * @reg_dcvs_boost_timer: DCVS-Boost FSM wait first timer register + * @reg_ps_boost_timer: PS-Boost FSM wait first timer register + * @boost_timer_reg_len: Length of boost timer registers + * @reg_boost_sync_delay: PLL signal timing control for Boost + * @reg_droop_ctrl: Droop control value + * @reg_droop_release_ctrl: Wait for Droop release + * @reg_droop_unstall_ctrl: Wait for Droop unstall + * @reg_droop_wait_release_ctrl: Time to wait for state release + * @reg_droop_timer_ctrl: Droop timer + * @reg_droop_sync_delay: PLL signal timing control for Droop + * @reg_pll_override: PLL Droop Override en/disable control + * @reg_cycle_counter: OSM CPU cycle counter + * + * This structure holds the register offsets that are used to set-up + * the Operating State Manager (OSM) parameters, when it is not (or + * not entirely) configured from the bootloader and TrustZone. + * + * Acronyms used in this documentation: + * CC = Core Count + * PS = Power-Save + * VC = Virtual Corner + * LLM = Limits Load Management + * DCVS = Dynamic Clock and Voltage Scaling + */ +struct qcom_cpufreq_soc_setup_data { + /* OSM phys register offsets */ + u16 reg_osm_sequencer; + + /* Frequency domain register offsets */ + u16 reg_override; + u16 reg_spare; + u16 reg_cc_zero_behav; + u16 reg_spm_cc_hyst; + u16 reg_spm_cc_dcvs_dis; + u16 reg_spm_core_ret_map; + u16 reg_llm_freq_vote_hyst; + u16 reg_llm_volt_vote_hyst; + u16 reg_llm_intf_dcvs_dis; + u16 reg_seq1; + u16 reg_pdn_fsm_ctrl; + u16 reg_cc_boost_timer; + u16 reg_dcvs_boost_timer; + u16 reg_ps_boost_timer; + u16 boost_timer_reg_len; + u16 reg_boost_sync_delay; + u16 reg_droop_ctrl; + u16 reg_droop_release_ctrl; + u16 reg_droop_unstall_ctrl; + u16 reg_droop_wait_release_ctrl; + u16 reg_droop_timer_ctrl; + u16 reg_droop_sync_delay; + u16 reg_pll_override; + u16 reg_cycle_counter; +}; + +/** + * struct qcom_cpufreq_soc_acd_data - Adaptive Clock Distribution data + * + * @tl_delay_reg: Tunable-Length Delay (TLD) register offset + * @acd_ctrl_reg: Control Register (CR) register offset + * @softstart_reg: Soft Start Control Register (SSCR) register offset + * @ext_intf_reg: External interface configuration register offset + * @auto_xfer_reg: Auto Register-Transfer register offset + * @auto_xfer_cfg_reg: Auto Register-Transfer Configuration reg offset + * @auto_xfer_ctl_reg: Auto Register-Transfer Control register offset + * @auto_xfer_sts_reg: Auto Register-Transfer Status register offset + * @dcvs_sw_reg: Software DCVS register offset + * @gfmux_cfg_reg: Glitch-Free MUX configuration register offset + * @write_ctl_reg: Write Control register + * @write_sts_reg: Write Status register + * @tl_delay_val: Tunable-Length Delay (TLD) value + * @acd_ctrl_val: Control Register (CR) value + * @softstart_val: Soft Start Control Register (SSCR) value + * @ext_intf0_val: Initial external interface configuration value + * @ext_intf1_val: Final external interface configuration value + * @auto_xfer_val: Auto-register Transfer Control value + * + * This structure holds the register offsets (from the ACD iospace base) + * and the parameters that are required to configure the OSM to + * initialize the Adaptive Clock Distribution (ACD) system. + */ +struct qcom_cpufreq_soc_acd_data { + u8 tl_delay_reg; + u8 acd_ctrl_reg; + u8 softstart_reg; + u8 ext_intf_reg; + u8 auto_xfer_reg; + u8 auto_xfer_cfg_reg; + u8 auto_xfer_ctl_reg; + u8 auto_xfer_sts_reg; + u8 dcvs_sw_reg; + u8 gfmux_cfg_reg; + u8 write_ctl_reg; + u8 write_sts_reg; + u32 tl_delay_val; + u32 acd_ctrl_val; + u32 softstart_val; + u32 ext_intf0_val; + u32 ext_intf1_val; + u32 auto_xfer_val; +}; + +/** + * struct qcom_cpufreq_hw_params - Operating State Manager (OSM) Parameters + * + * @volt_lut_val: Value composed of: virtual corner (vc) and voltage in mV. + * @freq_lut_val: Value composed of: core count, clock source and output + * frequency in MHz. + * @override_val: PLL parameters that the OSM uses to override the previous + * setting coming from the bootloader, or when uninitialized. + * @spare_val: Spare register, used by both this driver and the OSM HW + * to identify MEM-ACC levels in relation to virtual corners. + * + * This structure holds the parameters to write to the OSM registers for + * one "Virtual Corner" (VC), or one Performance State (p-state). + */ +struct qcom_cpufreq_hw_params { + u32 volt_lut_val; + u32 freq_lut_val; + u32 override_val; + u32 spare_val; +}; #define GT_IRQ_STATUS BIT(2) +/** + * struct qcom_cpufreq_soc_data - SoC specific register offsets of the OSM + * + * @reg_enable: OSM enable status + * @reg_index: Index of the Virtual Corner + * @reg_freq_lut: Frequency Lookup Table + * @reg_freq_lut_src_mask: Frequency Lookup Table clock-source mask + * @reg_volt_lut: Voltage Lookup Table + * @reg_perf_state: Performance State request register + * @lut_row_size: Lookup Table row size + * @clk_hw_div: Divider for "alternate" OSM clock-source + * @uses_tz: OSM already set-up and protected by TrustZone + * @setup_regs: Register offsets for OSM setup + */ struct qcom_cpufreq_soc_data { u32 reg_enable; u32 reg_domain_state; u32 reg_dcvs_ctrl; + u32 reg_index; u32 reg_freq_lut; + u32 reg_freq_lut_src_mask; u32 reg_volt_lut; u32 reg_intr_clr; u32 reg_current_vote; u32 reg_perf_state; u8 lut_row_size; + u8 clk_hw_div; + bool uses_tz; + const struct qcom_cpufreq_soc_setup_data setup_regs; + const struct qcom_cpufreq_soc_acd_data acd_data; }; struct qcom_cpufreq_data { @@ -61,6 +284,7 @@ struct qcom_cpufreq_data { struct freq_qos_request throttle_freq_req; }; +static const char *cprh_genpd_names[] = { "cprh", NULL }; static unsigned long cpu_hw_rate, xo_rate; static bool icc_scaling_enabled; @@ -201,6 +425,574 @@ static unsigned int qcom_cpufreq_hw_fast_switch(struct cpufreq_policy *policy, return policy->freq_table[index].frequency; } +/** + * qcom_cpufreq_hw_boost_setup() - Sets up OSM boost timer registers + * @timer0_addr: Start of boost timer0 register group + * @len: Length (size) of "sub" registers in timer0 group + */ +static void qcom_cpufreq_hw_boost_setup(void __iomem *timer0_addr, u32 len) +{ + u32 val; + + /* timer_reg0 */ + val = FIELD_PREP(BOOST_TIMER_REG_LO, PLL_WAIT_LOCK_TIME_NS); + val |= FIELD_PREP(BOOST_TIMER_REG_HI, SAFE_FREQ_WAIT_NS); + writel(val, timer0_addr); + + /* timer_reg1 */ + val = FIELD_PREP(BOOST_TIMER_REG_LO, PLL_WAIT_LOCK_TIME_NS); + val |= FIELD_PREP(BOOST_TIMER_REG_HI, PLL_WAIT_LOCK_TIME_NS); + writel(val, timer0_addr + len); + + /* timer_reg2 */ + val = FIELD_PREP(BOOST_TIMER_REG_LO, DEXT_DECREMENT_WAIT_NS); + writel(val, timer0_addr + (2 * len)); +} + +/** + * qcom_cpufreq_gen_params() - Generate parameters to send to the hardware + * @cpu_dev: CPU device + * @data: SoC specific register offsets + * @hw_tbl: Pointer to return the array of parameters + * @apm_vc: APM Virtual Corner crossover number, returned to the caller + * @acc_vc: MEMACC Virtual Corner crossover number, returned to the caller + * @cpu_count: Number of CPUs in the frequency domain + * @num_entries: Number of allocated (and filled) elements in the table, + * returned to the caller + * + * This function allocates a 'qcom_cpufreq_hw_params' parameters table, + * fills it and returns it to the consumer, ready to get sent to the HW. + * Since the APM threshold is just one + * Freeing the table after usage is left to the caller. + * + * Returns: Zero for success, otherwise negative value on errors. + */ +static int qcom_cpufreq_gen_params(struct device *cpu_dev, + struct qcom_cpufreq_data *data, + struct qcom_cpufreq_hw_params **hw_tbl, + int *apm_vc, int *acc_vc, int cpu_count, + u8 *num_entries) +{ + struct device **genpd_cpr_vdev; + struct platform_device *pdev = cpufreq_get_driver_data(); + const struct qcom_cpufreq_soc_data *soc_data = data->soc_data; + struct cpr_ext_data *cpr_data; + struct dev_pm_opp *genpd_opp; + unsigned long rate; + int apm_uV, acc_uV, i, gpd_opp_cnt, ret = 0; + + ret = devm_pm_opp_attach_genpd(cpu_dev, cprh_genpd_names, &genpd_cpr_vdev); + if (ret) { + dev_err(&pdev->dev, "Could not attach to pm_domain: %d\n", ret); + return ret; + } + + if (IS_ERR_OR_NULL(*genpd_cpr_vdev)) + return -EINVAL; + + /* + * In the CPR3 driver we have assigned data to the genpd newly created + * virtual device: this contains MEMACC and APM thresholds, as passing + * them through OPPs would be an API abuse. + */ + cpr_data = dev_get_drvdata(*genpd_cpr_vdev); + if (cpr_data == NULL) { + dev_err(&pdev->dev, "Cannot get CPR data\n"); + return -ENODATA; + } + + /* Get the count of available OPPs coming from the power domain */ + gpd_opp_cnt = dev_pm_opp_get_opp_count(cpu_dev); + if (gpd_opp_cnt < 2) { + ret = gpd_opp_cnt > 0 ? -EINVAL : gpd_opp_cnt; + goto detach_gpd; + } + + /* If we get no APM voltage, the system is going to be unstable */ + apm_uV = cpr_data->apm_threshold_uV; + if (apm_uV <= 0) { + ret = -EINVAL; + goto detach_gpd; + } + + /* + * Set apm_vc to a less than zero value: this is used later in the + * logic making sure that we're returning the right virtual corner + * for APM switch. + */ + *apm_vc = -1; + + /* + * Get the ACC threshold voltage: this is optional and not every + * SoC, or every SoC version, or every binning, needs it. + */ + if (cpr_data->mem_acc_threshold_uV <= 0) { + acc_uV = INT_MAX; + *acc_vc = U8_MAX; + } else { + acc_uV = cpr_data->mem_acc_threshold_uV; + *acc_vc = -1; + } + + *hw_tbl = devm_kmalloc_array(&pdev->dev, gpd_opp_cnt, + sizeof(**hw_tbl), GFP_KERNEL); + if (!hw_tbl) { + ret = -ENOMEM; + goto detach_gpd; + } + + for (i = 0, rate = 1000; i <= gpd_opp_cnt ; rate++, i++) { + struct qcom_cpufreq_hw_params *entry = *hw_tbl + i; + struct device_node *np; + u32 pll_div, millivolts, f_src; + + /* + * Find the next enabled OPP's frequency (ignores APM/ACC). + * + * We expect to get an error when we try to go past the last + * defined frequency, so we quit the loop gracefully without + * signaling any error, as this is the expected behavior. + */ + genpd_opp = dev_pm_opp_find_freq_ceil(cpu_dev, &rate); + if (IS_ERR(genpd_opp)) + break; + + /* Get mandatory and optional properties from the OPP DT */ + np = dev_pm_opp_get_of_node(genpd_opp); + if (!np) { + ret = -ENOENT; + goto detach_gpd; + } + + if (of_property_read_u32(np, "qcom,pll-override", + &entry->override_val)) { + ret = -EINVAL; + of_node_put(np); + goto detach_gpd; + } + + if (of_property_read_u32(np, "qcom,spare-data", + &entry->spare_val)) + entry->spare_val = 0; + + if (of_property_read_u32(np, "qcom,pll-div", &pll_div)) + pll_div = 0; + + of_node_put(np); + + /* Get voltage in microvolts, then convert to millivolts */ + millivolts = dev_pm_opp_get_voltage(genpd_opp); + if (millivolts >= apm_uV && *apm_vc < 0) + *apm_vc = i; + if (millivolts >= acc_uV && *acc_vc < 0) + *acc_vc = i; + + millivolts /= 1000; + + if (millivolts < 150 || millivolts > 1400) { + dev_err(&pdev->dev, + "Read invalid voltage: %u.\n", millivolts); + return -EINVAL; + } + + /* In the OSM firmware, "Virtual Corner" levels start from 0 */ + entry->volt_lut_val = FIELD_PREP(LUT_VOLT_VC, i); + entry->volt_lut_val |= FIELD_PREP(LUT_VOLT, millivolts); + + /* + * Only the first frequency has alternate source, as it is + * always that one that is used for low power idle states. + */ + f_src = i ? 1 : 0; + f_src <<= ffs(soc_data->reg_freq_lut_src_mask) - 1; + entry->freq_lut_val = f_src | div_u64(rate, xo_rate); + entry->freq_lut_val |= FIELD_PREP(LUT_CORE_COUNT, cpu_count); + + /* + * PLL divider is not always 0 and there is no way to determine + * it automatically, as setting this value higher than DIV1 + * will make the OSM HW to effectively set the PLL at 2-4x + * the CPU frequency and then divide the CPU clock by this div, + * so this value is effectively used as both a multiplier and + * divider. + * This value cannot be calculated because it depends on + * manual calibration and is (most probably) used to choose + * a PLL frequency that gives the least possible jitter. + */ + entry->freq_lut_val |= FIELD_PREP(LUT_PLL_DIV, pll_div); + + dev_dbg(&pdev->dev, + "[%d] freq=0x%x volt=0x%x override=0x%x spare=0x%x\n", + i, entry->freq_lut_val, entry->volt_lut_val, + entry->override_val, entry->spare_val); + dev_pm_opp_put(genpd_opp); + genpd_opp = NULL; + } + + /* + * If we've got a customized mem-acc corner but we couldn't + * find any suitable crossover, or the corner is less than + * the minimum amount of required corners for mem-acc scaling, + * the values are not valid, hence fall back to LUT values. + */ + if (acc_uV != INT_MAX && *acc_vc < SEQ_MEM_ACC_MAX_LEVELS - 1) { + dev_dbg(&pdev->dev, + "MEM-ACC corner: invalid values VC%d %duV\n", + *acc_vc, acc_uV); + *acc_vc = U8_MAX; + } + + /* + * If we have probed less params than what we need, then the + * OPP table that we got from the genpd is malformed for some + * reason: in this case, do not apply the table to the HW. + */ + if (i < gpd_opp_cnt) { + dev_err(&pdev->dev, "Got bad OPP table from power domain.\n"); + ret = -EINVAL; + goto detach_gpd; + } + *num_entries = i; + +detach_gpd: + return ret; +} + +static inline u32 qcom_cpufreq_acd_regbit(u8 acd_reg_offset) +{ + return BIT(acd_reg_offset / 4); +} + +static int qcom_cpufreq_hw_acd_write_autoxfer(struct qcom_cpufreq_data *data, + void __iomem *acd_base, u32 val) +{ + const struct qcom_cpufreq_soc_data *sdata = data->soc_data; + const struct qcom_cpufreq_soc_acd_data *aregs = &sdata->acd_data; + u32 regval = 0; + + writel(val, acd_base + aregs->auto_xfer_cfg_reg); + + /* (Clear, then) Set AUTOXFER START */ + writel(0, acd_base + aregs->auto_xfer_reg); + writel(1, acd_base + aregs->auto_xfer_reg); + + /* Poll for status: if the first bit is set the transfer is done. */ + return readl_poll_timeout(acd_base + aregs->auto_xfer_sts_reg, regval, + regval & BIT(0), 1, 3); +} + +static int qcom_cpufreq_hw_acd_write_xfer(struct qcom_cpufreq_data *data, + void __iomem *acd_base, u8 reg, + u32 val) +{ + const struct qcom_cpufreq_soc_data *sdata = data->soc_data; + const struct qcom_cpufreq_soc_acd_data *aregs = &sdata->acd_data; + u32 regval = 0; + + /* Write to the register, then initiate manual transfer */ + writel(val, acd_base + reg); + + /* Clear write control register */ + writel(0, acd_base + aregs->write_ctl_reg); + + regval = (reg / 4) << ACD_WRITE_CTL_SELECT_SHIFT; + regval |= ACD_WRITE_CTL_UPDATE_EN; + writel(regval, acd_base + aregs->write_ctl_reg); + + /* Wait until ACD Local Transfer is done */ + return readl_poll_timeout(acd_base + aregs->write_sts_reg, regval, + regval & qcom_cpufreq_acd_regbit(reg), 1, 3); +} + +/** + * qcom_cpufreq_hw_acd_init() - Initialize ACD params in the OSM + * @cpu_dev: CPU device + * @policy: CPUFreq policy structure + * @index: Instance number (CPU cluster number) + * + * On some SoCs it is required to send the ACD configuration parameters + * to the OSM. This function takes the parameters from the SoC specific + * configuration and writes them only if a "osm-acdN" iospace has been + * declared (hence, it's present). + * + * Returns: Zero for success, otherwise negative number on error. + */ +static int qcom_cpufreq_hw_acd_init(struct device *cpu_dev, + struct cpufreq_policy *policy, + int index) +{ + struct platform_device *pdev = cpufreq_get_driver_data(); + struct qcom_cpufreq_data *ddata = policy->driver_data; + const struct qcom_cpufreq_soc_data *sdata = ddata->soc_data; + const struct qcom_cpufreq_soc_acd_data *aregs = &sdata->acd_data; + char acd_resname[] = "osm-acdX"; + void __iomem *acd_base; + u32 rmask; + int ret; + + snprintf(acd_resname, sizeof(acd_resname), "osm-acd%d", index); + + acd_base = devm_platform_ioremap_resource_byname(pdev, acd_resname); + if (IS_ERR(acd_base)) { + dev_vdbg(cpu_dev, "Skipping ACD initialization.\n"); + return 0; + } + + writel(aregs->tl_delay_val, acd_base + aregs->tl_delay_reg); + writel(aregs->acd_ctrl_val, acd_base + aregs->acd_ctrl_reg); + writel(aregs->softstart_val, acd_base + aregs->softstart_reg); + writel(aregs->ext_intf0_val, acd_base + aregs->ext_intf_reg); + writel(aregs->auto_xfer_val, acd_base + aregs->auto_xfer_ctl_reg); + + rmask = qcom_cpufreq_acd_regbit(aregs->acd_ctrl_reg) | + qcom_cpufreq_acd_regbit(aregs->tl_delay_reg) | + qcom_cpufreq_acd_regbit(aregs->softstart_reg) | + qcom_cpufreq_acd_regbit(aregs->ext_intf_reg); + ret = qcom_cpufreq_hw_acd_write_autoxfer(ddata, acd_base, rmask); + if (ret) + return ret; + + /* Switch CPUSS clock source to ACD clock */ + ret = qcom_cpufreq_hw_acd_write_xfer(ddata, acd_base, + aregs->gfmux_cfg_reg, 1); + if (ret) + return ret; + + /* (Set, then) Clear DCVS_SW */ + ret = qcom_cpufreq_hw_acd_write_xfer(ddata, acd_base, + aregs->dcvs_sw_reg, 1); + if (ret) + return ret; + ret = qcom_cpufreq_hw_acd_write_xfer(ddata, acd_base, + aregs->dcvs_sw_reg, 0); + if (ret) + return ret; + + /* Wait for clock switch time */ + udelay(1); + + /* Program the final ACD external interface */ + ret = qcom_cpufreq_hw_acd_write_xfer(ddata, acd_base, + aregs->ext_intf_reg, + aregs->ext_intf1_val); + if (ret) + return ret; + + /* Initiate transfer of the final ACD value */ + rmask |= qcom_cpufreq_acd_regbit(aregs->gfmux_cfg_reg); + writel(rmask, acd_base + aregs->auto_xfer_cfg_reg); + + /* Wait for ACD to stabilize. Same wait as the OSM boot time... */ + udelay(OSM_BOOT_TIME_US); + return 0; +} + +/** + * qcom_cpufreq_hw_write_lut() - Write Lookup Table params to the OSM + * @cpu_dev: CPU device + * @policy: CPUFreq policy structure + * @cpu_count: Number of CPUs in the frequency domain + * @index: Instance number (CPU cluster number) + * + * Program all the Lookup Table (LUT) entries and related thresholds + * to the Operating State Manager on platforms where the same hasn't + * been done already by the bootloader or TrustZone before booting + * the operating system's kernel; + * On these platforms, write access to the OSM is (obviously) not + * blocked by the hypervisor. + * + * Returns: Zero for success, otherwise negative number on error. + */ +static int qcom_cpufreq_hw_write_lut(struct device *cpu_dev, + struct cpufreq_policy *policy, + int cpu_count, int index) +{ + struct platform_device *pdev = cpufreq_get_driver_data(); + struct qcom_cpufreq_data *ddata = policy->driver_data; + const struct qcom_cpufreq_soc_data *sdata = ddata->soc_data; + const struct qcom_cpufreq_soc_setup_data *sregs = &sdata->setup_regs; + struct qcom_cpufreq_hw_params *hw_tbl; + struct resource *osm_rsrc; + char osm_resname[] = "osm-domainX"; + u32 sreg, seq_addr, acc_lval = 0, last_spare = 1; + u8 num_entries = 0; + int apm_vc = INT_MAX, acc_vc = U8_MAX, acc_idx = 0; + int acc_val[SEQ_MEM_ACC_MAX_LEVELS], i, ret; + + snprintf(osm_resname, sizeof(osm_resname), "osm-domain%d", index); + + /* + * On some SoCs the OSM is not getting programmed from bootloader + * and needs to be done here: in this case, we need to retrieve + * the base physical address for the "Sequencer", so we will get + * the OSM base phys and apply the sequencer offset. + * + * Note: We are not remapping this iospace because we are really + * sending the physical address through SCM calls later. + */ + osm_rsrc = platform_get_resource_byname(pdev, IORESOURCE_MEM, osm_resname); + if (!osm_rsrc) + return -ENODEV; + + seq_addr = osm_rsrc->start + sregs->reg_osm_sequencer; + + ret = qcom_cpufreq_gen_params(cpu_dev, ddata, &hw_tbl, &apm_vc, + &acc_vc, cpu_count, &num_entries); + if (ret) + return ret; + + /* If we get less than 2 entries, scaling doesn't make sense */ + if (num_entries < 2) { + dev_err(&pdev->dev, "Not enough LUT entries found (%u)\n", num_entries); + return -EINVAL; + } + + for (i = 0; i < LUT_MAX_ENTRIES; i++) { + struct qcom_cpufreq_hw_params *entry; + int pos = i * sdata->lut_row_size; + + /* + * If we have reached the end of the params table, write + * the last valid entry until the end of the OSM table. + */ + if (i < num_entries) + entry = &hw_tbl[i]; + else + entry = &hw_tbl[num_entries - 1]; + + writel(i, ddata->base + sdata->reg_index + pos); + writel(entry->volt_lut_val, ddata->base + sdata->reg_volt_lut + pos); + writel(entry->freq_lut_val, ddata->base + sdata->reg_freq_lut + pos); + writel(entry->override_val, ddata->base + sregs->reg_override + pos); + writel(entry->spare_val, ddata->base + sregs->reg_spare + pos); + + dev_dbg(cpu_dev, "Writing [%d] v:0x%x f:0x%x ovr:0x%x s:0x%x\n", i, + entry->volt_lut_val, entry->freq_lut_val, + entry->override_val, entry->spare_val); + + /* + * MEM-ACC Virtual Corner threshold voltage: this gets set + * as the pairs of corners in which there is a transition + * between one MEM-ACC level and the next one. + * + * Notes: The spare_val can never be zero; + * The first spare_val is always 1; + * The maximum number of pairs is two (four registers). + * + * Example: (C = Corner Level - M = MEM-ACC Level) + * C0 M1 - C1 M1 - C2 M2 - C3 M2 - C4 M2 - C5 M3 + * Pairs: 1-2, 4-5 + */ + if (entry->spare_val <= last_spare || + acc_idx >= SEQ_MEM_ACC_MAX_LEVELS - 1) + continue; + + /* Standard mem-acc pairs using spare_val LUT crossovers */ + last_spare = entry->spare_val; + acc_val[acc_idx] = i - 1; + acc_idx++; + acc_val[acc_idx] = i; + acc_idx++; + } + + /* Sanity check: we *must* have two mem-acc crossovers (four values) */ + if (acc_idx < SEQ_MEM_ACC_MAX_LEVELS - 1) + return -EINVAL; + + /* + * Customized mem-acc corners, if any; in this case, the last corner + * in the external (CPRh) LUT is this one, placed after the APM one. + */ + if (acc_vc > 0 && acc_vc != U8_MAX) { + sreg = SEQUENCER_REG(seq_addr, SEQ_MEM_ACC_CROSSOVER_VC); + ret = qcom_scm_io_writel(sreg, num_entries + 1); + if (ret) + return ret; + + /* + * At the price of very-slightly higher power consumption, + * switch the ACC at one corner lower than what we've found, + * as this seems to be needed on at least some MSM8998 chips + * to achieve full system stability + */ + acc_vc--; + + /* Change only if we have to move the corner down */ + if (acc_vc < acc_val[3]) { + acc_val[2] = acc_vc - 1; + acc_val[3] = acc_vc; + } + + /* If needed, sanitize previously stored vals from the LUT */ + if (acc_val[2] <= acc_val[1]) + acc_val[1] = acc_val[2] - 1; + if (acc_val[1] <= acc_val[0]) + acc_val[0] = acc_val[1] - 1; + } + + for (i = 0; i < SEQ_MEM_ACC_MAX_LEVELS; i++) { + ret = qcom_scm_io_writel(SEQ_MEMACC_REG(seq_addr, i), acc_val[i]); + if (ret) + return ret; + } + dev_dbg(cpu_dev, "Wrote MEM-ACC Pairs: [%u-%u] [%u-%u]\n", + acc_val[0], acc_val[1], acc_val[2], acc_val[3]); + + /* + * Program the L_VAL of the first corner requesting MEM-ACC + * voltage level 3 to the right sequencer register + */ + acc_lval = FIELD_GET(LUT_L_VAL, hw_tbl[acc_val[3]].freq_lut_val); + ret = qcom_scm_io_writel(SEQUENCER_REG(seq_addr, SEQ_MEM_ACC_LVAL), acc_lval); + if (ret) { + dev_dbg(cpu_dev, "Cannot send memacc l_val\n"); + return ret; + } + dev_dbg(cpu_dev, "MEM-ACC L-Val is %u\n", acc_lval); + + /* + * Array Power Mux threshold level: the first virtual corner + * that requires a switch sequence of the APM from MX to APC. + */ + if (apm_vc == INT_MAX) + apm_vc = LUT_MAX_ENTRIES - 1; + + /* + * APM crossover virtual corner refers to CPRh: there, the APM corner + * is always appended to the table (so, at the end of it, right after + * the cluster dvfs entries). + */ + writel(num_entries, ddata->base + sregs->reg_seq1); + ret = qcom_scm_io_writel(SEQUENCER_REG(seq_addr, SEQ_APM_CROSSOVER_VC), num_entries); + if (ret) + return ret; + + ret = qcom_scm_io_writel(SEQUENCER_REG(seq_addr, SEQ_APM_THRESH_VC), apm_vc); + if (ret) + return ret; + + ret = qcom_scm_io_writel(SEQUENCER_REG(seq_addr, SEQ_APM_THRESH_PREVC), apm_vc - 1); + if (ret) + return ret; + + ret = qcom_scm_io_writel(SEQUENCER_REG(seq_addr, SEQ_APM_PARAM), + (0x39 | apm_vc << 6)); + if (ret) + return ret; + dev_dbg(cpu_dev, "Wrote APM Pair: [%u-%u]\n", apm_vc - 1, apm_vc); + + /* + * We succeeded! Dispose of the table that got allocated during + * qcom_cpufreq_gen_params, as that contains parameters that are + * relevant only to the context of OSM programming, which is done + * only once. + */ + if (hw_tbl) + devm_kfree(&pdev->dev, hw_tbl); + + return 0; +} + /** * qcom_cpufreq_hw_read_lut() - Read Lookup Table from the OSM * @cpu_dev: CPU device @@ -249,14 +1041,16 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, } for (i = 0; i < LUT_MAX_ENTRIES; i++) { - data = readl_relaxed(drv_data->base + soc_data->reg_freq_lut + - i * soc_data->lut_row_size); - src = FIELD_GET(LUT_SRC, data); + data = readl(drv_data->base + soc_data->reg_freq_lut + + i * soc_data->lut_row_size); + src = data & soc_data->reg_freq_lut_src_mask; + src >>= ffs(soc_data->reg_freq_lut_src_mask) - 1; + lval = FIELD_GET(LUT_L_VAL, data); core_count = FIELD_GET(LUT_CORE_COUNT, data); - data = readl_relaxed(drv_data->base + soc_data->reg_volt_lut + - i * soc_data->lut_row_size); + data = readl(drv_data->base + soc_data->reg_volt_lut + + i * soc_data->lut_row_size); volt = FIELD_GET(LUT_VOLT, data) * 1000; if (src) @@ -294,8 +1088,7 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, prev->frequency = prev_freq; prev->flags = CPUFREQ_BOOST_FREQ; } else { - dev_warn(cpu_dev, "failed to update OPP for freq=%d\n", - freq); + dev_warn(cpu_dev, "can't update OPP for freq=%u\n", freq); } } @@ -316,11 +1109,14 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, * qcom_get_related_cpus - Get mask of CPUs in the same frequency domain * @index: CPU number * @m: Returned CPU mask + * + * Returns: Count of CPUs inserted in the cpumask or negative number for error. */ -static void qcom_get_related_cpus(int index, struct cpumask *m) +static int qcom_get_related_cpus(int index, struct cpumask *m) { struct device_node *cpu_np; struct of_phandle_args args; + int count = 0; int cpu, ret; for_each_possible_cpu(cpu) { @@ -329,15 +1125,18 @@ static void qcom_get_related_cpus(int index, struct cpumask *m) continue; ret = of_parse_phandle_with_args(cpu_np, "qcom,freq-domain", - "#freq-domain-cells", 0, - &args); + "#freq-domain-cells", 0, &args); of_node_put(cpu_np); if (ret < 0) continue; - if (index == args.args[0]) + if (index == args.args[0]) { cpumask_set_cpu(cpu, m); + count++; + } } + + return count > 0 ? count : -EINVAL; } static unsigned long qcom_lmh_get_throttle_freq(struct qcom_cpufreq_data *data) @@ -432,10 +1231,75 @@ static const struct qcom_cpufreq_soc_data qcom_soc_data = { .reg_enable = 0x0, .reg_dcvs_ctrl = 0xbc, .reg_freq_lut = 0x110, + .reg_freq_lut_src_mask = LUT_SRC_845, .reg_volt_lut = 0x114, .reg_current_vote = 0x704, .reg_perf_state = 0x920, .lut_row_size = 32, + .clk_hw_div = 2, + .uses_tz = true, +}; + +static const struct qcom_cpufreq_soc_data msm8998_soc_data = { + .reg_enable = 0x4, + .reg_index = 0x150, + .reg_freq_lut = 0x154, + .reg_freq_lut_src_mask = LUT_SRC_8998, + .reg_volt_lut = 0x158, + .reg_perf_state = 0xf10, + .lut_row_size = 32, + .clk_hw_div = 1, + .uses_tz = false, + .setup_regs = { + /* Physical offset for sequencer scm calls */ + .reg_osm_sequencer = 0x300, + + /* Frequency domain offsets */ + .reg_override = 0x15c, + .reg_spare = 0x164, + .reg_cc_zero_behav = 0x0c, + .reg_spm_cc_hyst = 0x1c, + .reg_spm_cc_dcvs_dis = 0x20, + .reg_spm_core_ret_map = 0x24, + .reg_llm_freq_vote_hyst = 0x2c, + .reg_llm_volt_vote_hyst = 0x30, + .reg_llm_intf_dcvs_dis = 0x34, + .reg_seq1 = 0x48, + .reg_pdn_fsm_ctrl = 0x70, + .reg_cc_boost_timer = 0x74, + .reg_dcvs_boost_timer = 0x84, + .reg_ps_boost_timer = 0x94, + .boost_timer_reg_len = 0x4, + .reg_boost_sync_delay = 0xa0, + .reg_droop_ctrl = 0xa4, + .reg_droop_release_ctrl = 0xa8, + .reg_droop_unstall_ctrl = 0xac, + .reg_droop_wait_release_ctrl = 0xb0, + .reg_droop_timer_ctrl = 0xb8, + .reg_droop_sync_delay = 0xbc, + .reg_pll_override = 0xc0, + .reg_cycle_counter = 0xf00, + }, + .acd_data = { + .acd_ctrl_reg = 0x4, + .tl_delay_reg = 0x8, + .softstart_reg = 0x28, + .ext_intf_reg = 0x30, + .dcvs_sw_reg = 0x34, + .gfmux_cfg_reg = 0x3c, + .auto_xfer_cfg_reg = 0x80, + .auto_xfer_reg = 0x84, + .auto_xfer_ctl_reg = 0x88, + .auto_xfer_sts_reg = 0x8c, + .write_ctl_reg = 0x90, + .write_sts_reg = 0x94, + .tl_delay_val = 38417, + .acd_ctrl_val = 0x2b5ffd, + .softstart_val = 0x501, + .ext_intf0_val = 0x2cf9ae8, + .ext_intf1_val = 0x2cf9afe, + .auto_xfer_val = 0x15, + }, }; static const struct qcom_cpufreq_soc_data epss_soc_data = { @@ -443,14 +1307,18 @@ static const struct qcom_cpufreq_soc_data epss_soc_data = { .reg_domain_state = 0x20, .reg_dcvs_ctrl = 0xb0, .reg_freq_lut = 0x100, + .reg_freq_lut_src_mask = LUT_SRC_845, .reg_volt_lut = 0x200, .reg_intr_clr = 0x308, .reg_perf_state = 0x320, .lut_row_size = 4, + .clk_hw_div = 2, + .uses_tz = true, }; static const struct of_device_id qcom_cpufreq_hw_match[] = { { .compatible = "qcom,cpufreq-hw", .data = &qcom_soc_data }, + { .compatible = "qcom,cpufreq-hw-8998", .data = &msm8998_soc_data }, { .compatible = "qcom,cpufreq-epss", .data = &epss_soc_data }, {} }; @@ -550,6 +1418,130 @@ static void qcom_cpufreq_hw_lmh_exit(struct qcom_cpufreq_data *data) free_irq(data->throttle_irq, data); } +/** + * qcom_cpufreq_hw_osm_setup() - Setup and enable the OSM + * @cpu_dev: CPU device + * @policy: CPUFreq policy structure + * @cpu_count: Number of CPUs in the frequency domain + * + * On some platforms, the Operating State Manager (OSM) is not getting + * programmed by the bootloader, nor by TrustZone before booting the OS + * and its register space is not write-protected by the hypervisor. + * In this case, to achieve CPU DVFS, it is needed to program it from + * the OS itself, which includes setting LUT and all the various tunables + * that are required for it to manage the CPU frequencies and voltages + * on its own. + * Calling this function on a platform that had the OSM set-up by TZ + * will result in a hypervisor fault with system reboot in most cases. + * + * Returns: Zero for success, otherwise negative number on errors. + */ +static int qcom_cpufreq_hw_osm_setup(struct device *cpu_dev, + struct cpufreq_policy *policy, + int cpu_count, int index) +{ + struct qcom_cpufreq_data *drv_data = policy->driver_data; + const struct qcom_cpufreq_soc_setup_data *setup_regs; + u32 val; + int ret; + + ret = qcom_cpufreq_hw_write_lut(cpu_dev, policy, cpu_count, index); + if (ret) + return ret; + + setup_regs = &drv_data->soc_data->setup_regs; + + /* Set OSM to XO clock ratio and use XO edge for the cycle counter */ + val = FIELD_PREP(CYCLE_COUNTER_CLK_RATIO, OSM_XO_RATIO_VAL); + val |= CYCLE_COUNTER_USE_XO_EDGE; + + /* Enable the CPU cycle counter */ + val |= BIT(0); + writel(val, drv_data->base + setup_regs->reg_cycle_counter); + + /* CoreCount DCVS Policy: Wait time for frequency inc/decrement */ + val = FIELD_PREP(HYSTERESIS_UP_MASK, HYSTERESIS_CC_NS); + val |= FIELD_PREP(HYSTERESIS_DN_MASK, HYSTERESIS_CC_NS); + writel(val, drv_data->base + setup_regs->reg_spm_cc_hyst); + + /* Set the frequency index 0 and override for cluster power collapse */ + writel(BIT(0), drv_data->base + setup_regs->reg_cc_zero_behav); + + /* Treat cores in retention as active */ + writel(0, drv_data->base + setup_regs->reg_spm_core_ret_map); + + /* Enable CoreCount based DCVS */ + writel(0, drv_data->base + setup_regs->reg_spm_cc_dcvs_dis); + + /* CoreCount DCVS-LLM Policy: Wait time for frequency inc/decrement */ + val = FIELD_PREP(HYSTERESIS_UP_MASK, HYSTERESIS_LLM_NS); + val |= FIELD_PREP(HYSTERESIS_DN_MASK, HYSTERESIS_LLM_NS); + writel(val, drv_data->base + setup_regs->reg_llm_freq_vote_hyst); + + /* CoreCount DCVS-LLM Policy: Wait time for voltage inc/decrement */ + val = FIELD_PREP(HYSTERESIS_UP_MASK, HYSTERESIS_LLM_NS); + val |= FIELD_PREP(HYSTERESIS_DN_MASK, HYSTERESIS_LLM_NS); + writel(val, drv_data->base + setup_regs->reg_llm_volt_vote_hyst); + + /* Enable LLM frequency+voltage voting */ + writel(0, drv_data->base + setup_regs->reg_llm_intf_dcvs_dis); + + /* Setup Boost FSM Timers */ + qcom_cpufreq_hw_boost_setup(drv_data->base + setup_regs->reg_cc_boost_timer, + setup_regs->boost_timer_reg_len); + qcom_cpufreq_hw_boost_setup(drv_data->base + setup_regs->reg_dcvs_boost_timer, + setup_regs->boost_timer_reg_len); + qcom_cpufreq_hw_boost_setup(drv_data->base + setup_regs->reg_ps_boost_timer, + setup_regs->boost_timer_reg_len); + + /* PLL signal timing control for Boost */ + writel(BOOST_SYNC_DELAY, drv_data->base + setup_regs->reg_boost_sync_delay); + + /* Setup WFx and PC/RET droop unstall */ + val = FIELD_PREP(DROOP_TIMER1, DROOP_TIMER_NS); + val |= FIELD_PREP(DROOP_TIMER0, DROOP_TIMER_NS); + writel(val, drv_data->base + setup_regs->reg_droop_unstall_ctrl); + + /* Setup WFx and PC/RET droop wait-to-release */ + val = FIELD_PREP(DROOP_TIMER1, DROOP_WAIT_RELEASE_TIMER_NS); + val |= FIELD_PREP(DROOP_TIMER0, DROOP_WAIT_RELEASE_TIMER_NS); + writel(val, drv_data->base + setup_regs->reg_droop_wait_release_ctrl); + + /* PLL signal timing control for Droop */ + writel(1, drv_data->base + setup_regs->reg_droop_sync_delay); + + /* Setup DCVS timers */ + writel(DROOP_RELEASE_TIMER_NS, + drv_data->base + setup_regs->reg_droop_release_ctrl); + writel(DROOP_TIMER_NS, drv_data->base + setup_regs->reg_droop_timer_ctrl); + + /* Setup Droop control */ + val = readl(drv_data->base + setup_regs->reg_droop_ctrl); + val |= DROOP_CTRL_VAL; + writel(val, drv_data->base + setup_regs->reg_droop_ctrl); + + /* Enable CC-Boost, DCVS-Boost, PS-Boost, WFx, PC/RET, DCVS FSM */ + val = readl(drv_data->base + setup_regs->reg_pdn_fsm_ctrl); + val |= CC_BOOST_EN | PS_BOOST_EN | DCVS_BOOST_EN; + val |= WFX_DROOP_EN | PC_RET_EXIT_DROOP_EN | DCVS_DROOP_EN; + writel(val, drv_data->base + setup_regs->reg_pdn_fsm_ctrl); + + /* Enable PLL Droop Override */ + val = PLL_OVERRIDE_DROOP_EN; + writel(val, drv_data->base + setup_regs->reg_pll_override); + + /* Initialize the Adaptive Clock Distribution */ + ret = qcom_cpufreq_hw_acd_init(cpu_dev, policy, index); + if (ret) + return ret; + + /* We're ready: enable the OSM and give it time to boot (5uS) */ + writel(1, drv_data->base + drv_data->soc_data->reg_enable); + udelay(OSM_BOOT_TIME_US); + + return 0; +} + static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) { struct platform_device *pdev = cpufreq_get_driver_data(); @@ -560,7 +1552,8 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) struct resource *res; void __iomem *base; struct qcom_cpufreq_data *data; - int ret, index; + char fdom_resname[] = "freq-domainX"; + int cpu_count, index, ret; cpu_dev = get_cpu_device(policy->cpu); if (!cpu_dev) { @@ -581,7 +1574,9 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) index = args.args[0]; - res = platform_get_resource(pdev, IORESOURCE_MEM, index); + snprintf(fdom_resname, sizeof(fdom_resname), "freq-domain%d", index); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, fdom_resname); if (!res) { dev_err(dev, "failed to get mem resource %d\n", index); return -ENODEV; @@ -714,9 +1709,50 @@ static struct cpufreq_driver cpufreq_qcom_hw_driver = { static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev) { + const struct qcom_cpufreq_soc_data *soc_data; + struct device_node *pd_node; + struct platform_device *pd_dev; struct device *cpu_dev; struct clk *clk; - int ret; + int clk_div, ret; + + cpu_dev = get_cpu_device(0); + if (!cpu_dev) + return -EPROBE_DEFER; + + soc_data = of_device_get_match_data(&pdev->dev); + if (!soc_data) + return -EINVAL; + + if (!soc_data->uses_tz) { + /* + * When the OSM is not pre-programmed from TZ, we will + * need to program the sequencer through SCM calls. + */ + if (!qcom_scm_is_available()) + return -EPROBE_DEFER; + + /* + * If there are no power-domains, OSM programming cannot be + * performed, as in that case, we wouldn't know where to take + * the params from... + */ + pd_node = of_parse_phandle(cpu_dev->of_node, "power-domains", 0); + if (!pd_node) { + ret = PTR_ERR(pd_node); + dev_err(cpu_dev, "power domain not found: %d\n", ret); + return ret; + } + + /* + * If the power domain device is not registered yet, then + * defer probing this driver until that is available. + */ + pd_dev = of_find_device_by_node(pd_node); + if (!pd_dev || !pd_dev->dev.driver || + !device_is_bound(&pd_dev->dev)) + return -EPROBE_DEFER; + } clk = clk_get(&pdev->dev, "xo"); if (IS_ERR(clk)) @@ -729,16 +1765,16 @@ static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev) if (IS_ERR(clk)) return PTR_ERR(clk); - cpu_hw_rate = clk_get_rate(clk) / CLK_HW_DIV; + clk_div = soc_data->clk_hw_div; + if (clk_div == 0) + clk_div++; + + cpu_hw_rate = clk_get_rate(clk) / clk_div; clk_put(clk); cpufreq_qcom_hw_driver.driver_data = pdev; /* Check for optional interconnect paths on CPU0 */ - cpu_dev = get_cpu_device(0); - if (!cpu_dev) - return -EPROBE_DEFER; - ret = dev_pm_opp_of_find_icc_paths(cpu_dev, NULL); if (ret) return ret; From b1305a65ab81a6e1e4bed5bf1fe4d467a471faf9 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 19 Jan 2021 13:03:11 +0100 Subject: [PATCH 0242/1823] cpufreq: qcom-hw: Allow getting the maximum transition latency for OPPs In order to fine-tune the frequency scaling from various governors, allow to set a maximum transition latency from OPPs, which may be different depending on the SoC. Signed-off-by: AngeloGioacchino Del Regno [Fixup for 5.18 by Jami] --- drivers/cpufreq/qcom-cpufreq-hw.c | 43 +++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index feb6c64e3293..879822c6cf95 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -1553,6 +1553,7 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) void __iomem *base; struct qcom_cpufreq_data *data; char fdom_resname[] = "freq-domainX"; + unsigned int transition_latency; int cpu_count, index, ret; cpu_dev = get_cpu_device(policy->cpu); @@ -1603,6 +1604,28 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) data->soc_data = of_device_get_match_data(&pdev->dev); data->base = base; data->res = res; + policy->driver_data = data; + + if (readl_relaxed(base + data->soc_data->reg_dcvs_ctrl) & 0x1) + data->per_core_dcvs = true; + + cpu_count = qcom_get_related_cpus(index, policy->cpus); + if (cpumask_empty(policy->cpus)) { + dev_err(dev, "Domain-%d failed to get related CPUs\n", index); + ret = -ENOENT; + goto error; + } + + policy->dvfs_possible_from_any_cpu = true; + if (!data->soc_data->uses_tz) { + ret = qcom_cpufreq_hw_osm_setup(cpu_dev, policy, + cpu_count, index); + if (ret) { + dev_err(dev, "Cannot setup the OSM for CPU%d: %d\n", + policy->cpu, ret); + goto error; + } + } /* HW should be in enabled state to proceed */ if (!(readl_relaxed(base + data->soc_data->reg_enable) & 0x1)) { @@ -1611,19 +1634,6 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) goto error; } - if (readl_relaxed(base + data->soc_data->reg_dcvs_ctrl) & 0x1) - data->per_core_dcvs = true; - - qcom_get_related_cpus(index, policy->cpus); - if (cpumask_empty(policy->cpus)) { - dev_err(dev, "Domain-%d failed to get related CPUs\n", index); - ret = -ENOENT; - goto error; - } - - policy->driver_data = data; - policy->dvfs_possible_from_any_cpu = true; - ret = qcom_cpufreq_hw_read_lut(cpu_dev, policy); if (ret) { dev_err(dev, "Domain-%d failed to read LUT\n", index); @@ -1637,6 +1647,12 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) goto error; } + transition_latency = dev_pm_opp_get_max_transition_latency(cpu_dev); + if (!transition_latency) + transition_latency = CPUFREQ_ETERNAL; + + policy->cpuinfo.transition_latency = transition_latency; + if (policy_has_boost_freq(policy)) { ret = cpufreq_enable_boost_support(); if (ret) @@ -1649,6 +1665,7 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) return 0; error: + policy->driver_data = NULL; kfree(data); unmap_base: iounmap(base); From c53501d1551e584a9bb334cc9ddd7b307b91e14a Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Sat, 9 Jan 2021 12:58:27 +0100 Subject: [PATCH 0243/1823] dt-bindings: cpufreq: qcom-hw: Add bindings for 8998 The OSM programming addition has been done under the qcom,cpufreq-hw-8998 compatible name: specify the requirement of two additional register spaces for this functionality. This implementation, with the same compatible, has been tested on MSM8998 and SDM630. Signed-off-by: AngeloGioacchino Del Regno [Fixed up for 5.18 by Jami] --- .../bindings/cpufreq/cpufreq-qcom-hw.yaml | 60 +++++++++++++++---- 1 file changed, 48 insertions(+), 12 deletions(-) diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml b/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml index 24fa3d87a40b..e93904e8d4ef 100644 --- a/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml +++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml @@ -18,6 +18,10 @@ description: | properties: compatible: oneOf: + - description: Non-secure v1 of CPUFREQ HW + items: + - const: qcom,cpufreq-hw-8998 + - description: v1 of CPUFREQ HW items: - const: qcom,cpufreq-hw @@ -29,19 +33,9 @@ properties: - qcom,sm8250-cpufreq-epss - const: qcom,cpufreq-epss - reg: - minItems: 2 - items: - - description: Frequency domain 0 register region - - description: Frequency domain 1 register region - - description: Frequency domain 2 register region + reg: {} - reg-names: - minItems: 2 - items: - - const: freq-domain0 - - const: freq-domain1 - - const: freq-domain2 + reg-names: {} clocks: items: @@ -56,6 +50,48 @@ properties: '#freq-domain-cells': const: 1 +if: + properties: + compatible: + contains: + const: qcom,cpufreq-hw-8998 +then: + properties: + reg: + minItems: 2 + items: + - description: Frequency domain 0 register region + - description: Operating State Manager domain 0 register region + - description: Frequency domain 1 register region + - description: Operating State Manager domain 1 register region + - description: PLL ACD domain 0 register region (if ACD programming required) + - description: PLL ACD domain 1 register region (if ACD programming required) + + reg-names: + minItems: 2 + items: + - const: "osm-domain0" + - const: "freq-domain0" + - const: "osm-domain1" + - const: "freq-domain1" + - const: "osm-acd0" + - const: "osm-acd1" + +else: + properties: + reg: + minItems: 2 + items: + - description: Frequency domain 0 register region + - description: Frequency domain 1 register region + - description: Frequency domain 2 register region + reg-names: + minItems: 2 + items: + - const: "freq-domain0" + - const: "freq-domain1" + - const: "freq-domain2" + required: - compatible - reg From 85a644a8415aa124210ca62104940fea334a16eb Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Thu, 17 Jun 2021 23:05:40 +0200 Subject: [PATCH 0244/1823] dt-bindings: cpufreq: qcom-hw: Make reg-names a required property The property reg-names is required after the addition of the OSM programming sequence, as that mandates specifying different register domains; to avoid confusion and improve devicetree readability, specifying the regions names was made mandatory. --- Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml b/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml index e93904e8d4ef..8589a5c06460 100644 --- a/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml +++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml @@ -95,6 +95,7 @@ else: required: - compatible - reg + - reg-names - clocks - clock-names - '#freq-domain-cells' From b50e0add79cad442ab5dddec6aeee43972a97b4a Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 24 Nov 2020 20:31:05 +0100 Subject: [PATCH 0245/1823] soc: qcom: cpr: Move common functions to new file In preparation for implementing a new driver that will be handling CPRv3, CPRv4 and CPR-Hardened, format out common functions to a new file. Signed-off-by: AngeloGioacchino Del Regno (JAMI: fixup for 5.15 & 5.17) --- drivers/soc/qcom/Makefile | 2 +- drivers/soc/qcom/cpr-common.c | 349 +++++++++++++++++++++++++++++ drivers/soc/qcom/cpr-common.h | 113 ++++++++++ drivers/soc/qcom/cpr.c | 408 +++------------------------------- 4 files changed, 490 insertions(+), 382 deletions(-) create mode 100644 drivers/soc/qcom/cpr-common.c create mode 100644 drivers/soc/qcom/cpr-common.h diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index d66604aff2b0..f04bcabd450a 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -3,7 +3,7 @@ CFLAGS_rpmh-rsc.o := -I$(src) obj-$(CONFIG_QCOM_AOSS_QMP) += qcom_aoss.o obj-$(CONFIG_QCOM_GENI_SE) += qcom-geni-se.o obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o -obj-$(CONFIG_QCOM_CPR) += cpr.o +obj-$(CONFIG_QCOM_CPR) += cpr-common.o cpr.o obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o obj-$(CONFIG_QCOM_OCMEM) += ocmem.o diff --git a/drivers/soc/qcom/cpr-common.c b/drivers/soc/qcom/cpr-common.c new file mode 100644 index 000000000000..540bb074358d --- /dev/null +++ b/drivers/soc/qcom/cpr-common.c @@ -0,0 +1,349 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2019, Linaro Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cpr-common.h" + +int cpr_populate_ring_osc_idx(struct device *dev, + struct fuse_corner *fuse_corner, + const struct cpr_fuse *cpr_fuse, + int num_fuse_corners) +{ + struct fuse_corner *end = fuse_corner + num_fuse_corners; + u32 data; + int ret; + + for (; fuse_corner < end; fuse_corner++, cpr_fuse++) { + ret = nvmem_cell_read_variable_le_u32(dev, cpr_fuse->ring_osc, &data); + if (ret) + return ret; + fuse_corner->ring_osc_idx = data; + } + + return 0; +} + +int cpr_read_fuse_uV(int init_v_width, int step_size_uV, int ref_uV, + int adj, int step_volt, const char *init_v_efuse, + struct device *dev) +{ + int steps, uV; + u32 bits = 0; + int ret; + + ret = nvmem_cell_read_variable_le_u32(dev, init_v_efuse, &bits); + if (ret) + return ret; + + steps = bits & (BIT(init_v_width - 1) - 1); + /* Not two's complement.. instead highest bit is sign bit */ + if (bits & BIT(init_v_width - 1)) + steps = -steps; + + uV = ref_uV + steps * step_size_uV; + + /* Apply open-loop fixed adjustments to fused values */ + uV += adj; + + return DIV_ROUND_UP(uV, step_volt) * step_volt; +} + +const struct cpr_fuse *cpr_get_fuses(struct device *dev, int tid, + int num_fuse_corners) +{ + struct cpr_fuse *fuses; + int i; + + fuses = devm_kcalloc(dev, num_fuse_corners, + sizeof(struct cpr_fuse), + GFP_KERNEL); + if (!fuses) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < num_fuse_corners; i++) { + char tbuf[50]; + + snprintf(tbuf, sizeof(tbuf), "cpr%d_ring_osc%d", tid, i + 1); + fuses[i].ring_osc = devm_kstrdup(dev, tbuf, GFP_KERNEL); + if (!fuses[i].ring_osc) + return ERR_PTR(-ENOMEM); + + snprintf(tbuf, sizeof(tbuf), + "cpr%d_init_voltage%d", tid, i + 1); + fuses[i].init_voltage = devm_kstrdup(dev, tbuf, + GFP_KERNEL); + if (!fuses[i].init_voltage) + return ERR_PTR(-ENOMEM); + + snprintf(tbuf, sizeof(tbuf), "cpr%d_quotient%d", tid, i + 1); + fuses[i].quotient = devm_kstrdup(dev, tbuf, GFP_KERNEL); + if (!fuses[i].quotient) + return ERR_PTR(-ENOMEM); + + snprintf(tbuf, sizeof(tbuf), + "cpr%d_quotient_offset%d", tid, i + 1); + fuses[i].quotient_offset = devm_kstrdup(dev, tbuf, + GFP_KERNEL); + if (!fuses[i].quotient_offset) + return ERR_PTR(-ENOMEM); + } + + return fuses; +} + +int cpr_populate_fuse_common(struct device *dev, + struct fuse_corner_data *fdata, + const struct cpr_fuse *cpr_fuse, + struct fuse_corner *fuse_corner, + int step_volt, int init_v_width, + int init_v_step) +{ + int uV, ret; + + /* Populate uV */ + uV = cpr_read_fuse_uV(init_v_width, init_v_step, + fdata->ref_uV, fdata->volt_oloop_adjust, + step_volt, cpr_fuse->init_voltage, dev); + if (uV < 0) + return uV; + + /* + * Update SoC voltages: platforms might choose a different + * regulators than the one used to characterize the algorithms + * (ie, init_voltage_step). + */ + fdata->min_uV = roundup(fdata->min_uV, step_volt); + fdata->max_uV = roundup(fdata->max_uV, step_volt); + + fuse_corner->min_uV = fdata->min_uV; + fuse_corner->max_uV = fdata->max_uV; + fuse_corner->uV = clamp(uV, fuse_corner->min_uV, fuse_corner->max_uV); + + /* Populate target quotient by scaling */ + ret = nvmem_cell_read_variable_le_u32(dev, cpr_fuse->quotient, &fuse_corner->quot); + if (ret) + return ret; + + fuse_corner->quot *= fdata->quot_scale; + fuse_corner->quot += fdata->quot_offset; + fuse_corner->quot += fdata->quot_adjust; + + return 0; +} + +/* + * Returns: Index of the initial corner or negative number for error. + */ +int cpr_find_initial_corner(struct device *dev, struct clk *cpu_clk, + struct corner *corners, int num_corners) +{ + unsigned long rate; + struct corner *iter, *corner; + const struct corner *end; + unsigned int ret = 0; + + if (!cpu_clk) + return -EINVAL; + + end = &corners[num_corners - 1]; + rate = clk_get_rate(cpu_clk); + + /* + * Some bootloaders set a CPU clock frequency that is not defined + * in the OPP table. When running at an unlisted frequency, + * cpufreq_online() will change to the OPP which has the lowest + * frequency, at or above the unlisted frequency. + * Since cpufreq_online() always "rounds up" in the case of an + * unlisted frequency, this function always "rounds down" in case + * of an unlisted frequency. That way, when cpufreq_online() + * triggers the first ever call to cpr_set_performance_state(), + * it will correctly determine the direction as UP. + */ + for (iter = corners; iter <= end; iter++) { + if (iter->freq > rate) + break; + ret++; + if (iter->freq == rate) { + corner = iter; + break; + } + if (iter->freq < rate) + corner = iter; + } + + if (!corner) { + dev_err(dev, "boot up corner not found\n"); + return -EINVAL; + } + + dev_dbg(dev, "boot up perf state: %u\n", ret); + + return ret; +} + +u32 cpr_get_fuse_corner(struct dev_pm_opp *opp, u32 tid) +{ + struct device_node *np; + u32 fc; + + np = dev_pm_opp_get_of_node(opp); + if (of_property_read_u32_index(np, "qcom,opp-fuse-level", tid, &fc)) { + pr_debug("%s: missing 'qcom,opp-fuse-level' property\n", + __func__); + fc = 0; + } + + of_node_put(np); + + return fc; +} + +unsigned long cpr_get_opp_hz_for_req(struct dev_pm_opp *ref, + struct device *cpu_dev) +{ + u64 rate = 0; + struct device_node *ref_np; + struct device_node *desc_np; + struct device_node *child_np = NULL; + struct device_node *child_req_np = NULL; + + desc_np = dev_pm_opp_of_get_opp_desc_node(cpu_dev); + if (!desc_np) + return 0; + + ref_np = dev_pm_opp_get_of_node(ref); + if (!ref_np) + goto out_ref; + + do { + of_node_put(child_req_np); + child_np = of_get_next_available_child(desc_np, child_np); + child_req_np = of_parse_phandle(child_np, "required-opps", 0); + } while (child_np && child_req_np != ref_np); + + if (child_np && child_req_np == ref_np) + of_property_read_u64(child_np, "opp-hz", &rate); + + of_node_put(child_req_np); + of_node_put(child_np); + of_node_put(ref_np); +out_ref: + of_node_put(desc_np); + + return (unsigned long) rate; +} + +int cpr_calculate_scaling(const char *quot_offset, + struct device *dev, + const struct fuse_corner_data *fdata, + const struct corner *corner) +{ + u32 quot_diff = 0; + unsigned long freq_diff; + int scaling; + const struct fuse_corner *fuse, *prev_fuse; + int ret; + + fuse = corner->fuse_corner; + prev_fuse = fuse - 1; + + if (quot_offset) { + ret = nvmem_cell_read_variable_le_u32(dev, quot_offset, "_diff); + if (ret) + return ret; + + quot_diff *= fdata->quot_offset_scale; + quot_diff += fdata->quot_offset_adjust; + } else { + quot_diff = fuse->quot - prev_fuse->quot; + } + + freq_diff = fuse->max_freq - prev_fuse->max_freq; + freq_diff /= 1000000; /* Convert to MHz */ + scaling = 1000 * quot_diff / freq_diff; + return min(scaling, fdata->max_quot_scale); +} + +int cpr_interpolate(const struct corner *corner, int step_volt, + const struct fuse_corner_data *fdata) +{ + unsigned long f_high, f_low, f_diff; + int uV_high, uV_low, uV; + u64 temp, temp_limit; + const struct fuse_corner *fuse, *prev_fuse; + + fuse = corner->fuse_corner; + prev_fuse = fuse - 1; + + f_high = fuse->max_freq; + f_low = prev_fuse->max_freq; + uV_high = fuse->uV; + uV_low = prev_fuse->uV; + f_diff = fuse->max_freq - corner->freq; + + /* + * Don't interpolate in the wrong direction. This could happen + * if the adjusted fuse voltage overlaps with the previous fuse's + * adjusted voltage. + */ + if (f_high <= f_low || uV_high <= uV_low || f_high <= corner->freq) + return corner->uV; + + temp = f_diff * (uV_high - uV_low); + temp = div64_ul(temp, f_high - f_low); + + /* + * max_volt_scale has units of uV/MHz while freq values + * have units of Hz. Divide by 1000000 to convert to. + */ + temp_limit = f_diff * fdata->max_volt_scale; + do_div(temp_limit, 1000000); + + uV = uV_high - min(temp, temp_limit); + return roundup(uV, step_volt); +} + +int cpr_check_vreg_constraints(struct device *dev, struct regulator *vreg, + struct fuse_corner *f) +{ + int ret; + + ret = regulator_is_supported_voltage(vreg, f->min_uV, f->min_uV); + if (!ret) { + dev_err(dev, "min uV: %d not supported by regulator\n", + f->min_uV); + return -EINVAL; + } + + ret = regulator_is_supported_voltage(vreg, f->max_uV, f->max_uV); + if (!ret) { + dev_err(dev, "max uV: %d not supported by regulator\n", + f->max_uV); + return -EINVAL; + } + + return 0; +} diff --git a/drivers/soc/qcom/cpr-common.h b/drivers/soc/qcom/cpr-common.h new file mode 100644 index 000000000000..83a1f7c941b8 --- /dev/null +++ b/drivers/soc/qcom/cpr-common.h @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#include +#include +#include +#include + +enum voltage_change_dir { + NO_CHANGE, + DOWN, + UP, +}; + +struct fuse_corner_data { + int ref_uV; + int max_uV; + int min_uV; + int range_uV; + /* fuse volt: closed/open loop */ + int volt_cloop_adjust; + int volt_oloop_adjust; + int max_volt_scale; + int max_quot_scale; + /* fuse quot */ + int quot_offset; + int quot_scale; + int quot_adjust; + /* fuse quot_offset */ + int quot_offset_scale; + int quot_offset_adjust; +}; + +struct cpr_fuse { + char *ring_osc; + char *init_voltage; + char *quotient; + char *quotient_offset; +}; + +struct fuse_corner { + int min_uV; + int max_uV; + int uV; + int quot; + int step_quot; + const struct reg_sequence *accs; + int num_accs; + unsigned long max_freq; + u8 ring_osc_idx; +}; + +struct corner { + int min_uV; + int max_uV; + int uV; + int last_uV; + int quot_adjust; + u32 save_ctl; + u32 save_irq; + unsigned long freq; + bool is_open_loop; + struct fuse_corner *fuse_corner; +}; + +struct corner_data { + unsigned int fuse_corner; + unsigned long freq; +}; + +struct acc_desc { + unsigned int enable_reg; + u32 enable_mask; + + struct reg_sequence *config; + struct reg_sequence *settings; + int num_regs_per_fuse; +}; + +struct cpr_acc_desc { + const struct cpr_desc *cpr_desc; + const struct acc_desc *acc_desc; +}; + + +int cpr_read_efuse(struct device *dev, const char *cname, u32 *data); +int cpr_populate_ring_osc_idx(struct device *dev, + struct fuse_corner *fuse_corner, + const struct cpr_fuse *cpr_fuse, + int num_fuse_corners); +int cpr_read_fuse_uV(int init_v_width, int step_size_uV, int ref_uV, + int adj, int step_volt, const char *init_v_efuse, + struct device *dev); +const struct cpr_fuse *cpr_get_fuses(struct device *dev, int tid, + int num_fuse_corners); +int cpr_populate_fuse_common(struct device *dev, + struct fuse_corner_data *fdata, + const struct cpr_fuse *cpr_fuse, + struct fuse_corner *fuse_corner, + int step_volt, int init_v_width, + int init_v_step); +int cpr_find_initial_corner(struct device *dev, struct clk *cpu_clk, + struct corner *corners, int num_corners); +u32 cpr_get_fuse_corner(struct dev_pm_opp *opp, u32 tid); +unsigned long cpr_get_opp_hz_for_req(struct dev_pm_opp *ref, + struct device *cpu_dev); +int cpr_calculate_scaling(const char *quot_offset, + struct device *dev, + const struct fuse_corner_data *fdata, + const struct corner *corner); +int cpr_interpolate(const struct corner *corner, int step_volt, + const struct fuse_corner_data *fdata); +int cpr_check_vreg_constraints(struct device *dev, struct regulator *vreg, + struct fuse_corner *f); diff --git a/drivers/soc/qcom/cpr.c b/drivers/soc/qcom/cpr.c index e9b854ed1bdf..6c9947ac2455 100644 --- a/drivers/soc/qcom/cpr.c +++ b/drivers/soc/qcom/cpr.c @@ -25,6 +25,7 @@ #include #include #include +#include "cpr-common.h" /* Register Offsets for RB-CPR and Bit Definitions */ @@ -124,45 +125,12 @@ #define FUSE_REVISION_UNKNOWN (-1) -enum voltage_change_dir { - NO_CHANGE, - DOWN, - UP, -}; - -struct cpr_fuse { - char *ring_osc; - char *init_voltage; - char *quotient; - char *quotient_offset; -}; - -struct fuse_corner_data { - int ref_uV; - int max_uV; - int min_uV; - int max_volt_scale; - int max_quot_scale; - /* fuse quot */ - int quot_offset; - int quot_scale; - int quot_adjust; - /* fuse quot_offset */ - int quot_offset_scale; - int quot_offset_adjust; -}; - struct cpr_fuses { int init_voltage_step; int init_voltage_width; struct fuse_corner_data *fuse_corner_data; }; -struct corner_data { - unsigned int fuse_corner; - unsigned long freq; -}; - struct cpr_desc { unsigned int num_fuse_corners; int min_diff_quot; @@ -184,44 +152,6 @@ struct cpr_desc { bool reduce_to_corner_uV; }; -struct acc_desc { - unsigned int enable_reg; - u32 enable_mask; - - struct reg_sequence *config; - struct reg_sequence *settings; - int num_regs_per_fuse; -}; - -struct cpr_acc_desc { - const struct cpr_desc *cpr_desc; - const struct acc_desc *acc_desc; -}; - -struct fuse_corner { - int min_uV; - int max_uV; - int uV; - int quot; - int step_quot; - const struct reg_sequence *accs; - int num_accs; - unsigned long max_freq; - u8 ring_osc_idx; -}; - -struct corner { - int min_uV; - int max_uV; - int uV; - int last_uV; - int quot_adjust; - u32 save_ctl; - u32 save_irq; - unsigned long freq; - struct fuse_corner *fuse_corner; -}; - struct cpr_drv { unsigned int num_corners; unsigned int ref_clk_khz; @@ -801,62 +731,16 @@ unlock: return ret; } -static int -cpr_populate_ring_osc_idx(struct cpr_drv *drv) -{ - struct fuse_corner *fuse = drv->fuse_corners; - struct fuse_corner *end = fuse + drv->desc->num_fuse_corners; - const struct cpr_fuse *fuses = drv->cpr_fuses; - u32 data; - int ret; - - for (; fuse < end; fuse++, fuses++) { - ret = nvmem_cell_read_variable_le_u32(drv->dev, fuses->ring_osc, &data); - if (ret) - return ret; - fuse->ring_osc_idx = data; - } - - return 0; -} - -static int cpr_read_fuse_uV(const struct cpr_desc *desc, - const struct fuse_corner_data *fdata, - const char *init_v_efuse, - int step_volt, - struct cpr_drv *drv) -{ - int step_size_uV, steps, uV; - u32 bits = 0; - int ret; - - ret = nvmem_cell_read_variable_le_u32(drv->dev, init_v_efuse, &bits); - if (ret) - return ret; - - steps = bits & ~BIT(desc->cpr_fuses.init_voltage_width - 1); - /* Not two's complement.. instead highest bit is sign bit */ - if (bits & BIT(desc->cpr_fuses.init_voltage_width - 1)) - steps = -steps; - - step_size_uV = desc->cpr_fuses.init_voltage_step; - - uV = fdata->ref_uV + steps * step_size_uV; - return DIV_ROUND_UP(uV, step_volt) * step_volt; -} - static int cpr_fuse_corner_init(struct cpr_drv *drv) { const struct cpr_desc *desc = drv->desc; - const struct cpr_fuse *fuses = drv->cpr_fuses; + const struct cpr_fuse *cpr_fuse = drv->cpr_fuses; const struct acc_desc *acc_desc = drv->acc_desc; - int i; - unsigned int step_volt; struct fuse_corner_data *fdata; struct fuse_corner *fuse, *end; - int uV; const struct reg_sequence *accs; - int ret; + unsigned int step_volt; + int i, ret; accs = acc_desc->settings; @@ -869,24 +753,16 @@ static int cpr_fuse_corner_init(struct cpr_drv *drv) end = &fuse[desc->num_fuse_corners - 1]; fdata = desc->cpr_fuses.fuse_corner_data; - for (i = 0; fuse <= end; fuse++, fuses++, i++, fdata++) { - /* - * Update SoC voltages: platforms might choose a different - * regulators than the one used to characterize the algorithms - * (ie, init_voltage_step). - */ - fdata->min_uV = roundup(fdata->min_uV, step_volt); - fdata->max_uV = roundup(fdata->max_uV, step_volt); + for (i = 0; fuse <= end; fuse++, cpr_fuse++, i++, fdata++) { + ret = cpr_populate_fuse_common( + drv->dev, fdata, cpr_fuse, + fuse, step_volt, + desc->cpr_fuses.init_voltage_width, + desc->cpr_fuses.init_voltage_step); + if (ret) + return ret; - /* Populate uV */ - uV = cpr_read_fuse_uV(desc, fdata, fuses->init_voltage, - step_volt, drv); - if (uV < 0) - return uV; - - fuse->min_uV = fdata->min_uV; - fuse->max_uV = fdata->max_uV; - fuse->uV = clamp(uV, fuse->min_uV, fuse->max_uV); + fuse->step_quot = desc->step_quot[fuse->ring_osc_idx]; if (fuse == end) { /* @@ -898,16 +774,6 @@ static int cpr_fuse_corner_init(struct cpr_drv *drv) end->max_uV = max(end->max_uV, end->uV); } - /* Populate target quotient by scaling */ - ret = nvmem_cell_read_variable_le_u32(drv->dev, fuses->quotient, &fuse->quot); - if (ret) - return ret; - - fuse->quot *= fdata->quot_scale; - fuse->quot += fdata->quot_offset; - fuse->quot += fdata->quot_adjust; - fuse->step_quot = desc->step_quot[fuse->ring_osc_idx]; - /* Populate acc settings */ fuse->accs = accs; fuse->num_accs = acc_desc->num_regs_per_fuse; @@ -924,25 +790,9 @@ static int cpr_fuse_corner_init(struct cpr_drv *drv) else if (fuse->uV < fuse->min_uV) fuse->uV = fuse->min_uV; - ret = regulator_is_supported_voltage(drv->vdd_apc, - fuse->min_uV, - fuse->min_uV); - if (!ret) { - dev_err(drv->dev, - "min uV: %d (fuse corner: %d) not supported by regulator\n", - fuse->min_uV, i); - return -EINVAL; - } - - ret = regulator_is_supported_voltage(drv->vdd_apc, - fuse->max_uV, - fuse->max_uV); - if (!ret) { - dev_err(drv->dev, - "max uV: %d (fuse corner: %d) not supported by regulator\n", - fuse->max_uV, i); - return -EINVAL; - } + ret = cpr_check_vreg_constraints(drv->dev, drv->vdd_apc, fuse); + if (ret) + return ret; dev_dbg(drv->dev, "fuse corner %d: [%d %d %d] RO%hhu quot %d squot %d\n", @@ -953,126 +803,6 @@ static int cpr_fuse_corner_init(struct cpr_drv *drv) return 0; } -static int cpr_calculate_scaling(const char *quot_offset, - struct cpr_drv *drv, - const struct fuse_corner_data *fdata, - const struct corner *corner) -{ - u32 quot_diff = 0; - unsigned long freq_diff; - int scaling; - const struct fuse_corner *fuse, *prev_fuse; - int ret; - - fuse = corner->fuse_corner; - prev_fuse = fuse - 1; - - if (quot_offset) { - ret = nvmem_cell_read_variable_le_u32(drv->dev, quot_offset, "_diff); - if (ret) - return ret; - - quot_diff *= fdata->quot_offset_scale; - quot_diff += fdata->quot_offset_adjust; - } else { - quot_diff = fuse->quot - prev_fuse->quot; - } - - freq_diff = fuse->max_freq - prev_fuse->max_freq; - freq_diff /= 1000000; /* Convert to MHz */ - scaling = 1000 * quot_diff / freq_diff; - return min(scaling, fdata->max_quot_scale); -} - -static int cpr_interpolate(const struct corner *corner, int step_volt, - const struct fuse_corner_data *fdata) -{ - unsigned long f_high, f_low, f_diff; - int uV_high, uV_low, uV; - u64 temp, temp_limit; - const struct fuse_corner *fuse, *prev_fuse; - - fuse = corner->fuse_corner; - prev_fuse = fuse - 1; - - f_high = fuse->max_freq; - f_low = prev_fuse->max_freq; - uV_high = fuse->uV; - uV_low = prev_fuse->uV; - f_diff = fuse->max_freq - corner->freq; - - /* - * Don't interpolate in the wrong direction. This could happen - * if the adjusted fuse voltage overlaps with the previous fuse's - * adjusted voltage. - */ - if (f_high <= f_low || uV_high <= uV_low || f_high <= corner->freq) - return corner->uV; - - temp = f_diff * (uV_high - uV_low); - temp = div64_ul(temp, f_high - f_low); - - /* - * max_volt_scale has units of uV/MHz while freq values - * have units of Hz. Divide by 1000000 to convert to. - */ - temp_limit = f_diff * fdata->max_volt_scale; - do_div(temp_limit, 1000000); - - uV = uV_high - min(temp, temp_limit); - return roundup(uV, step_volt); -} - -static unsigned int cpr_get_fuse_corner(struct dev_pm_opp *opp) -{ - struct device_node *np; - unsigned int fuse_corner = 0; - - np = dev_pm_opp_get_of_node(opp); - if (of_property_read_u32(np, "qcom,opp-fuse-level", &fuse_corner)) - pr_err("%s: missing 'qcom,opp-fuse-level' property\n", - __func__); - - of_node_put(np); - - return fuse_corner; -} - -static unsigned long cpr_get_opp_hz_for_req(struct dev_pm_opp *ref, - struct device *cpu_dev) -{ - u64 rate = 0; - struct device_node *ref_np; - struct device_node *desc_np; - struct device_node *child_np = NULL; - struct device_node *child_req_np = NULL; - - desc_np = dev_pm_opp_of_get_opp_desc_node(cpu_dev); - if (!desc_np) - return 0; - - ref_np = dev_pm_opp_get_of_node(ref); - if (!ref_np) - goto out_ref; - - do { - of_node_put(child_req_np); - child_np = of_get_next_available_child(desc_np, child_np); - child_req_np = of_parse_phandle(child_np, "required-opps", 0); - } while (child_np && child_req_np != ref_np); - - if (child_np && child_req_np == ref_np) - of_property_read_u64(child_np, "opp-hz", &rate); - - of_node_put(child_req_np); - of_node_put(child_np); - of_node_put(ref_np); -out_ref: - of_node_put(desc_np); - - return (unsigned long) rate; -} - static int cpr_corner_init(struct cpr_drv *drv) { const struct cpr_desc *desc = drv->desc; @@ -1110,7 +840,7 @@ static int cpr_corner_init(struct cpr_drv *drv) opp = dev_pm_opp_find_level_exact(&drv->pd.dev, level); if (IS_ERR(opp)) return -EINVAL; - fc = cpr_get_fuse_corner(opp); + fc = cpr_get_fuse_corner(opp, 0); if (!fc) { dev_pm_opp_put(opp); return -EINVAL; @@ -1186,7 +916,7 @@ static int cpr_corner_init(struct cpr_drv *drv) corner->uV = fuse->uV; if (prev_fuse && cdata[i - 1].freq == prev_fuse->max_freq) { - scaling = cpr_calculate_scaling(quot_offset, drv, + scaling = cpr_calculate_scaling(quot_offset, drv->dev, fdata, corner); if (scaling < 0) return scaling; @@ -1224,47 +954,6 @@ static int cpr_corner_init(struct cpr_drv *drv) return 0; } -static const struct cpr_fuse *cpr_get_fuses(struct cpr_drv *drv) -{ - const struct cpr_desc *desc = drv->desc; - struct cpr_fuse *fuses; - int i; - - fuses = devm_kcalloc(drv->dev, desc->num_fuse_corners, - sizeof(struct cpr_fuse), - GFP_KERNEL); - if (!fuses) - return ERR_PTR(-ENOMEM); - - for (i = 0; i < desc->num_fuse_corners; i++) { - char tbuf[32]; - - snprintf(tbuf, 32, "cpr_ring_osc%d", i + 1); - fuses[i].ring_osc = devm_kstrdup(drv->dev, tbuf, GFP_KERNEL); - if (!fuses[i].ring_osc) - return ERR_PTR(-ENOMEM); - - snprintf(tbuf, 32, "cpr_init_voltage%d", i + 1); - fuses[i].init_voltage = devm_kstrdup(drv->dev, tbuf, - GFP_KERNEL); - if (!fuses[i].init_voltage) - return ERR_PTR(-ENOMEM); - - snprintf(tbuf, 32, "cpr_quotient%d", i + 1); - fuses[i].quotient = devm_kstrdup(drv->dev, tbuf, GFP_KERNEL); - if (!fuses[i].quotient) - return ERR_PTR(-ENOMEM); - - snprintf(tbuf, 32, "cpr_quotient_offset%d", i + 1); - fuses[i].quotient_offset = devm_kstrdup(drv->dev, tbuf, - GFP_KERNEL); - if (!fuses[i].quotient_offset) - return ERR_PTR(-ENOMEM); - } - - return fuses; -} - static void cpr_set_loop_allowed(struct cpr_drv *drv) { drv->loop_disabled = false; @@ -1296,54 +985,6 @@ static int cpr_init_parameters(struct cpr_drv *drv) return 0; } -static int cpr_find_initial_corner(struct cpr_drv *drv) -{ - unsigned long rate; - const struct corner *end; - struct corner *iter; - unsigned int i = 0; - - if (!drv->cpu_clk) { - dev_err(drv->dev, "cannot get rate from NULL clk\n"); - return -EINVAL; - } - - end = &drv->corners[drv->num_corners - 1]; - rate = clk_get_rate(drv->cpu_clk); - - /* - * Some bootloaders set a CPU clock frequency that is not defined - * in the OPP table. When running at an unlisted frequency, - * cpufreq_online() will change to the OPP which has the lowest - * frequency, at or above the unlisted frequency. - * Since cpufreq_online() always "rounds up" in the case of an - * unlisted frequency, this function always "rounds down" in case - * of an unlisted frequency. That way, when cpufreq_online() - * triggers the first ever call to cpr_set_performance_state(), - * it will correctly determine the direction as UP. - */ - for (iter = drv->corners; iter <= end; iter++) { - if (iter->freq > rate) - break; - i++; - if (iter->freq == rate) { - drv->corner = iter; - break; - } - if (iter->freq < rate) - drv->corner = iter; - } - - if (!drv->corner) { - dev_err(drv->dev, "boot up corner not found\n"); - return -EINVAL; - } - - dev_dbg(drv->dev, "boot up perf state: %u\n", i); - - return 0; -} - static const struct cpr_desc qcs404_cpr_desc = { .num_fuse_corners = 3, .min_diff_quot = CPR_FUSE_MIN_QUOT_DIFF, @@ -1531,8 +1172,9 @@ static int cpr_pd_attach_dev(struct generic_pm_domain *domain, if (ret) goto unlock; - ret = cpr_find_initial_corner(drv); - if (ret) + ret = cpr_find_initial_corner(drv->dev, drv->cpu_clk, drv->corners, + drv->num_corners); + if (ret < 0) goto unlock; if (acc_desc->config) @@ -1616,6 +1258,7 @@ static int cpr_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct cpr_drv *drv; + const struct cpr_desc *desc; int irq, ret; const struct cpr_acc_desc *data; struct device_node *np; @@ -1631,6 +1274,7 @@ static int cpr_probe(struct platform_device *pdev) drv->dev = dev; drv->desc = data->cpr_desc; drv->acc_desc = data->acc_desc; + desc = drv->desc; drv->fuse_corners = devm_kcalloc(dev, drv->desc->num_fuse_corners, sizeof(*drv->fuse_corners), @@ -1670,11 +1314,13 @@ static int cpr_probe(struct platform_device *pdev) if (ret) return ret; - drv->cpr_fuses = cpr_get_fuses(drv); + drv->cpr_fuses = cpr_get_fuses(drv->dev, 0, desc->num_fuse_corners); if (IS_ERR(drv->cpr_fuses)) return PTR_ERR(drv->cpr_fuses); - ret = cpr_populate_ring_osc_idx(drv); + ret = cpr_populate_ring_osc_idx(drv->dev, drv->fuse_corners, + drv->cpr_fuses, + desc->num_fuse_corners); if (ret) return ret; From b56256fdb4f133d01b7cafe8b6dc594a66cc4778 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 25 Nov 2020 11:23:17 +0100 Subject: [PATCH 0246/1823] arm64: qcom: qcs404: Change CPR nvmem-names The CPR driver's common functions were split and put in another file in order to support newer CPR revisions: to simplify the commonization, the expected names of the fuses had to be changed in order for both new and old support to use the same fuse name retrieval function and keeping the naming consistent. The thread id was added to the fuse name and, since CPRv1 does not support threads, it is expected to always read ID 0, which means that the expected name here is now "cpr0_(fuse_name)" instead of "cpr_(fuse_name)": luckily, QCS404 is the only user so change it accordingly. Signed-off-by: AngeloGioacchino Del Regno --- arch/arm64/boot/dts/qcom/qcs404.dtsi | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/qcs404.dtsi b/arch/arm64/boot/dts/qcom/qcs404.dtsi index 80f2d05595fa..0b793c984fba 100644 --- a/arch/arm64/boot/dts/qcom/qcs404.dtsi +++ b/arch/arm64/boot/dts/qcom/qcs404.dtsi @@ -1185,19 +1185,19 @@ <&cpr_efuse_ring2>, <&cpr_efuse_ring3>, <&cpr_efuse_revision>; - nvmem-cell-names = "cpr_quotient_offset1", - "cpr_quotient_offset2", - "cpr_quotient_offset3", - "cpr_init_voltage1", - "cpr_init_voltage2", - "cpr_init_voltage3", - "cpr_quotient1", - "cpr_quotient2", - "cpr_quotient3", - "cpr_ring_osc1", - "cpr_ring_osc2", - "cpr_ring_osc3", - "cpr_fuse_revision"; + nvmem-cell-names = "cpr0_quotient_offset1", + "cpr0_quotient_offset2", + "cpr0_quotient_offset3", + "cpr0_init_voltage1", + "cpr0_init_voltage2", + "cpr0_init_voltage3", + "cpr0_quotient1", + "cpr0_quotient2", + "cpr0_quotient3", + "cpr0_ring_osc1", + "cpr0_ring_osc2", + "cpr0_ring_osc3", + "cpr0_fuse_revision"; }; timer@b120000 { From 9b789ad11d10a1ef9d8820a76c9e9e91951bcb18 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 24 Nov 2020 20:39:22 +0100 Subject: [PATCH 0247/1823] soc: qcom: Add support for Core Power Reduction v3, v4 and Hardened This commit introduces a new driver, based on the one for cpr v1, to enable support for the newer Qualcomm Core Power Reduction hardware, known downstream as CPR3, CPR4 and CPRh, and support for MSM8998 and SDM630 CPU power reduction. In these new versions of the hardware, support for various new features was introduced, including voltage reduction for the GPU, security hardening and a new way of controlling CPU DVFS, consisting in internal communication between microcontrollers, specifically the CPR-Hardened and the Operating State Manager. The CPR v3, v4 and CPRh are present in a broad range of SoCs, from the mid-range to the high end ones including, but not limited to, MSM8953/8996/8998, SDM630/636/660/845. Signed-off-by: AngeloGioacchino Del Regno --- drivers/soc/qcom/Kconfig | 17 + drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/cpr-common.c | 35 +- drivers/soc/qcom/cpr-common.h | 4 + drivers/soc/qcom/cpr3.c | 2897 +++++++++++++++++++++++++++++++++ include/soc/qcom/cpr.h | 17 + 6 files changed, 2965 insertions(+), 6 deletions(-) create mode 100644 drivers/soc/qcom/cpr3.c create mode 100644 include/soc/qcom/cpr.h diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 024e420f1bb7..da278f796621 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -42,6 +42,23 @@ config QCOM_CPR To compile this driver as a module, choose M here: the module will be called qcom-cpr +config QCOM_CPR3 + tristate "QCOM Core Power Reduction (CPR v3/v4/Hardened) support" + depends on ARCH_QCOM && HAS_IOMEM + select PM_OPP + select REGMAP + help + Say Y here to enable support for the CPR hardware found on a broad + variety of Qualcomm SoCs like MSM8996, MSM8998, SDM630, SDM660, + SDM845 and others. + + This driver populates OPP tables and makes adjustments to them + based on feedback from the CPR hardware. If you want to do CPU + and/or GPU frequency scaling say Y here. + + To compile this driver as a module, choose M here: the module will + be called qcom-cpr3 + config QCOM_GENI_SE tristate "QCOM GENI Serial Engine Driver" depends on ARCH_QCOM || COMPILE_TEST diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index f04bcabd450a..ad7d7731c5b8 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_QCOM_AOSS_QMP) += qcom_aoss.o obj-$(CONFIG_QCOM_GENI_SE) += qcom-geni-se.o obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o obj-$(CONFIG_QCOM_CPR) += cpr-common.o cpr.o +obj-$(CONFIG_QCOM_CPR3) += cpr-common.o cpr3.o obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o obj-$(CONFIG_QCOM_OCMEM) += ocmem.o diff --git a/drivers/soc/qcom/cpr-common.c b/drivers/soc/qcom/cpr-common.c index 540bb074358d..25a1559ac388 100644 --- a/drivers/soc/qcom/cpr-common.c +++ b/drivers/soc/qcom/cpr-common.c @@ -219,6 +219,29 @@ u32 cpr_get_fuse_corner(struct dev_pm_opp *opp, u32 tid) of_node_put(np); return fc; + +} + +void cpr_get_corner_post_vadj(struct dev_pm_opp *opp, u32 tid, + s32 *open_loop, s32 *closed_loop) +{ + struct device_node *np; + + /* + * There is no of_property_read_s32_index, so we just store the + * result into a s32 variable. After all, the OF API is doing + * the exact same for of_property_read_s32... + */ + np = dev_pm_opp_get_of_node(opp); + if (of_property_read_u32_index(np, "qcom,opp-oloop-vadj", tid, + open_loop)) + *open_loop = 0; + + if (of_property_read_u32_index(np, "qcom,opp-cloop-vadj", tid, + closed_loop)) + *closed_loop = 0; + + of_node_put(np); } unsigned long cpr_get_opp_hz_for_req(struct dev_pm_opp *ref, @@ -261,11 +284,10 @@ int cpr_calculate_scaling(const char *quot_offset, const struct fuse_corner_data *fdata, const struct corner *corner) { - u32 quot_diff = 0; - unsigned long freq_diff; - int scaling; + u64 freq_diff; const struct fuse_corner *fuse, *prev_fuse; - int ret; + u32 quot_diff; + int scaling, ret; fuse = corner->fuse_corner; prev_fuse = fuse - 1; @@ -282,8 +304,9 @@ int cpr_calculate_scaling(const char *quot_offset, } freq_diff = fuse->max_freq - prev_fuse->max_freq; - freq_diff /= 1000000; /* Convert to MHz */ - scaling = 1000 * quot_diff / freq_diff; + freq_diff = div_u64(freq_diff, 1000000); /* Convert to MHz */ + scaling = 1000 * quot_diff; + do_div(scaling, freq_diff); return min(scaling, fdata->max_quot_scale); } diff --git a/drivers/soc/qcom/cpr-common.h b/drivers/soc/qcom/cpr-common.h index 83a1f7c941b8..96ff6301c81e 100644 --- a/drivers/soc/qcom/cpr-common.h +++ b/drivers/soc/qcom/cpr-common.h @@ -65,6 +65,8 @@ struct corner { struct corner_data { unsigned int fuse_corner; unsigned long freq; + int oloop_vadj; + int cloop_vadj; }; struct acc_desc { @@ -101,6 +103,8 @@ int cpr_populate_fuse_common(struct device *dev, int cpr_find_initial_corner(struct device *dev, struct clk *cpu_clk, struct corner *corners, int num_corners); u32 cpr_get_fuse_corner(struct dev_pm_opp *opp, u32 tid); +void cpr_get_corner_post_vadj(struct dev_pm_opp *opp, u32 tid, + s32 *open_loop, s32 *closed_loop); unsigned long cpr_get_opp_hz_for_req(struct dev_pm_opp *ref, struct device *cpu_dev); int cpr_calculate_scaling(const char *quot_offset, diff --git a/drivers/soc/qcom/cpr3.c b/drivers/soc/qcom/cpr3.c new file mode 100644 index 000000000000..7e368b39d07c --- /dev/null +++ b/drivers/soc/qcom/cpr3.c @@ -0,0 +1,2897 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2013-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2019 Linaro Limited + * Copyright (c) 2021, AngeloGioacchino Del Regno + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cpr-common.h" + +#define CPR3_RO_COUNT 16 +#define CPR3_RO_MASK GENMASK(CPR3_RO_COUNT - 1, 0) + +/* CPR3 registers */ +#define CPR3_REG_CPR_VERSION 0x0 +#define CPRH_CPR_VERSION_4P5 0x40050000 + +#define CPR3_REG_CPR_CTL 0x4 +#define CPR3_CPR_CTL_LOOP_EN_MASK BIT(0) +#define CPR3_CPR_CTL_IDLE_CLOCKS_MASK GENMASK(5, 1) +#define CPR3_CPR_CTL_IDLE_CLOCKS_SHIFT 1 +#define CPR3_CPR_CTL_COUNT_MODE_MASK GENMASK(7, 6) +#define CPR3_CPR_CTL_COUNT_MODE_SHIFT 6 +#define CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_MIN 0 +#define CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_MAX 1 +#define CPR3_CPR_CTL_COUNT_MODE_STAGGERED 2 +#define CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_AGE 3 +#define CPR3_CPR_CTL_COUNT_REPEAT_MASK GENMASK(31, 9) +#define CPR3_CPR_CTL_COUNT_REPEAT_SHIFT 9 + +#define CPR3_REG_CPR_STATUS 0x8 +#define CPR3_CPR_STATUS_BUSY_MASK BIT(0) + +/* + * This register is not present on controllers that support HW closed-loop + * except CPR4 APSS controller. + */ +#define CPR3_REG_CPR_TIMER_AUTO_CONT 0xC + +#define CPR3_REG_CPR_STEP_QUOT 0x14 +#define CPR3_CPR_STEP_QUOT_MIN_MASK GENMASK(5, 0) +#define CPR3_CPR_STEP_QUOT_MIN_SHIFT 0 +#define CPR3_CPR_STEP_QUOT_MAX_MASK GENMASK(11, 6) +#define CPR3_CPR_STEP_QUOT_MAX_SHIFT 6 +#define CPRH_DELTA_QUOT_STEP_FACTOR 4 + +#define CPR3_REG_GCNT(ro) (0xA0 + 0x4 * (ro)) +#define CPR3_REG_SENSOR_OWNER(sensor) (0x200 + 0x4 * (sensor)) + +#define CPR3_REG_CONT_CMD 0x800 +#define CPR3_CONT_CMD_ACK 0x1 +#define CPR3_CONT_CMD_NACK 0x0 + +#define CPR3_REG_THRESH(thread) (0x808 + 0x440 * (thread)) +#define CPR3_THRESH_CONS_DOWN_MASK GENMASK(3, 0) +#define CPR3_THRESH_CONS_DOWN_SHIFT 0 +#define CPR3_THRESH_CONS_UP_MASK GENMASK(7, 4) +#define CPR3_THRESH_CONS_UP_SHIFT 4 +#define CPR3_THRESH_DOWN_THRESH_MASK GENMASK(12, 8) +#define CPR3_THRESH_DOWN_THRESH_SHIFT 8 +#define CPR3_THRESH_UP_THRESH_MASK GENMASK(17, 13) +#define CPR3_THRESH_UP_THRESH_SHIFT 13 + +#define CPR3_REG_RO_MASK(thread) (0x80C + 0x440 * (thread)) + +#define CPR3_REG_RESULT0(thread) (0x810 + 0x440 * (thread)) +#define CPR3_RESULT0_BUSY_MASK BIT(0) +#define CPR3_RESULT0_STEP_DN_MASK BIT(1) +#define CPR3_RESULT0_STEP_UP_MASK BIT(2) +#define CPR3_RESULT0_ERROR_STEPS_MASK GENMASK(7, 3) +#define CPR3_RESULT0_ERROR_STEPS_SHIFT 3 +#define CPR3_RESULT0_ERROR_MASK GENMASK(19, 8) +#define CPR3_RESULT0_ERROR_SHIFT 8 + +#define CPR3_REG_RESULT1(thread) (0x814 + 0x440 * (thread)) +#define CPR3_RESULT1_QUOT_MIN_MASK GENMASK(11, 0) +#define CPR3_RESULT1_QUOT_MIN_SHIFT 0 +#define CPR3_RESULT1_QUOT_MAX_MASK GENMASK(23, 12) +#define CPR3_RESULT1_QUOT_MAX_SHIFT 12 +#define CPR3_RESULT1_RO_MIN_MASK GENMASK(27, 24) +#define CPR3_RESULT1_RO_MIN_SHIFT 24 +#define CPR3_RESULT1_RO_MAX_MASK GENMASK(31, 28) +#define CPR3_RESULT1_RO_MAX_SHIFT 28 + +#define CPR3_REG_RESULT2(thread) (0x818 + 0x440 * (thread)) +#define CPR3_RESULT2_STEP_QUOT_MIN_MASK GENMASK(5, 0) +#define CPR3_RESULT2_STEP_QUOT_MIN_SHIFT 0 +#define CPR3_RESULT2_STEP_QUOT_MAX_MASK GENMASK(11, 6) +#define CPR3_RESULT2_STEP_QUOT_MAX_SHIFT 6 +#define CPR3_RESULT2_SENSOR_MIN_MASK GENMASK(23, 16) +#define CPR3_RESULT2_SENSOR_MIN_SHIFT 16 +#define CPR3_RESULT2_SENSOR_MAX_MASK GENMASK(31, 24) +#define CPR3_RESULT2_SENSOR_MAX_SHIFT 24 + +#define CPR3_REG_IRQ_EN 0x81C +#define CPR3_REG_IRQ_CLEAR 0x820 +#define CPR3_REG_IRQ_STATUS 0x824 +#define CPR3_IRQ_UP BIT(3) +#define CPR3_IRQ_MID BIT(2) +#define CPR3_IRQ_DOWN BIT(1) +#define CPR3_IRQ_ALL (CPR3_IRQ_UP | CPR3_IRQ_MID | CPR3_IRQ_DOWN) + +#define CPR3_REG_TARGET_QUOT(thread, ro) (0x840 + 0x440 * (thread) + 0x4 * (ro)) + +/* Registers found only on controllers that support HW closed-loop. */ +#define CPR3_REG_PD_THROTTLE 0xE8 + +#define CPR3_REG_HW_CLOSED_LOOP_DISABLED 0x3000 +#define CPR3_REG_CPR_TIMER_MID_CONT 0x3004 +#define CPR3_REG_CPR_TIMER_UP_DN_CONT 0x3008 + +/* CPR4 controller specific registers and bit definitions */ +#define CPR4_REG_CPR_TIMER_CLAMP 0x10 +#define CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN BIT(27) + +#define CPR4_REG_MISC 0x700 +#define CPR4_MISC_RESET_STEP_QUOT_LOOP_EN BIT(2) +#define CPR4_MISC_THREAD_HAS_ALWAYS_VOTE_EN BIT(3) + +#define CPR4_REG_SAW_ERROR_STEP_LIMIT 0x7A4 +#define CPR4_SAW_ERROR_STEP_LIMIT_UP_MASK GENMASK(4, 0) +#define CPR4_SAW_ERROR_STEP_LIMIT_UP_SHIFT 0 +#define CPR4_SAW_ERROR_STEP_LIMIT_DN_MASK GENMASK(9, 5) +#define CPR4_SAW_ERROR_STEP_LIMIT_DN_SHIFT 5 + +#define CPR4_REG_MARGIN_TEMP_CORE_TIMERS 0x7A8 +#define CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_MASK GENMASK(28, 18) +#define CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_SHFT 18 + +#define CPR4_REG_MARGIN_ADJ_CTL 0x7F8 +#define CPR4_MARGIN_ADJ_HW_CLOSED_LOOP_EN BIT(4) +#define CPR4_MARGIN_ADJ_PER_RO_KV_MARGIN_EN BIT(7) +#define CPR4_MARGIN_ADJ_PMIC_STEP_SIZE_MASK GENMASK(16, 12) +#define CPR4_MARGIN_ADJ_PMIC_STEP_SIZE_SHIFT 12 +#define CPR4_MARGIN_ADJ_KV_MARGIN_ADJ_STEP_QUOT_MASK GENMASK(31, 26) +#define CPR4_MARGIN_ADJ_KV_MARGIN_ADJ_STEP_QUOT_SHIFT 26 + +#define CPR4_REG_CPR_MASK_THREAD(thread) (0x80C + 0x440 * (thread)) +#define CPR4_CPR_MASK_THREAD_DISABLE_THREAD BIT(31) +#define CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK GENMASK(15, 0) + +/* CPRh controller specific registers and bit definitions */ +#define __CPRH_REG_CORNER(rbase, tbase, tid, cnum) (rbase + (tbase * tid) + (0x4 * cnum)) +#define CPRH_REG_CORNER(d, t, c) __CPRH_REG_CORNER(d->reg_corner, d->reg_corner_tid, t, c) + +#define CPRH_CTL_OSM_ENABLED BIT(0) +#define CPRH_CTL_BASE_VOLTAGE_MASK GENMASK(10, 1) +#define CPRH_CTL_BASE_VOLTAGE_SHIFT 1 +#define CPRH_CTL_MODE_SWITCH_DELAY_MASK GENMASK(24, 17) +#define CPRH_CTL_MODE_SWITCH_DELAY_SHIFT 17 +#define CPRH_CTL_VOLTAGE_MULTIPLIER_MASK GENMASK(28, 25) +#define CPRH_CTL_VOLTAGE_MULTIPLIER_SHIFT 25 + +#define CPRH_CORNER_INIT_VOLTAGE_MASK GENMASK(7, 0) +#define CPRH_CORNER_INIT_VOLTAGE_SHIFT 0 +#define CPRH_CORNER_FLOOR_VOLTAGE_MASK GENMASK(15, 8) +#define CPRH_CORNER_FLOOR_VOLTAGE_SHIFT 8 +#define CPRH_CORNER_QUOT_DELTA_MASK GENMASK(24, 16) +#define CPRH_CORNER_QUOT_DELTA_SHIFT 16 +#define CPRH_CORNER_RO_SEL_MASK GENMASK(28, 25) +#define CPRH_CORNER_RO_SEL_SHIFT 25 +#define CPRH_CORNER_CPR_CL_DISABLE BIT(29) + +#define CPRH_CORNER_INIT_VOLTAGE_MAX_VALUE 255 +#define CPRH_CORNER_FLOOR_VOLTAGE_MAX_VALUE 255 +#define CPRH_CORNER_QUOT_DELTA_MAX_VALUE 511 + +enum cpr_type { + CTRL_TYPE_CPR3, + CTRL_TYPE_CPR4, + CTRL_TYPE_CPRH, + CTRL_TYPE_MAX, +}; + +/* + * struct cpr_thread_desc - CPR Thread-specific parameters + * + * @controller_id: Identifier of the CPR controller expected by the HW + * @ro_scaling_factor: Scaling factor for each ring oscillator entry + * @hw_tid: Identifier of the CPR thread expected by the HW + * @init_voltage_step: Voltage in uV for number of steps read from fuse array + * @init_voltage_width: Bit-width of the voltage read from the fuse array + * @sensor_range_start: First sensor ID used by a thread + * @sensor_range_end: Last sensor ID used by a thread + * @num_fuse_corners: Number of valid entries in fuse_corner_data + * @step_quot_init_min: Minimum achievable step quotient for this corner + * @step_quot_init_max: Maximum achievable step quotient for this corner + * @fuse_corner_data: Parameters for calculation of each fuse corner + */ +struct cpr_thread_desc { + u8 controller_id; + u8 hw_tid; + const int (*ro_scaling_factor)[CPR3_RO_COUNT]; + int ro_avail_corners; + int init_voltage_step; + int init_voltage_width; + u8 sensor_range_start; + u8 sensor_range_end; + u8 step_quot_init_min; + u8 step_quot_init_max; + unsigned int num_fuse_corners; + struct fuse_corner_data *fuse_corner_data; +}; + +/* + * struct cpr_desc - Driver instance-wide CPR parameters + * + * @cpr_type: Type (base version) of the CPR controller + * @num_threads: Max. number of threads supported by this controller + * @timer_delay_us: Loop delay time in uS + * @timer_updn_delay_us: Voltage after-up/before-down delay time in uS + * @timer_cons_up: Wait between consecutive up requests in uS + * @timer_cons_down: Wait between consecutive down requests in uS + * @up_threshold: Generic corner up threshold + * @down_threshold: Generic corner down threshold + * @idle_clocks: CPR Sensor: idle timer in cpr clocks unit + * @count_mode: CPR Sensor: counting mode + * @count_repeat: CPR Sensor: number of times to repeat reading + * @gcnt_us: CPR measurement interval in uS + * @vreg_step_fixed: Regulator voltage per step (if vreg unusable) + * @vreg_step_up_limit: Num. of steps up at once before re-measuring sensors + * @vreg_step_down_limit: Num. of steps dn at once before re-measuring sensors + * @vdd_settle_time_us: Settling timer to account for one VDD supply step + * @corner_settle_time_us: Settle time for corner switch request + * @mem_acc_threshold: Memory Accelerator (MEM-ACC) voltage threshold + * @apm_threshold: Array Power Mux (APM) voltage threshold + * @apm_crossover: Array Power Mux (APM) corner crossover voltage + * @apm_hysteresis: Hysteresis for APM V-threshold related calculations + * @cpr_base_voltage: Safety: Absolute minimum voltage (uV) on this CPR + * @cpr_max_voltage: Safety: Absolute maximum voltage (uV) on this CPR + * @pd_throttle_val: CPR Power Domain throttle during voltage switch + * @threads: Array containing "CPR Thread" specific parameters + * @reduce_to_fuse_uV: Reduce corner max volts (if higher) to fuse ceiling + * @reduce_to_corner_uV: Reduce corner max volts (if higher) to corner ceil. + * @hw_closed_loop_en: Enable CPR HW Closed-Loop voltage auto-adjustment + */ +struct cpr_desc { + enum cpr_type cpr_type; + unsigned int num_threads; + unsigned int timer_delay_us; + u8 timer_updn_delay_us; + u8 timer_cons_up; + u8 timer_cons_down; + u8 up_threshold; + u8 down_threshold; + u8 idle_clocks; + u8 count_mode; + u8 count_repeat; + u8 gcnt_us; + u16 vreg_step_fixed; + u8 vreg_step_up_limit; + u8 vreg_step_down_limit; + u8 vdd_settle_time_us; + u8 corner_settle_time_us; + int mem_acc_threshold; + int apm_threshold; + int apm_crossover; + int apm_hysteresis; + u32 cpr_base_voltage; + u32 cpr_max_voltage; + u32 pd_throttle_val; + + const struct cpr_thread_desc **threads; + bool reduce_to_fuse_uV; + bool reduce_to_corner_uV; + bool hw_closed_loop_en; +}; + +struct cpr_drv; +struct cpr_thread { + int num_corners; + int id; + bool enabled; + void __iomem *base; + struct clk *cpu_clk; + struct corner *corner; + struct corner *corners; + struct fuse_corner *fuse_corners; + struct cpr_drv *drv; + struct cpr_ext_data ext_data; + struct generic_pm_domain pd; + struct device *attached_cpu_dev; + struct work_struct restart_work; + bool restarting; + + const struct cpr_fuse *cpr_fuses; + const struct cpr_thread_desc *desc; +}; + +struct cpr_drv { + int irq; + unsigned int ref_clk_khz; + struct device *dev; + struct mutex lock; + struct regulator *vreg; + struct regmap *tcsr; + u32 gcnt; + u32 speed_bin; + u32 fusing_rev; + u32 last_uV; + u32 cpr_hw_rev; + u32 reg_corner; + u32 reg_corner_tid; + u32 reg_ctl; + u32 reg_status; + int fuse_level_set; + int extra_corners; + unsigned int vreg_step; + bool enabled; + + struct cpr_thread *threads; + struct genpd_onecell_data cell_data; + + const struct cpr_desc *desc; + const struct acc_desc *acc_desc; + struct dentry *debugfs; +}; + +/** + * cpr_get_ro_factor() - Get fuse corner ring oscillator factor + * + * Not all threads have different scaling factors for each + * Fuse Corner: if the RO factors are the same for all corners, + * then only one is specified, instead of uselessly repeating + * the same array for FC-times. + * This function checks for the same and gives back the right + * factor for the requested ring oscillator. + * + * Return: Ring oscillator factor + */ +static int cpr_get_ro_factor(const struct cpr_thread_desc *tdesc, + int fnum, int ro_idx) +{ + int ro_fnum; + + if (tdesc->ro_avail_corners == tdesc->num_fuse_corners) + ro_fnum = fnum; + else + ro_fnum = 0; + + return tdesc->ro_scaling_factor[ro_fnum][ro_idx]; +} + +static void cpr_write(struct cpr_thread *thread, u32 offset, u32 value) +{ + writel(value, thread->base + offset); +} + +static u32 cpr_read(struct cpr_thread *thread, u32 offset) +{ + return readl(thread->base + offset); +} + +static void +cpr_masked_write(struct cpr_thread *thread, u32 offset, u32 mask, u32 value) +{ + u32 val; + + val = readl(thread->base + offset); + val &= ~mask; + val |= value & mask; + writel(val, thread->base + offset); +} + +static void cpr_irq_clr(struct cpr_thread *thread) +{ + cpr_write(thread, CPR3_REG_IRQ_CLEAR, CPR3_IRQ_ALL); +} + +static void cpr_irq_clr_nack(struct cpr_thread *thread) +{ + cpr_irq_clr(thread); + cpr_write(thread, CPR3_REG_CONT_CMD, CPR3_CONT_CMD_NACK); +} + +static void cpr_irq_clr_ack(struct cpr_thread *thread) +{ + cpr_irq_clr(thread); + cpr_write(thread, CPR3_REG_CONT_CMD, CPR3_CONT_CMD_ACK); +} + +static void cpr_irq_set(struct cpr_thread *thread, u32 int_bits) +{ + /* On CPR-hardened, interrupts are managed by and on firmware */ + if (thread->drv->desc->cpr_type == CTRL_TYPE_CPRH) + return; + + cpr_write(thread, CPR3_REG_IRQ_EN, int_bits); +} + +/** + * cpr_ctl_enable() - Enable CPR thread + * @thread: Structure holding CPR thread-specific parameters + */ +static void cpr_ctl_enable(struct cpr_thread *thread) +{ + if (thread->drv->enabled && !thread->restarting) { + cpr_masked_write(thread, CPR3_REG_CPR_CTL, + CPR3_CPR_CTL_LOOP_EN_MASK, + CPR3_CPR_CTL_LOOP_EN_MASK); + } +} + +/** + * cpr_ctl_disable() - Disable CPR thread + * @thread: Structure holding CPR thread-specific parameters + */ +static void cpr_ctl_disable(struct cpr_thread *thread) +{ + const struct cpr_desc *desc = thread->drv->desc; + + if (desc->cpr_type != CTRL_TYPE_CPRH) { + cpr_irq_set(thread, 0); + cpr_irq_clr(thread); + } + + cpr_masked_write(thread, CPR3_REG_CPR_CTL, + CPR3_CPR_CTL_LOOP_EN_MASK, 0); +} + +/** + * cpr_ctl_is_enabled() - Check if thread is enabled + * @thread: Structure holding CPR thread-specific parameters + * + * Return: true if the CPR is enabled, false if it is disabled. + */ +static bool cpr_ctl_is_enabled(struct cpr_thread *thread) +{ + u32 reg_val; + + reg_val = cpr_read(thread, CPR3_REG_CPR_CTL); + return reg_val & CPR3_CPR_CTL_LOOP_EN_MASK; +} + +/** + * cpr_check_any_thread_busy() - Check if HW is done processing + * @thread: Structure holding CPR thread-specific parameters + * + * Return: true if the CPR is busy, false if it is ready. + */ +static bool cpr_check_any_thread_busy(struct cpr_thread *thread) +{ + int i; + + for (i = 0; i < thread->drv->desc->num_threads; i++) + if (cpr_read(thread, CPR3_REG_RESULT0(i)) & + CPR3_RESULT0_BUSY_MASK) + return true; + + return false; +} + +static void cpr_restart_worker(struct work_struct *work) +{ + struct cpr_thread *thread = container_of(work, struct cpr_thread, + restart_work); + struct cpr_drv *drv = thread->drv; + int i; + + mutex_lock(&drv->lock); + + thread->restarting = true; + cpr_ctl_disable(thread); + disable_irq(drv->irq); + + mutex_unlock(&drv->lock); + + for (i = 0; i < 20; i++) { + u32 cpr_status = cpr_read(thread, CPR3_REG_CPR_STATUS); + u32 ctl = cpr_read(thread, CPR3_REG_CPR_CTL); + + if ((cpr_status & CPR3_CPR_STATUS_BUSY_MASK) && + !(ctl & CPR3_CPR_CTL_LOOP_EN_MASK)) + break; + + udelay(10); + } + + cpr_irq_clr(thread); + + for (i = 0; i < 20; i++) { + u32 status = cpr_read(thread, CPR3_REG_IRQ_STATUS); + + if (!(status & CPR3_IRQ_ALL)) + break; + udelay(10); + } + + mutex_lock(&drv->lock); + + thread->restarting = false; + enable_irq(drv->irq); + cpr_ctl_enable(thread); + + mutex_unlock(&drv->lock); +} + +/** + * cpr_corner_restore() - Restore saved corner level + * @thread: Structure holding CPR thread-specific parameters + * @corner: Structure holding the saved corner level + */ +static void cpr_corner_restore(struct cpr_thread *thread, + struct corner *corner) +{ + struct cpr_drv *drv = thread->drv; + struct fuse_corner *fuse = corner->fuse_corner; + const struct cpr_thread_desc *tdesc = thread->desc; + u32 ro_sel = fuse->ring_osc_idx; + + cpr_write(thread, CPR3_REG_GCNT(ro_sel), drv->gcnt); + + cpr_write(thread, CPR3_REG_RO_MASK(tdesc->hw_tid), + CPR3_RO_MASK & ~BIT(ro_sel)); + + cpr_write(thread, CPR3_REG_TARGET_QUOT(tdesc->hw_tid, ro_sel), + fuse->quot - corner->quot_adjust); + + if (drv->desc->cpr_type == CTRL_TYPE_CPR4) { + cpr_masked_write(thread, + CPR4_REG_CPR_MASK_THREAD(tdesc->hw_tid), + CPR4_CPR_MASK_THREAD_DISABLE_THREAD | + CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK, 0); + } + + thread->corner = corner; + corner->last_uV = corner->uV; +} + +/** + * cpr_set_acc() - Set fuse level to the mem-acc + * @thread: Structure holding CPR thread-specific parameters + * @f: Fuse level + */ +static void cpr_set_acc(struct cpr_drv *drv, int f) +{ + const struct acc_desc *desc = drv->acc_desc; + struct reg_sequence *s = desc->settings; + int n = desc->num_regs_per_fuse; + + if (!s || f == drv->fuse_level_set) + return; + + regmap_multi_reg_write(drv->tcsr, s + (n * f), n); + drv->fuse_level_set = f; +} + +/** + * cpr_post_voltage() - Actions to execute before setting voltage + * @thread: Structure holding CPR thread-specific parameters + * @dir: Enumeration for voltage change direction + * @fuse_level: Fuse corner for mem-acc, if supported. + * + * Return: Zero for success or negative number on errors. + */ +static int cpr_pre_voltage(struct cpr_thread *thread, + enum voltage_change_dir dir, + int fuse_level) +{ + struct cpr_drv *drv = thread->drv; + + if (drv->desc->cpr_type == CTRL_TYPE_CPR3 && + drv->desc->pd_throttle_val) + cpr_write(thread, CPR3_REG_PD_THROTTLE, + drv->desc->pd_throttle_val); + + if (drv->tcsr && dir == DOWN) + cpr_set_acc(drv, fuse_level); + + return 0; +} + +/** + * cpr_post_voltage() - Actions to execute after setting voltage + * @thread: Structure holding CPR thread-specific parameters + * @dir: Enumeration for voltage change direction + * @fuse_level: Fuse corner for mem-acc, if supported. + * + * Return: Zero for success or negative number on errors. + */ +static int cpr_post_voltage(struct cpr_thread *thread, + enum voltage_change_dir dir, + int fuse_level) +{ + struct cpr_drv *drv = thread->drv; + + if (drv->tcsr && dir == UP) + cpr_set_acc(drv, fuse_level); + + if (drv->desc->cpr_type == CTRL_TYPE_CPR3) + cpr_write(thread, CPR3_REG_PD_THROTTLE, 0); + + return 0; +} + +/** + * cpr_commit_state() - Set the newly requested voltage + * @thread: Structure holding CPR thread-specific parameters + * + * Return: IRQ_SUCCESS for success, IRQ_NONE if the CPR is disabled. + */ +static int cpr_commit_state(struct cpr_thread *thread) +{ + struct cpr_drv *drv = thread->drv; + int min_uV = 0, max_uV = 0, new_uV = 0, fuse_level = 0; + enum voltage_change_dir dir; + u32 next_irqmask = 0; + int ret, i; + + /* On CPRhardened, control states are managed in firmware */ + if (drv->desc->cpr_type == CTRL_TYPE_CPRH) + return 0; + + for (i = 0; i < drv->desc->num_threads; i++) { + struct cpr_thread *thread = &drv->threads[i]; + + if (!thread->corner) + continue; + + fuse_level = max(fuse_level, + (int) (thread->corner->fuse_corner - + &thread->fuse_corners[0])); + + max_uV = max(max_uV, thread->corner->max_uV); + min_uV = max(min_uV, thread->corner->min_uV); + new_uV = max(new_uV, thread->corner->last_uV); + } + dev_vdbg(drv->dev, "%s: new uV: %d, last uV: %d\n", + __func__, new_uV, drv->last_uV); + + /* + * Safety measure: if the voltage is out of the globally allowed + * range, then go out and warn the user. + * This should *never* happen. + */ + if (new_uV > drv->desc->cpr_max_voltage || + new_uV < drv->desc->cpr_base_voltage) { + dev_warn(drv->dev, "Voltage (%u uV) out of range.", new_uV); + return -EINVAL; + } + + if (new_uV == drv->last_uV || fuse_level == drv->fuse_level_set) + goto out; + + if (fuse_level > drv->fuse_level_set) + dir = UP; + else + dir = DOWN; + + ret = cpr_pre_voltage(thread, fuse_level, dir); + if (ret) + return ret; + + dev_vdbg(drv->dev, "setting voltage: %d\n", new_uV); + + ret = regulator_set_voltage(drv->vreg, new_uV, new_uV); + if (ret) { + dev_err_ratelimited(drv->dev, "failed to set voltage %d: %d\n", new_uV, ret); + return ret; + } + + ret = cpr_post_voltage(thread, fuse_level, dir); + if (ret) + return ret; + + drv->last_uV = new_uV; +out: + if (new_uV > min_uV) + next_irqmask |= CPR3_IRQ_DOWN; + if (new_uV < max_uV) + next_irqmask |= CPR3_IRQ_UP; + + cpr_irq_set(thread, next_irqmask); + + return 0; +} + +static unsigned int cpr_get_cur_perf_state(struct cpr_thread *thread) +{ + return thread->corner ? thread->corner - thread->corners + 1 : 0; +} + +/** + * cpr_scale() - Calculate new voltage for the received direction + * @thread: Structure holding CPR thread-specific parameters + * @dir: Enumeration for voltage change direction + * + * The CPR scales one by one: this function calculates the new + * voltage to set when a voltage-UP or voltage-DOWN request comes + * and stores it into the per-thread structure that gets passed. + */ +static void cpr_scale(struct cpr_thread *thread, enum voltage_change_dir dir) +{ + struct cpr_drv *drv = thread->drv; + const struct cpr_thread_desc *tdesc = thread->desc; + u32 val, error_steps; + int last_uV, new_uV; + struct corner *corner; + + if (dir != UP && dir != DOWN) + return; + + corner = thread->corner; + val = cpr_read(thread, CPR3_REG_RESULT0(tdesc->hw_tid)); + error_steps = val >> CPR3_RESULT0_ERROR_STEPS_SHIFT; + error_steps &= CPR3_RESULT0_ERROR_STEPS_MASK; + + last_uV = corner->last_uV; + + if (dir == UP) { + if (!(val & CPR3_RESULT0_STEP_UP_MASK)) + return; + + /* Calculate new voltage */ + new_uV = last_uV + drv->vreg_step; + new_uV = min(new_uV, corner->max_uV); + + dev_vdbg(drv->dev, "[T%u] UP - new_uV=%d last_uV=%d p-state=%u st=%u\n", + thread->id, new_uV, last_uV, + cpr_get_cur_perf_state(thread), error_steps); + } else { + if (!(val & CPR3_RESULT0_STEP_DN_MASK)) + return; + + /* Calculate new voltage */ + new_uV = last_uV - drv->vreg_step; + new_uV = max(new_uV, corner->min_uV); + dev_vdbg(drv->dev, "[T%u] DOWN - new_uV=%d last_uV=%d p-state=%u st=%u\n", + thread->id, new_uV, last_uV, + cpr_get_cur_perf_state(thread), error_steps); + } + corner->last_uV = new_uV; +} + +/** + * cpr_irq_handler() - Handle CPR3/CPR4 status interrupts + * @irq: Number of the interrupt + * @dev: Pointer to the cpr_thread structure + * + * Handle the interrupts coming from non-hardened CPR HW as to get + * an ok to scale voltages immediately, or to pass error status to + * the hardware (either success/ACK or failure/NACK). + * + * Return: IRQ_SUCCESS for success, IRQ_NONE if the CPR is disabled. + */ +static irqreturn_t cpr_irq_handler(int irq, void *dev) +{ + struct cpr_thread *thread = dev; + struct cpr_drv *drv = thread->drv; + irqreturn_t ret = IRQ_HANDLED; + int i, rc; + enum voltage_change_dir dir = NO_CHANGE; + u32 val; + + mutex_lock(&drv->lock); + + val = cpr_read(thread, CPR3_REG_IRQ_STATUS); + + dev_vdbg(drv->dev, "IRQ_STATUS = %#02x\n", val); + + if (!cpr_ctl_is_enabled(thread)) { + dev_vdbg(drv->dev, "CPR is disabled\n"); + ret = IRQ_NONE; + } else if (cpr_check_any_thread_busy(thread)) { + cpr_irq_clr_nack(thread); + dev_dbg(drv->dev, "CPR measurement is not ready\n"); + } else { + /* + * Following sequence of handling is as per each IRQ's + * priority + */ + if (val & CPR3_IRQ_UP) + dir = UP; + else if (val & CPR3_IRQ_DOWN) + dir = DOWN; + + if (dir != NO_CHANGE) { + for (i = 0; i < drv->desc->num_threads; i++) { + thread = &drv->threads[i]; + cpr_scale(thread, dir); + } + + rc = cpr_commit_state(thread); + if (rc) + cpr_irq_clr_nack(thread); + else + cpr_irq_clr_ack(thread); + } else if (val & CPR3_IRQ_MID) { + dev_dbg(drv->dev, "IRQ occurred for Mid Flag\n"); + } else { + dev_warn(drv->dev, "IRQ occurred for unknown flag (%#08x)\n", val); + schedule_work(&thread->restart_work); + } + } + + mutex_unlock(&drv->lock); + + return ret; +} + +static int cpr_switch(struct cpr_drv *drv) +{ + int i, ret; + bool enabled = false; + + if (drv->desc->cpr_type == CTRL_TYPE_CPRH) + return 0; + + for (i = 0; i < drv->desc->num_threads && !enabled; i++) + enabled = drv->threads[i].enabled; + + dev_vdbg(drv->dev, "%s: enabled = %d\n", __func__, enabled); + + if (enabled == drv->enabled) + return 0; + + if (enabled) { + ret = regulator_enable(drv->vreg); + if (ret) + return ret; + + drv->enabled = enabled; + + for (i = 0; i < drv->desc->num_threads; i++) + if (drv->threads[i].corner) + break; + + if (i < drv->desc->num_threads) { + cpr_irq_clr(&drv->threads[i]); + + cpr_commit_state(&drv->threads[i]); + cpr_ctl_enable(&drv->threads[i]); + } + } else { + for (i = 0; i < drv->desc->num_threads && !enabled; i++) + cpr_ctl_disable(&drv->threads[i]); + + drv->enabled = enabled; + + ret = regulator_disable(drv->vreg); + if (ret < 0) + return ret; + } + + return 0; +} + +/** + * cpr_enable() - Enables a CPR thread + * @thread: Structure holding CPR thread-specific parameters + * + * Return: Zero for success or negative number on errors. + */ +static int cpr_enable(struct cpr_thread *thread) +{ + struct cpr_drv *drv = thread->drv; + int ret; + + dev_dbg(drv->dev, "Enabling thread %d\n", thread->id); + + mutex_lock(&drv->lock); + + thread->enabled = true; + ret = cpr_switch(thread->drv); + + mutex_unlock(&drv->lock); + + return ret; +} + +/** + * cpr_disable() - Disables a CPR thread + * @thread: Structure holding CPR thread-specific parameters + * + * Return: Zero for success or negative number on errors. + */ +static int cpr_disable(struct cpr_thread *thread) +{ + struct cpr_drv *drv = thread->drv; + int ret; + + dev_dbg(drv->dev, "Disabling thread %d\n", thread->id); + + mutex_lock(&drv->lock); + + thread->enabled = false; + ret = cpr_switch(thread->drv); + + mutex_unlock(&drv->lock); + + return ret; +} + +/** + * cpr_configure() - Configure main HW parameters + * @thread: Structure holding CPR thread-specific parameters + * + * This function configures the main CPR hardware parameters, such as + * internal timers (and delays), sensor ownerships, activates and/or + * deactivates cpr-threads and others, as one sequence for all of the + * versions supported in this driver. By design, the function may + * return a success earlier if the sequence for "a previous version" + * has ended. + * + * Context: The CPR must be clocked before calling this function! + * + * Return: Zero for success or negative number on errors. + */ +static int cpr_configure(struct cpr_thread *thread) +{ + struct cpr_drv *drv = thread->drv; + const struct cpr_desc *desc = drv->desc; + const struct cpr_thread_desc *tdesc = thread->desc; + u32 val; + int i; + + /* Disable interrupt and CPR */ + cpr_irq_set(thread, 0); + cpr_write(thread, CPR3_REG_CPR_CTL, 0); + + /* Init and save gcnt */ + drv->gcnt = drv->ref_clk_khz * desc->gcnt_us; + do_div(drv->gcnt, 1000); + + /* Program the delay count for the timer */ + val = drv->ref_clk_khz * desc->timer_delay_us; + do_div(val, 1000); + if (desc->cpr_type == CTRL_TYPE_CPR3) { + cpr_write(thread, CPR3_REG_CPR_TIMER_MID_CONT, val); + + val = drv->ref_clk_khz * desc->timer_updn_delay_us; + do_div(val, 1000); + cpr_write(thread, CPR3_REG_CPR_TIMER_UP_DN_CONT, val); + } else { + cpr_write(thread, CPR3_REG_CPR_TIMER_AUTO_CONT, val); + } + dev_dbg(drv->dev, "Timer count: %#0x (for %d us)\n", val, + desc->timer_delay_us); + + /* Program the control register */ + val = desc->idle_clocks << CPR3_CPR_CTL_IDLE_CLOCKS_SHIFT; + val |= desc->count_mode << CPR3_CPR_CTL_COUNT_MODE_SHIFT; + val |= desc->count_repeat << CPR3_CPR_CTL_COUNT_REPEAT_SHIFT; + cpr_write(thread, CPR3_REG_CPR_CTL, val); + + /* Configure CPR default step quotients */ + val = tdesc->step_quot_init_min << CPR3_CPR_STEP_QUOT_MIN_SHIFT; + val |= tdesc->step_quot_init_max << CPR3_CPR_STEP_QUOT_MAX_SHIFT; + + cpr_write(thread, CPR3_REG_CPR_STEP_QUOT, val); + + /* + * Configure the CPR sensor ownership always on thread 0 + * TODO: SDM845 has different ownership for sensors!! + */ + for (i = tdesc->sensor_range_start; i < tdesc->sensor_range_end; i++) + cpr_write(thread, CPR3_REG_SENSOR_OWNER(i), 0); + + /* Program Consecutive Up & Down */ + val = desc->timer_cons_up << CPR3_THRESH_CONS_UP_SHIFT; + val |= desc->timer_cons_down << CPR3_THRESH_CONS_DOWN_SHIFT; + val |= desc->up_threshold << CPR3_THRESH_UP_THRESH_SHIFT; + val |= desc->down_threshold << CPR3_THRESH_DOWN_THRESH_SHIFT; + cpr_write(thread, CPR3_REG_THRESH(tdesc->hw_tid), val); + + /* Mask all ring oscillators for all threads initially */ + cpr_write(thread, CPR3_REG_RO_MASK(tdesc->hw_tid), CPR3_RO_MASK); + + /* HW Closed-loop control */ + if (desc->cpr_type == CTRL_TYPE_CPR3) { + cpr_write(thread, CPR3_REG_HW_CLOSED_LOOP_DISABLED, + !desc->hw_closed_loop_en); + } else { + cpr_masked_write(thread, CPR4_REG_MARGIN_ADJ_CTL, + CPR4_MARGIN_ADJ_HW_CLOSED_LOOP_EN, + desc->hw_closed_loop_en ? + CPR4_MARGIN_ADJ_HW_CLOSED_LOOP_EN : 0); + } + + /* Additional configuration for CPR4 and beyond */ + if (desc->cpr_type < CTRL_TYPE_CPR4) + return 0; + + /* Disable threads initially only on non-hardened CPR4 */ + if (desc->cpr_type == CTRL_TYPE_CPR4) { + cpr_masked_write(thread, CPR4_REG_CPR_MASK_THREAD(1), + CPR4_CPR_MASK_THREAD_DISABLE_THREAD | + CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK, + CPR4_CPR_MASK_THREAD_DISABLE_THREAD | + CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK); + } + + if (tdesc->hw_tid > 0) { + cpr_masked_write(thread, CPR4_REG_MISC, + CPR4_MISC_RESET_STEP_QUOT_LOOP_EN | + CPR4_MISC_THREAD_HAS_ALWAYS_VOTE_EN, + CPR4_MISC_RESET_STEP_QUOT_LOOP_EN | + CPR4_MISC_THREAD_HAS_ALWAYS_VOTE_EN); + } + + val = drv->vreg_step; + do_div(val, 1000); + cpr_masked_write(thread, CPR4_REG_MARGIN_ADJ_CTL, + CPR4_MARGIN_ADJ_PMIC_STEP_SIZE_MASK, + val << CPR4_MARGIN_ADJ_PMIC_STEP_SIZE_SHIFT); + + cpr_masked_write(thread, CPR4_REG_SAW_ERROR_STEP_LIMIT, + CPR4_SAW_ERROR_STEP_LIMIT_DN_MASK, + desc->vreg_step_down_limit << + CPR4_SAW_ERROR_STEP_LIMIT_DN_SHIFT); + + cpr_masked_write(thread, CPR4_REG_SAW_ERROR_STEP_LIMIT, + CPR4_SAW_ERROR_STEP_LIMIT_UP_MASK, + desc->vreg_step_up_limit << + CPR4_SAW_ERROR_STEP_LIMIT_UP_SHIFT); + + cpr_masked_write(thread, CPR4_REG_MARGIN_ADJ_CTL, + CPR4_MARGIN_ADJ_PER_RO_KV_MARGIN_EN, + CPR4_MARGIN_ADJ_PER_RO_KV_MARGIN_EN); + + if (tdesc->hw_tid > 0) { + cpr_masked_write(thread, CPR4_REG_CPR_TIMER_CLAMP, + CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN, + CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN); + } + + /* Settling timer to account for one VDD supply step */ + if (desc->vdd_settle_time_us > 0) { + u32 m = CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_MASK; + u32 s = CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_SHFT; + + cpr_masked_write(thread, CPR4_REG_MARGIN_TEMP_CORE_TIMERS, + m, desc->vdd_settle_time_us << s); + } + + /* Additional configuration for CPR-hardened */ + if (desc->cpr_type < CTRL_TYPE_CPRH) + return 0; + + /* Settling timer to account for one corner-switch request */ + if (desc->corner_settle_time_us > 0) { + cpr_masked_write(thread, drv->reg_ctl, + CPRH_CTL_MODE_SWITCH_DELAY_MASK, + desc->corner_settle_time_us << + CPRH_CTL_MODE_SWITCH_DELAY_SHIFT); + } + + /* Base voltage and multiplier values for CPRh internal calculations */ + cpr_masked_write(thread, drv->reg_ctl, + CPRH_CTL_BASE_VOLTAGE_MASK, + (DIV_ROUND_UP(desc->cpr_base_voltage, + drv->vreg_step) << + CPRH_CTL_BASE_VOLTAGE_SHIFT)); + + cpr_masked_write(thread, drv->reg_ctl, + CPRH_CTL_VOLTAGE_MULTIPLIER_MASK, + DIV_ROUND_UP(drv->vreg_step, 1000) << + CPRH_CTL_VOLTAGE_MULTIPLIER_SHIFT); + + return 0; +} + + +static int cprh_dummy_set_performance_state(struct generic_pm_domain *domain, + unsigned int state) +{ + return 0; +} + +static int cpr_set_performance_state(struct generic_pm_domain *domain, + unsigned int state) +{ + struct cpr_thread *thread = container_of(domain, struct cpr_thread, pd); + struct cpr_drv *drv = thread->drv; + struct corner *corner, *end; + int ret = 0; + + mutex_lock(&drv->lock); + + dev_dbg(drv->dev, "setting perf state: %u (prev state: %u thread: %u)\n", + state, cpr_get_cur_perf_state(thread), thread->id); + + /* + * Determine new corner we're going to. + * Remove one since lowest performance state is 1. + */ + corner = thread->corners + state - 1; + end = &thread->corners[thread->num_corners - 1]; + if (corner > end || corner < thread->corners) { + ret = -EINVAL; + goto unlock; + } + + cpr_ctl_disable(thread); + + cpr_irq_clr(thread); + if (thread->corner != corner) + cpr_corner_restore(thread, corner); + + ret = cpr_commit_state(thread); + if (ret) + goto unlock; + + cpr_ctl_enable(thread); +unlock: + mutex_unlock(&drv->lock); + + dev_dbg(drv->dev, "set perf state %u on thread %u\n", state, thread->id); + + return ret; +} + +/** + * cpr3_adjust_quot - Adjust the closed-loop quotients + * @thread: Structure holding CPR thread-specific parameters + * + * Calculates the quotient adjustment factor based on closed-loop + * quotients and ring oscillator factor. + * + * Return: Adjusted quotient + */ +static int cpr3_adjust_quot(int ring_osc_factor, int volt_closed_loop) +{ + s64 temp; + + if (ring_osc_factor == 0 || volt_closed_loop == 0) + return 0; + + temp = (s64)(ring_osc_factor * volt_closed_loop); + return (int)div_s64(temp, 1000000); +} + +/** + * cpr_fuse_corner_init() - Calculate fuse corner table + * @thread: Structure holding CPR thread-specific parameters + * + * This function populates the fuse corners table by reading the + * values from the fuses, eventually adjusting them with a fixed + * per-corner offset and doing basic checks about them being + * supported by the regulator that is assigned to this CPR - if + * it is available (on CPR-Hardened, there is no usable vreg, as + * that is protected by the hypervisor). + * + * Return: Zero for success, negative number on error + */ +static int cpr_fuse_corner_init(struct cpr_thread *thread) +{ + struct cpr_drv *drv = thread->drv; + const struct cpr_thread_desc *desc = thread->desc; + const struct cpr_fuse *cpr_fuse = thread->cpr_fuses; + struct fuse_corner_data *fdata; + struct fuse_corner *fuse, *prev_fuse, *end; + int i, ret; + + /* Populate fuse_corner members */ + fuse = thread->fuse_corners; + prev_fuse = &fuse[0]; + end = &fuse[desc->num_fuse_corners - 1]; + fdata = desc->fuse_corner_data; + + for (i = 0; fuse <= end; fuse++, cpr_fuse++, i++, fdata++) { + int factor = cpr_get_ro_factor(desc, i, fuse->ring_osc_idx); + + ret = cpr_populate_fuse_common(drv->dev, fdata, cpr_fuse, + fuse, drv->vreg_step, + desc->init_voltage_width, + desc->init_voltage_step); + if (ret) + return ret; + + /* + * Adjust the fuse quot with per-fuse-corner closed-loop + * voltage adjustment parameters. + */ + fuse->quot += cpr3_adjust_quot(factor, fdata->volt_cloop_adjust); + + /* CPRh: no regulator access... */ + if (drv->desc->cpr_type == CTRL_TYPE_CPRH) + goto skip_pvs_restrict; + + /* Re-check if corner voltage range is supported by regulator */ + ret = cpr_check_vreg_constraints(drv->dev, drv->vreg, fuse); + if (ret) + return ret; + +skip_pvs_restrict: + if (fuse->uV < prev_fuse->uV) + fuse->uV = prev_fuse->uV; + prev_fuse = fuse; + dev_dbg(drv->dev, "fuse corner %d: [%d %d %d] RO%hhu quot %d\n", + i, fuse->min_uV, fuse->uV, fuse->max_uV, + fuse->ring_osc_idx, fuse->quot); + + /* Check if constraints are valid */ + if (fuse->uV < fuse->min_uV || fuse->uV > fuse->max_uV) { + dev_err(drv->dev, "fuse corner %d: Bad voltage range.\n", i); + return -EINVAL; + } + } + + return 0; +} + +static void cpr3_restrict_corner(struct corner *corner, int threshold, + int hysteresis, int step) +{ + if (threshold > corner->min_uV && threshold <= corner->max_uV) { + if (corner->uV >= threshold) { + corner->min_uV = max(corner->min_uV, + threshold - hysteresis); + if (corner->min_uV > corner->uV) + corner->uV = corner->min_uV; + } else { + corner->max_uV = threshold; + corner->max_uV -= step; + } + } +} + +/* + * cprh_corner_adjust_opps() - Set voltage on each CPU OPP table entry + * + * On CPR-Hardened, the voltage level is controlled internally through + * the OSM hardware: in order to initialize the latter, we have to + * communicate the voltage to its driver, so that it will be able to + * write the right parameters (as they have to be set both on the CPRh + * and on the OSM) on it. + * This function is called only for CPRh. + * + * Return: Zero for success, negative number for error. + */ +static int cprh_corner_adjust_opps(struct cpr_thread *thread) +{ + struct corner *corner = thread->corners; + struct cpr_drv *drv = thread->drv; + int i, ret; + + for (i = 0; i < thread->num_corners; i++) { + ret = dev_pm_opp_adjust_voltage(thread->attached_cpu_dev, + corner[i].freq, + corner[i].uV, + corner[i].min_uV, + corner[i].max_uV); + if (ret) + break; + + dev_dbg(drv->dev, "OPP voltage adjusted for %lu kHz, %d uV\n", + corner[i].freq, corner[i].uV); + } + + /* If we couldn't adjust voltage for all corners, something went wrong */ + if (i < thread->num_corners) + return -EINVAL; + + return ret; +} + +/** + * cpr3_corner_init() - Calculate and set-up corners for the CPR HW + * @thread: Structure holding CPR thread-specific parameters + * + * This function calculates all the corner parameters by comparing + * and interpolating the values read from the various set-points + * read from the fuses (also called "fuse corners") to generate and + * program to the CPR a lookup table that describes each voltage + * step, mapped to a performance level (or corner number). + * + * It also programs other essential parameters on the CPR and - if + * we are dealing with CPR-Hardened, it will also enable the internal + * interface between the Operating State Manager (OSM) and the CPRh + * in order to achieve CPU DVFS. + * + * Return: Zero for success, negative number on error + */ +static int cpr3_corner_init(struct cpr_thread *thread) +{ + struct cpr_drv *drv = thread->drv; + const struct cpr_desc *desc = drv->desc; + const struct cpr_thread_desc *tdesc = thread->desc; + const struct cpr_fuse *fuses = thread->cpr_fuses; + int i, ret, total_corners, extra_corners, level, scaling = 0; + unsigned int fnum, fc; + const char *quot_offset; + const struct fuse_corner_data *fdata; + struct fuse_corner *fuse, *prev_fuse; + struct corner *corner, *prev_corner, *end; + struct corner_data *cdata; + struct dev_pm_opp *opp; + unsigned long freq; + u32 ring_osc_mask = CPR3_RO_MASK, min_quotient = U32_MAX; + + corner = thread->corners; + prev_corner = &thread->corners[0]; + end = &corner[thread->num_corners - 1]; + + cdata = devm_kcalloc(drv->dev, thread->num_corners + drv->extra_corners, + sizeof(struct corner_data), GFP_KERNEL); + if (!cdata) + return -ENOMEM; + + for (level = 1; level <= thread->num_corners; level++) { + opp = dev_pm_opp_find_level_exact(&thread->pd.dev, level); + if (IS_ERR(opp)) + return -EINVAL; + + /* + * If there is only one specified qcom,opp-fuse-level, then + * it is assumed that this only one is global and valid for + * all IDs, so try to get the specific one but, on failure, + * go for the global one. + */ + fc = cpr_get_fuse_corner(opp, thread->id); + if (fc == 0) { + fc = cpr_get_fuse_corner(opp, 0); + if (fc == 0) { + dev_err(drv->dev, "qcom,opp-fuse-level is missing!\n"); + dev_pm_opp_put(opp); + return -EINVAL; + } + } + fnum = fc - 1; + + freq = cpr_get_opp_hz_for_req(opp, thread->attached_cpu_dev); + if (!freq) { + thread->num_corners = max(level - 1, 0); + end = &thread->corners[thread->num_corners - 1]; + break; + } + + /* + * If any post-vadj (open/closed loop) is not specified, then + * it's zero, meaning that it is not required for this corner. + */ + cpr_get_corner_post_vadj(opp, thread->id, + &cdata[level - 1].oloop_vadj, + &cdata[level - 1].cloop_vadj); + cdata[level - 1].fuse_corner = fnum; + cdata[level - 1].freq = freq; + + fuse = &thread->fuse_corners[fnum]; + dev_dbg(drv->dev, "freq: %lu level: %u fuse level: %u\n", + freq, dev_pm_opp_get_level(opp) - 1, fnum); + if (freq > fuse->max_freq) + fuse->max_freq = freq; + dev_pm_opp_put(opp); + + /* + * Make sure that the frequencies in the table are in ascending + * order, as this is critical for the algorithm to work. + */ + if (cdata[level - 2].freq > freq) { + dev_err(drv->dev, "Frequency table not in ascending order.\n"); + return -EINVAL; + } + } + + if (thread->num_corners < 2) { + dev_err(drv->dev, "need at least 2 OPPs to use CPR\n"); + return -EINVAL; + } + + /* + * Get the quotient adjustment scaling factor, according to: + * + * scaling = min(1000 * (QUOT(corner_N) - QUOT(corner_N-1)) + * / (freq(corner_N) - freq(corner_N-1)), max_factor) + * + * QUOT(corner_N): quotient read from fuse for fuse corner N + * QUOT(corner_N-1): quotient read from fuse for fuse corner (N - 1) + * freq(corner_N): max frequency in MHz supported by fuse corner N + * freq(corner_N-1): max frequency in MHz supported by fuse corner + * (N - 1) + * + * Then walk through the corners mapped to each fuse corner + * and calculate the quotient adjustment for each one using the + * following formula: + * + * quot_adjust = (freq_max - freq_corner) * scaling / 1000 + * + * freq_max: max frequency in MHz supported by the fuse corner + * freq_corner: frequency in MHz corresponding to the corner + * scaling: calculated from above equation + * + * + * + + + * | v | + * q | f c o | f c + * u | c l | c + * o | f t | f + * t | c a | c + * | c f g | c f + * | e | + * +--------------- +---------------- + * 0 1 2 3 4 5 6 0 1 2 3 4 5 6 + * corner corner + * + * c = corner + * f = fuse corner + * + */ + for (i = 0; corner <= end; corner++, i++) { + unsigned long freq_diff_mhz; + int ro_fac, vadj, prev_quot; + + fnum = cdata[i].fuse_corner; + fdata = &tdesc->fuse_corner_data[fnum]; + quot_offset = fuses[fnum].quotient_offset; + fuse = &thread->fuse_corners[fnum]; + ring_osc_mask &= (u16)(~BIT(fuse->ring_osc_idx)); + if (fnum) + prev_fuse = &thread->fuse_corners[fnum - 1]; + else + prev_fuse = NULL; + + corner->fuse_corner = fuse; + corner->freq = cdata[i].freq; + corner->uV = fuse->uV; + + if (prev_fuse) { + if (prev_fuse->ring_osc_idx == fuse->ring_osc_idx) + quot_offset = NULL; + + scaling = cpr_calculate_scaling(quot_offset, drv->dev, + fdata, corner); + if (scaling < 0) + return scaling; + + freq_diff_mhz = fuse->max_freq - corner->freq; + do_div(freq_diff_mhz, 1000000); /* now in MHz */ + + corner->quot_adjust = scaling * freq_diff_mhz; + do_div(corner->quot_adjust, 1000); + + /* Fine-tune QUOT (closed-loop) based on fixed values */ + ro_fac = cpr_get_ro_factor(tdesc, fnum, fuse->ring_osc_idx); + vadj = cdata[i].cloop_vadj; + corner->quot_adjust -= cpr3_adjust_quot(ro_fac, vadj); + dev_vdbg(drv->dev, "Quot fine-tuning to %d for post-vadj=%d\n", + corner->quot_adjust, vadj); + + /* + * Make sure that we scale (up) monotonically. + * P.S.: Fuse quots can never be descending. + */ + prev_quot = prev_corner->fuse_corner->quot; + prev_quot -= prev_corner->quot_adjust; + if (fuse->quot - corner->quot_adjust < prev_quot) { + int new_adj = prev_corner->fuse_corner->quot; + new_adj -= fuse->quot; + dev_vdbg(drv->dev, "Monotonic increase forced: %d->%d\n", + corner->quot_adjust, new_adj); + corner->quot_adjust = new_adj; + } + + corner->uV = cpr_interpolate(corner, + drv->vreg_step, fdata); + } + /* Negative fuse quotients are nonsense. */ + if (fuse->quot < corner->quot_adjust) + return -EINVAL; + + min_quotient = min(min_quotient, + (u32)(fuse->quot - corner->quot_adjust)); + + /* Fine-tune voltages (open-loop) based on fixed values */ + corner->uV += cdata[i].oloop_vadj; + dev_dbg(drv->dev, "Voltage fine-tuning to %d for post-vadj=%d\n", + corner->uV, cdata[i].oloop_vadj); + + corner->max_uV = fuse->max_uV; + corner->min_uV = fuse->min_uV; + corner->uV = clamp(corner->uV, corner->min_uV, corner->max_uV); + dev_vdbg(drv->dev, "Clamped after interpolation: [%d %d %d]\n", + corner->min_uV, corner->uV, corner->max_uV); + + /* Make sure that we scale monotonically here, too. */ + if (corner->uV < prev_corner->uV) + corner->uV = prev_corner->uV; + + corner->last_uV = corner->uV; + + /* Reduce the ceiling voltage if needed */ + if (desc->reduce_to_corner_uV && corner->uV < corner->max_uV) + corner->max_uV = corner->uV; + else if (desc->reduce_to_fuse_uV && fuse->uV < corner->max_uV) + corner->max_uV = max(corner->min_uV, fuse->uV); + + corner->min_uV = max(corner->max_uV - fdata->range_uV, + corner->min_uV); + + /* + * Adjust per-corner floor and ceiling voltages so that + * they do not overlap the memory Array Power Mux (APM) + * nor the Memory Accelerator (MEM-ACC) threshold voltages. + */ + if (desc->apm_threshold) + cpr3_restrict_corner(corner, desc->apm_threshold, + desc->apm_hysteresis, + drv->vreg_step); + if (desc->mem_acc_threshold) + cpr3_restrict_corner(corner, desc->mem_acc_threshold, + 0, drv->vreg_step); + + prev_corner = corner; + dev_dbg(drv->dev, "corner %d: [%d %d %d] scaling %d quot %d\n", i, + corner->min_uV, corner->uV, corner->max_uV, scaling, + fuse->quot - corner->quot_adjust); + } + + /* Additional setup for CPRh only */ + if (desc->cpr_type < CTRL_TYPE_CPRH) + return 0; + + /* If the OPPs can't be adjusted, programming the CPRh is useless */ + ret = cprh_corner_adjust_opps(thread); + if (ret) { + dev_err(drv->dev, "Cannot adjust CPU OPP voltages: %d\n", ret); + return ret; + } + + total_corners = thread->num_corners; + extra_corners = drv->extra_corners; + + /* If the APM extra corner exists, add it now. */ + if (desc->apm_crossover && desc->apm_threshold && extra_corners) { + /* Program the APM crossover corner on the CPR-Hardened */ + thread->corners[total_corners].uV = desc->apm_crossover; + thread->corners[total_corners].min_uV = desc->apm_crossover; + thread->corners[total_corners].max_uV = desc->apm_crossover; + thread->corners[total_corners].is_open_loop = true; + + /* + * We have calculated the APM parameters for this clock plan: + * make the APM *threshold* available to external callers. + * The crossover is used only internally in the CPR. + */ + thread->ext_data.apm_threshold_uV = desc->apm_threshold; + + dev_dbg(drv->dev, "corner %d (APM): [%d %d %d] Open-Loop\n", + total_corners, desc->apm_crossover, + desc->apm_crossover, desc->apm_crossover); + + total_corners++; + extra_corners--; + } + + if (desc->mem_acc_threshold && extra_corners) { + /* Program the Memory Accelerator threshold corner to CPRh */ + thread->corners[total_corners].uV = desc->mem_acc_threshold; + thread->corners[total_corners].min_uV = desc->mem_acc_threshold; + thread->corners[total_corners].max_uV = desc->mem_acc_threshold; + thread->corners[total_corners].is_open_loop = true; + + /* + * We have calculated a mem-acc threshold for this clock plan: + * make it available to external callers. + */ + thread->ext_data.mem_acc_threshold_uV = desc->mem_acc_threshold; + + dev_dbg(drv->dev, "corner %d (MEMACC): [%d %d %d] Open-Loop\n", + total_corners, desc->mem_acc_threshold, + desc->mem_acc_threshold, desc->mem_acc_threshold); + + total_corners++; + extra_corners--; + } + + /* + * If there are any extra corners left, it means that even though we + * expect to fill in both APM and MEM-ACC crossovers, one couldn't + * satisfy requirements, which means that the specified parameters + * are wrong: in this case, inform the user and bail out, otherwise + * if we go on writing the (invalid) table to the CPR-Hardened, the + * hardware (in this case, the CPU) will surely freeze and crash. + */ + if (unlikely(extra_corners)) { + dev_err(drv->dev, "APM/MEM-ACC corners: bad parameters.\n"); + return -EINVAL; + } + /* Reassign extra_corners, as we have to exclude delta_quot for them */ + extra_corners = drv->extra_corners; + + /* Disable the interface between OSM and CPRh */ + cpr_masked_write(thread, drv->reg_ctl, + CPRH_CTL_OSM_ENABLED, 0); + + /* Program the GCNT before unmasking ring oscillator(s) */ + for (i = 0; i < CPR3_RO_COUNT; i++) { + if (!(ring_osc_mask & BIT(i))) { + cpr_write(thread, CPR3_REG_GCNT(i), drv->gcnt); + dev_vdbg(drv->dev, "RO%d gcnt=%d\n", i, drv->gcnt); + } + } + + /* + * Unmask the ring oscillator(s) that we're going to use: it seems + * to be mandatory to do this *before* sending the rest of the + * CPRhardened specific configuration. + */ + dev_dbg(drv->dev, "Unmasking ring oscillators with mask 0x%x\n", ring_osc_mask); + cpr_write(thread, CPR3_REG_RO_MASK(tdesc->hw_tid), ring_osc_mask); + + /* Setup minimum quotients for ring oscillators */ + for (i = 0; i < CPR3_RO_COUNT; i++) { + u32 tgt_quot_reg = CPR3_REG_TARGET_QUOT(tdesc->hw_tid, i); + u32 tgt_quot_val = 0; + + if (!(ring_osc_mask & BIT(i))) + tgt_quot_val = min_quotient; + + cpr_write(thread, tgt_quot_reg, tgt_quot_val); + dev_vdbg(drv->dev, "Programmed min quotient %u for Ring Oscillator %d\n", + tgt_quot_val, tgt_quot_reg); + } + + for (i = 0; i < total_corners; i++) { + int volt_oloop_steps, volt_floor_steps, delta_quot_steps; + int ring_osc; + u32 val; + + fnum = cdata[i].fuse_corner; + fuse = &thread->fuse_corners[fnum]; + + val = thread->corners[i].uV - desc->cpr_base_voltage; + volt_oloop_steps = DIV_ROUND_UP(val, drv->vreg_step); + + val = thread->corners[i].min_uV - desc->cpr_base_voltage; + volt_floor_steps = DIV_ROUND_UP(val, drv->vreg_step); + + /* + * If we are accessing corners that are not used as + * an active DCVS set-point, then always select RO 0 + * and zero out the delta quotient. + */ + if (i >= thread->num_corners) { + ring_osc = 0; + delta_quot_steps = 0; + } else { + ring_osc = fuse->ring_osc_idx; + val = fuse->quot - thread->corners[i].quot_adjust; + val -= min_quotient; + delta_quot_steps = DIV_ROUND_UP(val, + CPRH_DELTA_QUOT_STEP_FACTOR); + } + + if (volt_oloop_steps > CPRH_CORNER_INIT_VOLTAGE_MAX_VALUE || + volt_floor_steps > CPRH_CORNER_FLOOR_VOLTAGE_MAX_VALUE || + delta_quot_steps > CPRH_CORNER_QUOT_DELTA_MAX_VALUE) { + dev_err(drv->dev, "Invalid cfg: oloop=%d, floor=%d, delta=%d\n", + volt_oloop_steps, volt_floor_steps, + delta_quot_steps); + return -EINVAL; + } + /* Green light: Go, Go, Go! */ + + /* Set number of open-loop steps */ + val = volt_oloop_steps << CPRH_CORNER_INIT_VOLTAGE_SHIFT; + val &= CPRH_CORNER_INIT_VOLTAGE_MASK; + + /* Set number of floor voltage steps */ + val |= (volt_floor_steps << CPRH_CORNER_FLOOR_VOLTAGE_SHIFT) & + CPRH_CORNER_FLOOR_VOLTAGE_MASK; + + /* Set number of target quotient delta steps */ + val |= (delta_quot_steps << CPRH_CORNER_QUOT_DELTA_SHIFT) & + CPRH_CORNER_QUOT_DELTA_MASK; + + /* Select ring oscillator for this corner */ + val |= (ring_osc << CPRH_CORNER_RO_SEL_SHIFT) & + CPRH_CORNER_RO_SEL_MASK; + + /* Open loop corner is usually APM/ACC crossover */ + if (thread->corners[i].is_open_loop) { + dev_dbg(drv->dev, "Disabling Closed-Loop on corner %d\n", i); + val |= CPRH_CORNER_CPR_CL_DISABLE; + } + cpr_write(thread, CPRH_REG_CORNER(drv, tdesc->hw_tid, i), val); + + dev_dbg(drv->dev, "steps [%d]: open-loop %d, floor %d, delta_quot %d\n", + i, volt_oloop_steps, volt_floor_steps, + delta_quot_steps); + } + + /* YAY! Setup is done! Enable the internal loop to start CPR. */ + cpr_masked_write(thread, CPR3_REG_CPR_CTL, + CPR3_CPR_CTL_LOOP_EN_MASK, + CPR3_CPR_CTL_LOOP_EN_MASK); + + /* + * All the writes are going through before enabling internal + * communication between the OSM and the CPRh controllers + * because we are never using relaxed accessors, but should + * we use them, it would be critical to issue a barrier here, + * otherwise there is a high risk of hardware lockups due to + * under-voltage for the selected CPU clock. + * + * Please note that the CPR-hardened gets set-up in Linux but + * then gets actually used in firmware (and only by the OSM); + * after handing it off we will have no more control on it. + */ + + /* Enable the interface between OSM and CPRh */ + cpr_masked_write(thread, drv->reg_ctl, + CPRH_CTL_OSM_ENABLED, + CPRH_CTL_OSM_ENABLED); + + /* On success, free cdata manually */ + devm_kfree(drv->dev, cdata); + return 0; +} + +/** + * cpr3_init_parameters() - Initialize CPR global parameters + * @drv: Main driver structure + * + * Initial "integrity" checks and setup for the thread-independent parameters. + * + * Return: Zero for success, negative number on error + */ +static int cpr3_init_parameters(struct cpr_drv *drv) +{ + const struct cpr_desc *desc = drv->desc; + struct clk *clk; + + clk = devm_clk_get(drv->dev, "ref"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + drv->ref_clk_khz = clk_get_rate(clk); + do_div(drv->ref_clk_khz, 1000); + + /* On CPRh this clock is not always-on... */ + if (desc->cpr_type == CTRL_TYPE_CPRH) + clk_prepare_enable(clk); + else + devm_clk_put(drv->dev, clk); + + if (desc->timer_cons_up > CPR3_THRESH_CONS_UP_MASK || + desc->timer_cons_down > CPR3_THRESH_CONS_DOWN_MASK || + desc->up_threshold > CPR3_THRESH_UP_THRESH_MASK || + desc->down_threshold > CPR3_THRESH_DOWN_THRESH_MASK || + desc->idle_clocks > CPR3_CPR_CTL_IDLE_CLOCKS_MASK || + desc->count_mode > CPR3_CPR_CTL_COUNT_MODE_MASK || + desc->count_repeat > CPR3_CPR_CTL_COUNT_REPEAT_MASK) + return -EINVAL; + + /* + * Read the CPR version register only from CPR3 onwards: + * this is needed to get the additional register offsets. + * + * Note: When threaded, even if multi-controller, there + * is no chance to have different versions at the + * same time in the same domain, so it is safe to + * check this only on the first controller/thread. + */ + drv->cpr_hw_rev = cpr_read(&drv->threads[0], + CPR3_REG_CPR_VERSION); + dev_dbg(drv->dev, "CPR hardware revision: 0x%x\n", drv->cpr_hw_rev); + + if (drv->cpr_hw_rev >= CPRH_CPR_VERSION_4P5) { + drv->reg_corner = 0x3500; + drv->reg_corner_tid = 0xa0; + drv->reg_ctl = 0x3a80; + drv->reg_status = 0x3a84; + } else { + drv->reg_corner = 0x3a00; + drv->reg_corner_tid = 0; + drv->reg_ctl = 0x3aa0; + drv->reg_status = 0x3aa4; + } + + dev_dbg(drv->dev, "up threshold = %u, down threshold = %u\n", + desc->up_threshold, desc->down_threshold); + + return 0; +} + +/** + * cpr3_find_initial_corner() - Finds boot-up p-state and enables CPR + * @thread: Structure holding CPR thread-specific parameters + * + * Differently from CPRv1, from CPRv3 onwards when we successfully find + * the target boot-up performance state, we must refresh the HW + * immediately to guarantee system stability and to avoid overheating + * during the boot process, thing that would more likely happen without + * this driver doing its job. + * + * Return: Zero for success, negative number on error + */ +static int cpr3_find_initial_corner(struct cpr_thread *thread) +{ + struct cpr_drv *drv = thread->drv; + struct corner *corner; + int uV, idx; + + idx = cpr_find_initial_corner(drv->dev, thread->cpu_clk, + thread->corners, + thread->num_corners); + if (idx < 0) + return idx; + + cpr_ctl_disable(thread); + + corner = &thread->corners[idx]; + cpr_corner_restore(thread, corner); + + uV = regulator_get_voltage(drv->vreg); + uV = clamp(uV, corner->min_uV, corner->max_uV); + + corner->last_uV = uV; + if (!drv->last_uV) + drv->last_uV = uV; + + cpr_commit_state(thread); + thread->enabled = true; + cpr_switch(drv); + + return 0; +} + +static const int msm8998_gold_scaling_factor[][CPR3_RO_COUNT] = { + /* Fuse Corner 0 */ + { + 2857, 3057, 2828, 2952, 2699, 2798, 2446, 2631, + 2629, 2578, 2244, 3344, 3289, 3137, 3164, 2655 + }, + /* Fuse Corner 1 */ + { + 2857, 3057, 2828, 2952, 2699, 2798, 2446, 2631, + 2629, 2578, 2244, 3344, 3289, 3137, 3164, 2655 + }, + /* Fuse Corner 2 */ + { + 2603, 2755, 2676, 2777, 2573, 2685, 2465, 2610, + 2312, 2423, 2243, 3104, 3022, 3036, 2740, 2303 + }, + /* Fuse Corner 3 */ + { + 1901, 2016, 2096, 2228, 2034, 2161, 2077, 2188, + 1565, 1870, 1925, 2235, 2205, 2413, 1762, 1478 + } +}; + +static const int msm8998_silver_scaling_factor[][CPR3_RO_COUNT] = { + /* Fuse Corner 0 */ + { + 2595, 2794, 2577, 2762, 2471, 2674, 2199, 2553, + 3189, 3255, 3192, 2962, 3054, 2982, 2042, 2945 + }, + /* Fuse Corner 1 */ + { + 2595, 2794, 2577, 2762, 2471, 2674, 2199, 2553, + 3189, 3255, 3192, 2962, 3054, 2982, 2042, 2945 + }, + /* Fuse Corner 2 */ + { + 2391, 2550, 2483, 2638, 2382, 2564, 2259, 2555, + 2766, 3041, 2988, 2935, 2873, 2688, 2013, 2784 + }, + /* Fuse Corner 3 */ + { + 2066, 2153, 2300, 2434, 2220, 2386, 2288, 2465, + 2028, 2511, 2487, 2734, 2554, 2117, 1892, 2377 + } +}; + +static const struct cpr_thread_desc msm8998_thread_gold = { + .controller_id = 1, + .hw_tid = 0, + .ro_scaling_factor = msm8998_gold_scaling_factor, + .ro_avail_corners = ARRAY_SIZE(msm8998_gold_scaling_factor), + .sensor_range_start = 0, + .sensor_range_end = 9, + .init_voltage_step = 10000, + .init_voltage_width = 6, + .step_quot_init_min = 9, + .step_quot_init_max = 14, + .num_fuse_corners = 4, + .fuse_corner_data = (struct fuse_corner_data[]){ + /* fuse corner 0 */ + { + .ref_uV = 756000, + .max_uV = 828000, + .min_uV = 568000, + .range_uV = 32000, + .volt_cloop_adjust = 0, + .volt_oloop_adjust = 8000, + .max_volt_scale = 4, + .max_quot_scale = 10, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + /* fuse corner 1 */ + { + .ref_uV = 756000, + .max_uV = 900000, + .min_uV = 624000, + .range_uV = 32000, + .volt_cloop_adjust = 0, + .volt_oloop_adjust = 0, + .max_volt_scale = 320, + .max_quot_scale = 350, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + /* fuse corner 2 */ + { + .ref_uV = 828000, + .max_uV = 952000, + .min_uV = 632000, + .range_uV = 32000, + .volt_cloop_adjust = 12000, + .volt_oloop_adjust = 12000, + .max_volt_scale = 620, + .max_quot_scale = 750, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + /* fuse corner 3 */ + { + .ref_uV = 1056000, + .max_uV = 1136000, + .min_uV = 772000, + .range_uV = 40000, + .volt_cloop_adjust = 50000, + .volt_oloop_adjust = 52000, + .max_volt_scale = 580, + .max_quot_scale = 1040, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + }, +}; + +static const struct cpr_thread_desc msm8998_thread_silver = { + .controller_id = 0, + .hw_tid = 0, + .ro_scaling_factor = msm8998_silver_scaling_factor, + .ro_avail_corners = ARRAY_SIZE(msm8998_silver_scaling_factor), + .sensor_range_start = 0, + .sensor_range_end = 6, + .init_voltage_step = 10000, + .init_voltage_width = 6, + .step_quot_init_min = 11, + .step_quot_init_max = 12, + .num_fuse_corners = 4, + .fuse_corner_data = (struct fuse_corner_data[]){ + /* fuse corner 0 */ + { + .ref_uV = 688000, + .max_uV = 828000, + .min_uV = 568000, + .range_uV = 32000, + .volt_cloop_adjust = 20000, + .volt_oloop_adjust = 40000, + .max_volt_scale = 4, + .max_quot_scale = 10, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + /* fuse corner 1 */ + { + .ref_uV = 756000, + .max_uV = 900000, + .min_uV = 632000, + .range_uV = 32000, + .volt_cloop_adjust = 26000, + .volt_oloop_adjust = 24000, + .max_volt_scale = 500, + .max_quot_scale = 800, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + /* fuse corner 2 */ + { + .ref_uV = 828000, + .max_uV = 952000, + .min_uV = 664000, + .range_uV = 32000, + .volt_cloop_adjust = 12000, + .volt_oloop_adjust = 12000, + .max_volt_scale = 280, + .max_quot_scale = 650, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + + }, + /* fuse corner 3 */ + { + .ref_uV = 1056000, + .max_uV = 1056000, + .min_uV = 772000, + .range_uV = 40000, + .volt_cloop_adjust = 30000, + .volt_oloop_adjust = 30000, + .max_volt_scale = 430, + .max_quot_scale = 800, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + }, +}; + +static const struct cpr_desc msm8998_cpr_desc = { + .cpr_type = CTRL_TYPE_CPRH, + .num_threads = 2, + .mem_acc_threshold = 852000, + .apm_threshold = 800000, + .apm_crossover = 880000, + .apm_hysteresis = 0, + .cpr_base_voltage = 352000, + .cpr_max_voltage = 1200000, + .timer_delay_us = 5000, + .timer_cons_up = 0, + .timer_cons_down = 2, + .up_threshold = 2, + .down_threshold = 2, + .idle_clocks = 15, + .count_mode = CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_MIN, + .count_repeat = 14, + .gcnt_us = 1, + .vreg_step_fixed = 4000, + .vreg_step_up_limit = 1, + .vreg_step_down_limit = 1, + .vdd_settle_time_us = 34, + .corner_settle_time_us = 6, + .reduce_to_corner_uV = true, + .hw_closed_loop_en = true, + .threads = (const struct cpr_thread_desc *[]) { + &msm8998_thread_silver, + &msm8998_thread_gold, + }, +}; + +static const struct cpr_acc_desc msm8998_cpr_acc_desc = { + .cpr_desc = &msm8998_cpr_desc, +}; + +static const int sdm630_gold_scaling_factor[][CPR3_RO_COUNT] = { + /* Same RO factors for all fuse corners */ + { + 4040, 3230, 0, 2210, 2560, 2450, 2230, 2220, + 2410, 2300, 2560, 2470, 1600, 3120, 2620, 2280 + } +}; + +static const int sdm630_silver_scaling_factor[][CPR3_RO_COUNT] = { + /* Same RO factors for all fuse corners */ + { + 3600, 3600, 3830, 2430, 2520, 2700, 1790, 1760, + 1970, 1880, 2110, 2010, 2510, 4900, 4370, 4780, + } +}; + +static const struct cpr_thread_desc sdm630_thread_gold = { + .controller_id = 0, + .hw_tid = 0, + .ro_scaling_factor = sdm630_gold_scaling_factor, + .ro_avail_corners = ARRAY_SIZE(sdm630_gold_scaling_factor), + .sensor_range_start = 0, + .sensor_range_end = 6, + .init_voltage_step = 10000, + .init_voltage_width = 6, + .step_quot_init_min = 12, + .step_quot_init_max = 14, + .num_fuse_corners = 5, + .fuse_corner_data = (struct fuse_corner_data[]){ + /* fuse corner 0 */ + { + .ref_uV = 644000, + .max_uV = 724000, + .min_uV = 588000, + .range_uV = 40000, + .volt_cloop_adjust = -30000, + .volt_oloop_adjust = 15000, + .max_volt_scale = 10, + .max_quot_scale = 300, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + /* fuse corner 1 */ + { + .ref_uV = 788000, + .max_uV = 788000, + .min_uV = 652000, + .range_uV = 40000, + .volt_cloop_adjust = -30000, + .volt_oloop_adjust = 5000, + .max_volt_scale = 320, + .max_quot_scale = 275, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + /* fuse corner 2 */ + { + .ref_uV = 868000, + .max_uV = 868000, + .min_uV = 712000, + .range_uV = 40000, + .volt_cloop_adjust = -30000, + .volt_oloop_adjust = 5000, + .max_volt_scale = 350, + .max_quot_scale = 800, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + /* fuse corner 3 */ + { + .ref_uV = 988000, + .max_uV = 988000, + .min_uV = 784000, + .range_uV = 66000, + .volt_cloop_adjust = -30000, + .volt_oloop_adjust = 0, + .max_volt_scale = 868, + .max_quot_scale = 980, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + /* fuse corner 4 */ + { + .ref_uV = 1068000, + .max_uV = 1068000, + .min_uV = 844000, + .range_uV = 40000, + .volt_cloop_adjust = -30000, + .volt_oloop_adjust = 0, + .max_volt_scale = 868, + .max_quot_scale = 980, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + }, +}; + +static const struct cpr_thread_desc sdm630_thread_silver = { + .controller_id = 1, + .hw_tid = 0, + .ro_scaling_factor = sdm630_silver_scaling_factor, + .ro_avail_corners = ARRAY_SIZE(sdm630_silver_scaling_factor), + .sensor_range_start = 0, + .sensor_range_end = 6, + .init_voltage_step = 10000, + .init_voltage_width = 6, + .step_quot_init_min = 12, + .step_quot_init_max = 14, + .num_fuse_corners = 3, + .fuse_corner_data = (struct fuse_corner_data[]){ + /* fuse corner 0 */ + { + .ref_uV = 644000, + .max_uV = 724000, + .min_uV = 588000, + .range_uV = 32000, + .volt_cloop_adjust = -30000, + .volt_oloop_adjust = 0, + .max_volt_scale = 10, + .max_quot_scale = 360, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + /* fuse corner 1 */ + { + .ref_uV = 788000, + .max_uV = 788000, + .min_uV = 652000, + .range_uV = 40000, + .volt_cloop_adjust = -30000, + .volt_oloop_adjust = 0, + .max_volt_scale = 500, + .max_quot_scale = 550, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + /* fuse corner 2 */ + { + .ref_uV = 1068000, + .max_uV = 1068000, + .min_uV = 800000, + .range_uV = 40000, + .volt_cloop_adjust = -30000, + .volt_oloop_adjust = 0, + .max_volt_scale = 2370, + .max_quot_scale = 550, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + }, +}; + +static const struct cpr_desc sdm630_cpr_desc = { + .cpr_type = CTRL_TYPE_CPRH, + .num_threads = 2, + .apm_threshold = 872000, + .apm_crossover = 872000, + .apm_hysteresis = 20000, + .cpr_base_voltage = 400000, + .cpr_max_voltage = 1300000, + .timer_delay_us = 5000, + .timer_cons_up = 0, + .timer_cons_down = 2, + .up_threshold = 2, + .down_threshold = 2, + .idle_clocks = 15, + .count_mode = CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_MIN, + .count_repeat = 14, + .gcnt_us = 1, + .vreg_step_fixed = 4000, + .vreg_step_up_limit = 1, + .vreg_step_down_limit = 1, + .vdd_settle_time_us = 34, + .corner_settle_time_us = 5, + .reduce_to_corner_uV = true, + .hw_closed_loop_en = true, + .threads = (const struct cpr_thread_desc *[]) { + &sdm630_thread_gold, + &sdm630_thread_silver, + }, +}; + +static const struct cpr_acc_desc sdm630_cpr_acc_desc = { + .cpr_desc = &sdm630_cpr_desc, +}; + +static unsigned int cpr_get_performance_state(struct generic_pm_domain *genpd, + struct dev_pm_opp *opp) +{ + return dev_pm_opp_get_level(opp); +} + +static int cpr_power_off(struct generic_pm_domain *domain) +{ + struct cpr_thread *thread = container_of(domain, struct cpr_thread, pd); + + return cpr_disable(thread); +} + +static int cpr_power_on(struct generic_pm_domain *domain) +{ + struct cpr_thread *thread = container_of(domain, struct cpr_thread, pd); + + return cpr_enable(thread); +} + +static void cpr_pd_detach_dev(struct generic_pm_domain *domain, + struct device *dev) +{ + struct cpr_thread *thread = container_of(domain, struct cpr_thread, pd); + struct cpr_drv *drv = thread->drv; + + mutex_lock(&drv->lock); + + dev_dbg(drv->dev, "detach callback for: %s\n", dev_name(dev)); + thread->attached_cpu_dev = NULL; + + mutex_unlock(&drv->lock); +} + +static int cpr_pd_attach_dev(struct generic_pm_domain *domain, + struct device *dev) +{ + struct cpr_thread *thread = container_of(domain, struct cpr_thread, pd); + struct cpr_drv *drv = thread->drv; + const struct acc_desc *acc_desc = drv->acc_desc; + bool cprh_opp_remove_table = false; + int ret = 0; + + mutex_lock(&drv->lock); + + dev_dbg(drv->dev, "attach callback for: %s\n", dev_name(dev)); + + /* + * This driver only supports scaling voltage for a CPU cluster + * where all CPUs in the cluster share a single regulator. + * Therefore, save the struct device pointer only for the first + * CPU device that gets attached. There is no need to do any + * additional initialization when further CPUs get attached. + * This is not an error condition. + */ + if (thread->attached_cpu_dev) + goto unlock; + + /* + * cpr_scale_voltage() requires the direction (if we are changing + * to a higher or lower OPP). The first time + * cpr_set_performance_state() is called, there is no previous + * performance state defined. Therefore, we call + * cpr_find_initial_corner() that gets the CPU clock frequency + * set by the bootloader, so that we can determine the direction + * the first time cpr_set_performance_state() is called. + */ + thread->cpu_clk = devm_clk_get(dev, NULL); + if (drv->desc->cpr_type < CTRL_TYPE_CPRH && IS_ERR(thread->cpu_clk)) { + ret = PTR_ERR(thread->cpu_clk); + if (ret != -EPROBE_DEFER) + dev_err(drv->dev, "could not get cpu clk: %d\n", ret); + goto unlock; + } + thread->attached_cpu_dev = dev; + + /* + * We are exporting the APM and MEM-ACC thresholds to the caller; + * while APM is necessary in the CPU CPR case, MEM-ACC may not be, + * depending on the SoC and on fuses. + * Initialize both to an invalid value, so that the caller can check + * if they got calculated or read from fuses in this driver. + */ + thread->ext_data.apm_threshold_uV = -1; + thread->ext_data.mem_acc_threshold_uV = -1; + dev_set_drvdata(thread->attached_cpu_dev, &thread->ext_data); + + dev_dbg(drv->dev, "using cpu clk from: %s\n", + dev_name(thread->attached_cpu_dev)); + + /* + * Everything related to (virtual) corners has to be initialized + * here, when attaching to the power domain, since we need to know + * the maximum frequency for each fuse corner, and this is only + * available after the cpufreq driver has attached to us. + * The reason for this is that we need to know the highest + * frequency associated with each fuse corner. + */ + ret = dev_pm_opp_get_opp_count(&thread->pd.dev); + if (ret < 0) { + dev_err(drv->dev, "could not get OPP count\n"); + thread->attached_cpu_dev = NULL; + goto unlock; + } + thread->num_corners = ret; + + thread->corners = devm_kcalloc(drv->dev, + thread->num_corners + + drv->extra_corners, + sizeof(*thread->corners), + GFP_KERNEL); + if (!thread->corners) { + ret = -ENOMEM; + goto unlock; + } + + /* + * If we are on CPR-Hardened we have to make sure that the attached + * device has a OPP table installed, as we're going to modify it here + * with our calculations based on qfprom values. + */ + if (drv->desc->cpr_type == CTRL_TYPE_CPRH) { + ret = dev_pm_opp_of_add_table(dev); + if (ret && ret != -EEXIST) { + dev_err(drv->dev, "Cannot add table: %d\n", ret); + goto unlock; + } + cprh_opp_remove_table = true; + } + + ret = cpr3_corner_init(thread); + if (ret) + goto exit; + + if (drv->desc->cpr_type < CTRL_TYPE_CPRH) { + ret = cpr3_find_initial_corner(thread); + if (ret) + goto exit; + + if (acc_desc->config) + regmap_multi_reg_write(drv->tcsr, acc_desc->config, + acc_desc->num_regs_per_fuse); + + /* Enable ACC if required */ + if (acc_desc->enable_mask) + regmap_update_bits(drv->tcsr, acc_desc->enable_reg, + acc_desc->enable_mask, + acc_desc->enable_mask); + } + dev_info(drv->dev, "thread %d initialized with %u OPPs\n", + thread->id, thread->num_corners); +exit: + /* + * If we are on CPRh and we reached an error condition, we installed + * the OPP table but we haven't done any setup on it, nor we ever will. + * In order to leave a clean state, remove the table. + */ + if (ret && cprh_opp_remove_table) + dev_pm_opp_of_remove_table(thread->attached_cpu_dev); +unlock: + mutex_unlock(&drv->lock); + + return ret; +} + +static int cpr3_debug_info_show(struct seq_file *s, void *unused) +{ + u32 ro_sel, ctl, irq_status, reg, quot; + struct cpr_thread *thread = s->private; + struct corner *corner = thread->corners; + struct fuse_corner *fuse = thread->fuse_corners; + unsigned int i; + + const struct { + const char *name; + uint32_t mask; + uint8_t shift; + } result0_fields[] = { + { "busy", 1, 0 }, + { "step_dn", 1, 1 }, + { "step_up", 1, 2 }, + { "error_steps", CPR3_RESULT0_ERROR_STEPS_MASK, + CPR3_RESULT0_ERROR_STEPS_SHIFT }, + { "error", CPR3_RESULT0_ERROR_MASK, CPR3_RESULT0_ERROR_SHIFT }, + { "negative", 1, 20 }, + }, result1_fields[] = { + { "quot_min", CPR3_RESULT1_QUOT_MIN_MASK, + CPR3_RESULT1_QUOT_MIN_SHIFT }, + { "quot_max", CPR3_RESULT1_QUOT_MAX_MASK, + CPR3_RESULT1_QUOT_MAX_SHIFT }, + { "ro_min", CPR3_RESULT1_RO_MIN_MASK, + CPR3_RESULT1_RO_MIN_SHIFT }, + { "ro_max", CPR3_RESULT1_RO_MAX_MASK, + CPR3_RESULT1_RO_MAX_SHIFT }, + }, result2_fields[] = { + { "qout_step_min", CPR3_RESULT2_STEP_QUOT_MIN_MASK, + CPR3_RESULT2_STEP_QUOT_MIN_SHIFT }, + { "qout_step_max", CPR3_RESULT2_STEP_QUOT_MAX_MASK, + CPR3_RESULT2_STEP_QUOT_MAX_SHIFT }, + { "sensor_min", CPR3_RESULT2_SENSOR_MIN_MASK, + CPR3_RESULT2_SENSOR_MIN_SHIFT }, + { "sensor_max", CPR3_RESULT2_SENSOR_MAX_MASK, + CPR3_RESULT2_SENSOR_MAX_SHIFT }, + }; + + if (thread->drv->desc->cpr_type < CTRL_TYPE_CPRH) + seq_printf(s, "current_volt = %d uV\n", thread->drv->last_uV); + + irq_status = cpr_read(thread, CPR3_REG_IRQ_STATUS); + seq_printf(s, "irq_status = %#02X\n", irq_status); + + ctl = cpr_read(thread, CPR3_REG_CPR_CTL); + seq_printf(s, "cpr_ctl = %#02X\n", ctl); + + seq_printf(s, "thread %d - hw tid: %u - enabled: %d:\n", + thread->id, thread->desc->hw_tid, thread->enabled); + seq_printf(s, "%d corners, derived from %d fuse corners\n", + thread->num_corners, thread->desc->num_fuse_corners); + + for (i = 0; i < thread->num_corners; i++, corner++) + seq_printf(s, "corner %d - uV=[%d %d %d] quot=%d freq=%lu\n", + i, corner->min_uV, corner->uV, corner->max_uV, + corner->quot_adjust, corner->freq); + + for (i = 0; i < thread->desc->num_fuse_corners; i++, fuse++) + seq_printf(s, "fuse %d - uV=[%d %d %d] quot=%d freq=%lu\n", + i, fuse->min_uV, fuse->uV, fuse->max_uV, + fuse->quot, corner->freq); + + seq_printf(s, "requested voltage: %d uV\n", thread->corner->last_uV); + + ro_sel = corner->fuse_corner->ring_osc_idx; + quot = cpr_read(thread, CPR3_REG_TARGET_QUOT(i, ro_sel)); + seq_printf(s, "quot_target (%u) = %#02X\n", ro_sel, quot); + + reg = cpr_read(thread, CPR3_REG_RESULT0(i)); + seq_printf(s, "cpr_result_0 = %#02X\n [", reg); + for (i = 0; i < ARRAY_SIZE(result0_fields); i++) + seq_printf(s, "%s%s = %u", + i ? ", " : "", + result0_fields[i].name, + (reg >> result0_fields[i].shift) & + result0_fields[i].mask); + seq_puts(s, "]\n"); + reg = cpr_read(thread, CPR3_REG_RESULT1(i)); + seq_printf(s, "cpr_result_1 = %#02X\n [", reg); + for (i = 0; i < ARRAY_SIZE(result1_fields); i++) + seq_printf(s, "%s%s = %u", + i ? ", " : "", + result1_fields[i].name, + (reg >> result1_fields[i].shift) & + result1_fields[i].mask); + seq_puts(s, "]\n"); + reg = cpr_read(thread, CPR3_REG_RESULT2(i)); + seq_printf(s, "cpr_result_2 = %#02X\n [", reg); + for (i = 0; i < ARRAY_SIZE(result2_fields); i++) + seq_printf(s, "%s%s = %u", + i ? ", " : "", + result2_fields[i].name, + (reg >> result2_fields[i].shift) & + result2_fields[i].mask); + seq_puts(s, "]\n"); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(cpr3_debug_info); + +static void cpr3_debugfs_init(struct cpr_drv *drv) +{ + int i; + + drv->debugfs = debugfs_create_dir("qcom_cpr3", NULL); + + for (i = 0; i < drv->desc->num_threads; i++) { + char buf[50]; + + snprintf(buf, sizeof(buf), "thread%d", i); + + debugfs_create_file(buf, 0444, drv->debugfs, &drv->threads[i], + &cpr3_debug_info_fops); + } +} + +/** + * cpr_thread_init() - Initialize CPR thread related parameters + * @drv: Main driver structure + * @tid: Thread ID + * + * Return: Zero for success, negative number on error + */ +static int cpr_thread_init(struct cpr_drv *drv, int tid) +{ + const struct cpr_desc *desc = drv->desc; + const struct cpr_thread_desc *tdesc = desc->threads[tid]; + struct cpr_thread *thread = &drv->threads[tid]; + int ret; + + if (tdesc->step_quot_init_min > CPR3_CPR_STEP_QUOT_MIN_MASK || + tdesc->step_quot_init_max > CPR3_CPR_STEP_QUOT_MAX_MASK) + return -EINVAL; + + thread->id = tid; + thread->drv = drv; + thread->desc = tdesc; + thread->fuse_corners = devm_kcalloc(drv->dev, + tdesc->num_fuse_corners + + drv->extra_corners, + sizeof(*thread->fuse_corners), + GFP_KERNEL); + if (!thread->fuse_corners) + return -ENOMEM; + + thread->cpr_fuses = cpr_get_fuses(drv->dev, tid, + tdesc->num_fuse_corners); + if (IS_ERR(thread->cpr_fuses)) + return PTR_ERR(thread->cpr_fuses); + + ret = cpr_populate_ring_osc_idx(thread->drv->dev, thread->fuse_corners, + thread->cpr_fuses, + tdesc->num_fuse_corners); + if (ret) + return ret; + + ret = cpr_fuse_corner_init(thread); + if (ret) + return ret; + + thread->pd.name = devm_kasprintf(drv->dev, GFP_KERNEL, + "%s_thread%d", + drv->dev->of_node->full_name, + thread->id); + if (!thread->pd.name) + return -EINVAL; + + thread->pd.power_off = cpr_power_off; + thread->pd.power_on = cpr_power_on; + thread->pd.opp_to_performance_state = cpr_get_performance_state; + thread->pd.attach_dev = cpr_pd_attach_dev; + thread->pd.detach_dev = cpr_pd_detach_dev; + + /* CPR-Hardened performance states are managed in firmware */ + if (desc->cpr_type == CTRL_TYPE_CPRH) { + thread->pd.set_performance_state = cprh_dummy_set_performance_state; + } else { + thread->pd.set_performance_state = cpr_set_performance_state; + } + + /* Anything later than CPR1 must be always-on for now */ + thread->pd.flags = GENPD_FLAG_ALWAYS_ON; + + drv->cell_data.domains[tid] = &thread->pd; + + ret = pm_genpd_init(&thread->pd, NULL, false); + if (ret) + return ret; + + /* On CPRhardened, the interrupts are managed in firmware */ + if (desc->cpr_type < CTRL_TYPE_CPRH) { + INIT_WORK(&thread->restart_work, cpr_restart_worker); + + ret = devm_request_threaded_irq(drv->dev, drv->irq, + NULL, cpr_irq_handler, + IRQF_ONESHOT | + IRQF_TRIGGER_RISING, + "cpr", drv); + if (ret) + return ret; + } + + return 0; +} + +/** + * cpr3_resources_init() - Initialize resources used by this driver + * @pdev: Platform device + * @drv: Main driver structure + * + * Return: Zero for success, negative number on error + */ +static int cpr3_resources_init(struct platform_device *pdev, + struct cpr_drv *drv) +{ + const struct cpr_desc *desc = drv->desc; + struct cpr_thread *threads = drv->threads; + unsigned int i; + u8 cid_mask = 0; + + /* + * Here, we are accounting for the following usecases: + * - One controller + * - One or multiple threads on the same iospace + * + * - Multiple controllers + * - Each controller has its own iospace and each + * may have one or multiple threads in their + * parent controller's iospace + * + * Then, to avoid complicating the code for no reason, + * this also needs a mandatory order in the list of + * threads which implies that all of them from the same + * controllers are specified sequentially. As an example: + * + * C0-T0, C0-T1...C0-Tn, C1-T0, C1-T1...C1-Tn + */ + for (i = 0; i < desc->num_threads; i++) { + u8 cid = desc->threads[i]->controller_id; + + if (cid_mask & BIT(cid)) { + if (desc->threads[i - 1]->controller_id != cid) { + dev_err(drv->dev, "Bad threads order. Please fix!\n"); + return -EINVAL; + } + threads[i].base = threads[i - 1].base; + continue; + } + threads[i].base = devm_platform_ioremap_resource(pdev, cid); + if (IS_ERR(threads[i].base)) + return PTR_ERR(threads[i].base); + cid_mask |= BIT(cid); + } + return 0; +} + +static int cpr_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cpr_drv *drv; + const struct cpr_desc *desc; + const struct cpr_acc_desc *data; + struct device_node *np; + unsigned int i; + int ret; + + data = of_device_get_match_data(dev); + if (!data || !data->cpr_desc) + return -EINVAL; + + desc = data->cpr_desc; + + /* CPRh disallows MEM-ACC access from the HLOS */ + if (!data->acc_desc && desc->cpr_type < CTRL_TYPE_CPRH) + return -EINVAL; + + drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + + drv->dev = dev; + drv->desc = desc; + drv->threads = devm_kcalloc(dev, desc->num_threads, + sizeof(*drv->threads), GFP_KERNEL); + if (!drv->threads) + return -ENOMEM; + + drv->cell_data.num_domains = desc->num_threads; + drv->cell_data.domains = devm_kcalloc(drv->dev, + drv->cell_data.num_domains, + sizeof(*drv->cell_data.domains), + GFP_KERNEL); + if (!drv->cell_data.domains) + return -ENOMEM; + + if (data->acc_desc) + drv->acc_desc = data->acc_desc; + + mutex_init(&drv->lock); + + if (desc->cpr_type < CTRL_TYPE_CPRH) { + np = of_parse_phandle(dev->of_node, "acc-syscon", 0); + if (!np) + return -ENODEV; + + drv->tcsr = syscon_node_to_regmap(np); + of_node_put(np); + if (IS_ERR(drv->tcsr)) + return PTR_ERR(drv->tcsr); + } + + ret = cpr3_resources_init(pdev, drv); + if (ret) + return ret; + + drv->irq = platform_get_irq_optional(pdev, 0); + if ((desc->cpr_type != CTRL_TYPE_CPRH) && (drv->irq < 0)) + return -EINVAL; + + /* On CPRhardened, vreg access it not allowed */ + drv->vreg = devm_regulator_get_optional(dev, "vdd"); + if (desc->cpr_type != CTRL_TYPE_CPRH && IS_ERR(drv->vreg)) + return PTR_ERR(drv->vreg); + + /* + * On at least CPRhardened, vreg is unaccessible and there is no + * way to read linear step from that regulator, hence it is hardcoded + * in the driver; + * When the vreg_step is not declared in the cpr data (or is zero), + * then having access to the vreg regulator is mandatory, as this + * will be retrieved through the regulator API. + */ + if (desc->vreg_step_fixed) + drv->vreg_step = desc->vreg_step_fixed; + else + drv->vreg_step = regulator_get_linear_step(drv->vreg); + + if (!drv->vreg_step) + return -EINVAL; + + /* + * Initialize fuse corners, since it simply depends + * on data in efuses. + * Everything related to (virtual) corners has to be + * initialized after attaching to the power domain, + * since it depends on the CPU's OPP table. + */ + ret = nvmem_cell_read_variable_le_u32(dev, "cpr_fuse_revision", &drv->fusing_rev); + if (ret) + return ret; + + ret = nvmem_cell_read_variable_le_u32(dev, "cpr_speed_bin", &drv->speed_bin); + if (ret) + return ret; + + /* + * Some SoCs require extra corners for MEM-ACC or APM: if + * the related parameters have been specified, then reserve + * a corner for the APM and/or MEM-ACC crossover, used by + * OSM and CPRh HW to set the supply voltage during the APM + * and/or MEM-ACC switch routine. + */ + if (desc->cpr_type == CTRL_TYPE_CPRH) { + if (desc->apm_crossover && desc->apm_hysteresis >= 0) + drv->extra_corners++; + + if (desc->mem_acc_threshold) + drv->extra_corners++; + } + + /* Initialize all threads */ + for (i = 0; i < desc->num_threads; i++) { + ret = cpr_thread_init(drv, i); + if (ret) + return ret; + } + + /* Initialize global parameters */ + ret = cpr3_init_parameters(drv); + if (ret) + return ret; + + /* Write initial configuration on all threads */ + for (i = 0; i < desc->num_threads; i++) { + ret = cpr_configure(&drv->threads[i]); + if (ret) + return ret; + } + + ret = of_genpd_add_provider_onecell(dev->of_node, &drv->cell_data); + if (ret) + return ret; + + platform_set_drvdata(pdev, drv); + cpr3_debugfs_init(drv); + + return 0; +} + +static int cpr_remove(struct platform_device *pdev) +{ + struct cpr_drv *drv = platform_get_drvdata(pdev); + int i; + + of_genpd_del_provider(pdev->dev.of_node); + + for (i = 0; i < drv->desc->num_threads; i++) { + cpr_ctl_disable(&drv->threads[i]); + cpr_irq_set(&drv->threads[i], 0); + pm_genpd_remove(&drv->threads[i].pd); + } + + debugfs_remove_recursive(drv->debugfs); + + return 0; +} + +static const struct of_device_id cpr3_match_table[] = { + { .compatible = "qcom,msm8998-cprh", .data = &msm8998_cpr_acc_desc }, + { .compatible = "qcom,sdm630-cprh", .data = &sdm630_cpr_acc_desc }, + { } +}; +MODULE_DEVICE_TABLE(of, cpr3_match_table); + +static struct platform_driver cpr3_driver = { + .probe = cpr_probe, + .remove = cpr_remove, + .driver = { + .name = "qcom-cpr3", + .of_match_table = cpr3_match_table, + }, +}; +module_platform_driver(cpr3_driver) + +MODULE_DESCRIPTION("Core Power Reduction (CPR) v3/v4 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/soc/qcom/cpr.h b/include/soc/qcom/cpr.h new file mode 100644 index 000000000000..2ba4324d18f6 --- /dev/null +++ b/include/soc/qcom/cpr.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2013-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2019 Linaro Limited + * Copyright (c) 2021, AngeloGioacchino Del Regno + * + */ + +#ifndef __CPR_H__ +#define __CPR_H__ + +struct cpr_ext_data { + int mem_acc_threshold_uV; + int apm_threshold_uV; +}; + +#endif /* __CPR_H__ */ From 8d333acb7b03692ef16364d8de5ff86daafe22d5 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 25 Nov 2020 20:40:26 +0100 Subject: [PATCH 0248/1823] MAINTAINERS: Add entry for Qualcomm CPRv3/v4/Hardened driver Add maintainers entry for the Qualcomm CPR3/CPR4/CPRh driver. Signed-off-by: AngeloGioacchino Del Regno --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 379945f82a64..96139c6aa810 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17022,6 +17022,12 @@ S: Maintained F: Documentation/devicetree/bindings/power/avs/qcom,cpr.yaml F: drivers/soc/qcom/cpr.c +QUALCOMM CORE POWER REDUCTION v3/v4/Hardened AVS DRIVER +M: AngeloGioacchino Del Regno +S: Maintained +F: Documentation/devicetree/bindings/soc/qcom/qcom,cpr3.yaml +F: drivers/soc/qcom/cpr3.c + QUALCOMM CPUFREQ DRIVER MSM8996/APQ8096 M: Ilia Lin L: linux-pm@vger.kernel.org From d9a01e884d7d0e9b6ea7b0416e7ece011c88b958 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 25 Nov 2020 20:31:49 +0100 Subject: [PATCH 0249/1823] dt-bindings: soc: qcom: cpr3: Add bindings for CPR3 driver Add the bindings for the CPR3 driver to the documentation. Signed-off-by: AngeloGioacchino Del Regno Reviewed-by: Rob Herring --- .../bindings/soc/qcom/qcom,cpr3.yaml | 241 ++++++++++++++++++ 1 file changed, 241 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,cpr3.yaml diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,cpr3.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,cpr3.yaml new file mode 100644 index 000000000000..e2753740c86b --- /dev/null +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,cpr3.yaml @@ -0,0 +1,241 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/soc/qcom/qcom,cpr3.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Qualcomm Core Power Reduction v3/v4/Hardened (CPR3, CPR4, CPRh) + +description: | + CPR (Core Power Reduction) is a technology to reduce core power on a CPU + or other device. Each OPP of a device corresponds to a "corner" that has + a range of valid voltages for a particular frequency. While the device is + running at a particular frequency, CPR monitors dynamic factors such as + temperature, etc. and suggests or, in the CPR-Hardened case performs, + adjustments to the voltage to save power and meet silicon characteristic + requirements. + +maintainers: + - AngeloGioacchino Del Regno + +properties: + compatible: + oneOf: + - description: CPRv3 controller + items: + - const: qcom,cpr3 + - description: CPRv4 controller + items: + - const: qcom,cpr4 + - description: CPRv4-Hardened controller + items: + - enum: + - qcom,msm8998-cprh + - qcom,sdm630-cprh + - const: qcom,cprh + + reg: + description: Base address and size of the CPR controller(s) + minItems: 1 + maxItems: 2 + + interrupts: + maxItems: 1 + + clock-names: + items: + - const: "ref" + + clocks: + items: + - description: CPR reference clock + + vdd-supply: + description: Autonomous Phase Control (APC) or other power supply + + '#power-domain-cells': + const: 1 + + acc-syscon: + description: phandle to syscon for writing ACC settings + + nvmem-cells: + description: Cells containing the fuse corners and revision data + minItems: 10 + maxItems: 32 + + nvmem-cell-names: + minItems: 10 + maxItems: 32 + + operating-points-v2: true + +required: + - compatible + - reg + - clock-names + - clocks + - "#power-domain-cells" + - nvmem-cells + - nvmem-cell-names + - operating-points-v2 + +additionalProperties: false + +examples: + - | + #include + #include + + cpus { + #address-cells = <2>; + #size-cells = <0>; + + cpu@0 { + compatible = "qcom,kryo280"; + device_type = "cpu"; + reg = <0x0 0x0>; + operating-points-v2 = <&cpu_gold_opp_table>; + power-domains = <&apc_cprh 0>; + power-domain-names = "cprh"; + }; + + cpu@100 { + compatible = "qcom,kryo280"; + device_type = "cpu"; + reg = <0x0 0x0>; + operating-points-v2 = <&cpu_silver_opp_table>; + power-domains = <&apc_cprh 1>; + power-domain-names = "cprh"; + }; + }; + + cpu_silver_opp_table: cpu-silver-opp-table { + compatible = "operating-points-v2"; + opp-shared; + + opp-1843200000 { + opp-hz = /bits/ 64 <1843200000>; + required-opps = <&cprh_opp3>; + }; + opp-1094400000 { + opp-hz = /bits/ 64 <1094400000>; + required-opps = <&cprh_opp2>; + }; + opp-300000000 { + opp-hz = /bits/ 64 <300000000>; + required-opps = <&cprh_opp1>; + }; + }; + + cpu_gold_opp_table: cpu-gold-opp-table { + compatible = "operating-points-v2"; + opp-shared; + + opp-2208000000 { + opp-hz = /bits/ 64 <2208000000>; + required-opps = <&cprh_opp3>; + }; + opp-1113600000 { + opp-hz = /bits/ 64 <1113600000>; + required-opps = <&cprh_opp2>; + }; + opp-300000000 { + opp-hz = /bits/ 64 <300000000>; + required-opps = <&cprh_opp1>; + }; + }; + + cprh_opp_table: cpr-hardened-opp-table { + compatible = "operating-points-v2-qcom-level"; + + cprh_opp1: opp1 { + opp-level = <1>; + qcom,opp-fuse-level = <1>; + }; + cprh_opp2: opp2 { + opp-level = <2>; + qcom,opp-fuse-level = <2>; + }; + cprh_opp3: opp3 { + opp-level = <3>; + qcom,opp-fuse-level = <2 3>; + }; + }; + + apc_cprh: power-controller@179c8000 { + compatible = "qcom,msm8998-cprh", "qcom,cprh"; + reg = <0x0179c8000 0x4000>, <0x0179c4000 0x4000>; + clocks = <&gcc GCC_HMSS_RBCPR_CLK>; + clock-names = "ref"; + + #power-domain-cells = <1>; + operating-points-v2 = <&cprh_opp_table>; + + nvmem-cells = <&cpr_efuse_speedbin>, + <&cpr_fuse_revision>, + <&cpr_quot0_pwrcl>, + <&cpr_quot1_pwrcl>, + <&cpr_quot2_pwrcl>, + <&cpr_quot3_pwrcl>, + <&cpr_quot_offset1_pwrcl>, + <&cpr_quot_offset2_pwrcl>, + <&cpr_quot_offset3_pwrcl>, + <&cpr_init_voltage0_pwrcl>, + <&cpr_init_voltage1_pwrcl>, + <&cpr_init_voltage2_pwrcl>, + <&cpr_init_voltage3_pwrcl>, + <&cpr_ro_sel0_pwrcl>, + <&cpr_ro_sel1_pwrcl>, + <&cpr_ro_sel2_pwrcl>, + <&cpr_ro_sel3_pwrcl>, + <&cpr_quot0_perfcl>, + <&cpr_quot1_perfcl>, + <&cpr_quot2_perfcl>, + <&cpr_quot3_perfcl>, + <&cpr_quot_offset1_perfcl>, + <&cpr_quot_offset2_perfcl>, + <&cpr_quot_offset3_perfcl>, + <&cpr_init_voltage0_perfcl>, + <&cpr_init_voltage1_perfcl>, + <&cpr_init_voltage2_perfcl>, + <&cpr_init_voltage3_perfcl>, + <&cpr_ro_sel0_perfcl>, + <&cpr_ro_sel1_perfcl>, + <&cpr_ro_sel2_perfcl>, + <&cpr_ro_sel3_perfcl>; + + nvmem-cell-names = "cpr_speed_bin", + "cpr_fuse_revision", + "cpr0_quotient1", + "cpr0_quotient2", + "cpr0_quotient3", + "cpr0_quotient4", + "cpr0_quotient_offset2", + "cpr0_quotient_offset3", + "cpr0_quotient_offset4", + "cpr0_init_voltage1", + "cpr0_init_voltage2", + "cpr0_init_voltage3", + "cpr0_init_voltage4", + "cpr0_ring_osc1", + "cpr0_ring_osc2", + "cpr0_ring_osc3", + "cpr0_ring_osc4", + "cpr1_quotient1", + "cpr1_quotient2", + "cpr1_quotient3", + "cpr1_quotient4", + "cpr1_quotient_offset2", + "cpr1_quotient_offset3", + "cpr1_quotient_offset4", + "cpr1_init_voltage1", + "cpr1_init_voltage2", + "cpr1_init_voltage3", + "cpr1_init_voltage4", + "cpr1_ring_osc1", + "cpr1_ring_osc2", + "cpr1_ring_osc3", + "cpr1_ring_osc4"; + }; +... From c48720e33d9b8bfb1a3a6d7152d5ea0adccc328b Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Sat, 5 Dec 2020 17:53:51 +0100 Subject: [PATCH 0250/1823] arm64: dts: qcom: Enable panel etc. on MSM8998 Sony Yoshino platform (JAMI: fixup for 5.16 & 6.0-rc2) --- .../msm8998-sony-xperia-yoshino-maple.dts | 57 ++++++ .../msm8998-sony-xperia-yoshino-poplar.dts | 12 ++ .../dts/qcom/msm8998-sony-xperia-yoshino.dtsi | 176 +++++++++++++++++- 3 files changed, 244 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-maple.dts b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-maple.dts index 20fe0394a3c1..4c2cbae89193 100644 --- a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-maple.dts +++ b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-maple.dts @@ -26,6 +26,28 @@ }; }; +&dsi0 { + qcom,dual-dsi-mode; + qcom,master-dsi; +}; + +&dsi1 { + vdd-supply = <&vreg_l1a_0p875>; + vdda-supply = <&vreg_l2a_1p2>; + qcom,dual-dsi-mode; + status = "ok"; +}; + +&dsi1_out { + remote-endpoint = <&panel_in1>; + data-lanes = <0 1 2 3>; +}; + +&dsi1_phy { + vdds-supply = <&vreg_l1a_0p875>; + status = "ok"; +}; + &ibb { regulator-min-microvolt = <5600000>; regulator-max-microvolt = <5600000>; @@ -37,6 +59,32 @@ qcom,soft-start-us = <200>; }; +&panel { + compatible = "sharp,ls055d1sx04"; + + dvdd-supply = <&disp_dvdd_vreg>; + ports { + port@1 { + reg = <1>; + panel_in1: endpoint { + remote-endpoint = <&dsi1_out>; + }; + }; + }; +}; + +&pm8005_gpio { + ear_en_default: ear-en-active { + pins = "gpio1"; + function = PMIC_GPIO_FUNC_NORMAL; + output-low; + drive-push-pull; + bias-disable; + qcom,drive-strength = ; + power-source = <1>; + }; +}; + &pmi8998_gpio { disp_dvdd_en: disp-dvdd-en-active-state { pins = "gpio10"; @@ -53,3 +101,12 @@ regulator-min-microvolt = <2704000>; regulator-max-microvolt = <2704000>; }; + + +&pmi8998_wled { + status = "okay"; + + qcom,num-strings = <3>; + qcom,enabled-strings = <0 1 2>; +}; + diff --git a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-poplar.dts b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-poplar.dts index c21333aa73c2..055018b4eacc 100644 --- a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-poplar.dts +++ b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-poplar.dts @@ -25,6 +25,18 @@ qcom,soft-start-us = <800>; }; +&pm8005_gpio { + ear_en_default: ear-en-active { + pins = "gpio1"; + function = PMIC_GPIO_FUNC_NORMAL; + output-low; + drive-push-pull; + bias-disable; + qcom,drive-strength = ; + power-source = <1>; + }; +}; + &vreg_l18a_2p85 { /* Note: Round-down from 2850000 to be a multiple of PLDO step-size 8000 */ regulator-min-microvolt = <2848000>; diff --git a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi index d08639082247..65f007429d5e 100644 --- a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi @@ -18,6 +18,10 @@ qcom,msm-id = <0x124 0x20000>, <0x124 0x20001>; /* 8998v2, v2.1 */ qcom,board-id = <8 0>; + chosen { + bootargs = "clk_ignore_unused root=/dev/mmcblk0p1"; + }; + clocks { div1_mclk: divclk1 { compatible = "gpio-gate-clock"; @@ -80,6 +84,15 @@ pinctrl-0 = <&ts_vddio_en>; }; + /* The gpio-vibrator driver enforces requiring a regulator */ + vib_vreg: vib-regulator { + compatible = "regulator-fixed"; + regulator-name = "vib"; + + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + vph_pwr: vph-pwr-regulator { compatible = "regulator-fixed"; regulator-name = "vph_pwr"; @@ -188,11 +201,28 @@ vibrator { compatible = "gpio-vibrator"; enable-gpios = <&pmi8998_gpio 5 GPIO_ACTIVE_HIGH>; + vcc-supply = <&vib_vreg>; pinctrl-names = "default"; pinctrl-0 = <&vib_default>; }; }; +&adreno_gpu { + status = "ok"; + + zap-shader { + memory-region = <&zap_shader_region>; + }; +}; + +&adreno_smmu { + status = "ok"; +}; + +&apc_cprh { + status = "ok"; +}; + &blsp1_i2c5 { status = "okay"; clock-frequency = <355000>; @@ -257,7 +287,10 @@ reg = <0x29>; interrupt-parent = <&tlmm>; + /* Angelo: not 23? */ interrupts = <22 IRQ_TYPE_EDGE_FALLING>; + //#io-channel-cells = <1>; + //label = "back_camera_tof"; reset-gpios = <&tlmm 27 GPIO_ACTIVE_LOW>; vdd-supply = <&cam_vio_vreg>; @@ -267,6 +300,62 @@ }; }; +&cpufreq_hw { + status = "ok"; +}; + +&dsi0 { + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + vdd-supply = <&vreg_l1a_0p875>; + vdda-supply = <&vreg_l2a_1p2>; + + panel: panel@0 { + reg = <0>; + + backlight = <&pmi8998_wled>; + disp-te-gpios = <&tlmm 10 GPIO_ACTIVE_HIGH>; + reset-gpios = <&tlmm 94 GPIO_ACTIVE_HIGH>; + + avdd-supply = <&lab>; + avee-supply = <&ibb>; + vddio-supply = <&vreg_l14a_1p85>; + tavdd-supply = <&vreg_l28_3p0>; + tvddio-supply = <&touch_vddio_vreg>; + + pinctrl-names = "default"; + pinctrl-0 = <&panel_reset_n &mdp_vsync_n>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + panel_in0: endpoint { + remote-endpoint = <&dsi0_out>; + }; + }; + }; + }; +}; + +&dsi0_out { + remote-endpoint = <&panel_in0>; + data-lanes = <0 1 2 3>; +}; + +&dsi0_phy { + status = "okay"; + vdds-supply = <&vreg_l1a_0p875>; +}; + +&gpucc { + status = "ok"; +}; + &ibb { regulator-min-microamp = <800000>; regulator-max-microamp = <800000>; @@ -292,10 +381,35 @@ regulator-soft-start; }; +&mmcc { + status = "ok"; +}; + +&mmss_smmu { + status = "ok"; +}; + +&mdss { + status = "okay"; +}; + +&mmcc { + status = "ok"; +}; + +&mmss_smmu { + status = "ok"; +}; + &pm8005_regulators { /* VDD_GFX supply */ pm8005_s1: s1 { - regulator-min-microvolt = <524000>; + /* + * HACK: Set enough voltage for max GPU frequency + * and set the regulator always on until the + * GPU Core Power Reduction gets available + */ + regulator-min-microvolt = <988000>; regulator-max-microvolt = <1088000>; regulator-enable-ramp-delay = <500>; /* Hack until we rig up the gpu consumer */ @@ -333,6 +447,13 @@ function = "func2"; power-source = <0>; }; + + nfc_clk_req_pin: nfc-clk-req-active { + pins = "gpio21"; + function = PMIC_GPIO_FUNC_NORMAL; + input-enable; + power-source = <1>; + }; }; &pmi8998_gpio { @@ -367,6 +488,18 @@ }; }; +&pmi8998_wled { + status = "okay"; + + default-brightness = <3000>; + qcom,switching-freq = <800>; + qcom,ovp-millivolt = <29600>; + qcom,current-boost-limit = <970>; + qcom,current-limit-microamp = <25000>; + qcom,num-strings = <2>; + qcom,enabled-strings = <0 1>; +}; + &qusb2phy { status = "okay"; @@ -374,6 +507,18 @@ vdda-phy-dpdm-supply = <&vreg_l24a_3p075>; }; +&remoteproc_adsp { + firmware-name = "adsp.mdt"; +}; + +&remoteproc_mss { + status = "disabled"; +}; + +&remoteproc_slpi { + firmware-name = "slpi.mdt"; +}; + &rpm_requests { pm8998-regulators { compatible = "qcom,rpm-pm8998-regulators"; @@ -642,6 +787,35 @@ function = "gpio"; bias-disable; drive-strength = <2>; + //output-low; + }; + + cam1_rst_default: cam1-rst-n { + pins = "gpio28"; + function = "gpio"; + bias-disable; + drive-strength = <2>; + }; + + cam0_rst_default: cam0-rst-n { + pins = "gpio30"; + function = "gpio"; + bias-disable; + drive-strength = <2>; + }; + + ts_reset_n: ts-reset-n { + pins = "gpio89"; + function = "gpio"; + bias-pull-up; + drive-strength = <8>; + }; + + panel_reset_n: panel-rst-n { + pins = "gpio94"; + function = "gpio"; + bias-disable; + drive-strength = <2>; }; hall_sensor0_default: acc-cover-open { From 7c3825a968dae0add9148d5bcbd2016c00f7ee44 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Sat, 5 Dec 2020 12:45:50 +0100 Subject: [PATCH 0251/1823] arm64: dts: msm8998: Add disabled configuration for DPU1/DSI This SoC supports both the MDP5 and DPU1 drivers, but the latter was chosen as it's more feature-complete; Configure the DPU1, DSI and related phy and pll in order to achieve display functionality and keep it disabled. Enabling it will be done on board specific DT when needed, as not all boards have a usable display attached to them. Signed-off-by: AngeloGioacchino Del Regno --- arch/arm64/boot/dts/qcom/msm8998.dtsi | 264 +++++++++++++++++++++++++- 1 file changed, 260 insertions(+), 4 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi index f05f16ac5cc1..c1b0048f56e4 100644 --- a/arch/arm64/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi @@ -2401,16 +2401,272 @@ "core_bi_pll_test_se"; clocks = <&rpmcc RPM_SMD_XO_CLK_SRC>, <&gcc GCC_MMSS_GPLL0_CLK>, - <0>, - <0>, - <0>, - <0>, + <&dsi0_phy 1>, + <&dsi0_phy 0>, + <&dsi1_phy 1>, + <&dsi1_phy 0>, <0>, <0>, <0>, <0>; }; + dsi_opp_table: dsi-opp-table { + compatible = "operating-points-v2"; + + opp-131250000 { + opp-hz = /bits/ 64 <131250000>; + required-opps = <&rpmpd_opp_low_svs>; + }; + + opp-210000000 { + opp-hz = /bits/ 64 <210000000>; + required-opps = <&rpmpd_opp_svs>; + }; + + opp-312500000 { + opp-hz = /bits/ 64 <312500000>; + required-opps = <&rpmpd_opp_nom>; + }; + }; + + mdss: mdss@c900000 { + compatible = "qcom,sdm845-mdss"; + reg = <0x0c900000 0x1000>; + reg-names = "mdss"; + + clocks = <&mmcc MDSS_AHB_CLK>, + <&mmcc MDSS_AXI_CLK>, + <&mmcc MDSS_MDP_CLK>; + clock-names = "iface", "bus", "core"; + + assigned-clocks = <&mmcc MDSS_MDP_CLK>; + assigned-clock-rates = <300000000>; + interrupts = ; + interrupt-controller; + iommus = <&mmss_smmu 0>; + #interrupt-cells = <1>; + #address-cells = <1>; + #size-cells = <1>; + power-domains = <&mmcc MDSS_GDSC>; + ranges; + status = "disabled"; + + mdss_mdp: mdp@c901000 { + compatible = "qcom,msm8998-dpu"; + reg = <0x0c901000 0x8f000>, + <0x0c9a8e00 0xf0>, + <0x0c9b0000 0x2008>, + <0x0c9b8000 0x1040>; + reg-names = "mdp", "regdma", "vbif", + "vbif_nrt"; + + assigned-clocks = <&mmcc MDSS_MDP_CLK>, + <&mmcc MDSS_VSYNC_CLK>; + assigned-clock-rates = <412500000>, + <19200000>; + + clocks = <&mmcc MDSS_AHB_CLK>, + <&mmcc MDSS_AXI_CLK>, + <&mmcc MNOC_AHB_CLK>, + <&mmcc MDSS_MDP_CLK>, + <&mmcc MDSS_VSYNC_CLK>; + clock-names = "iface", "bus", "mnoc", + "core", "vsync"; + + interrupt-parent = <&mdss>; + interrupts = <0 IRQ_TYPE_LEVEL_HIGH>; + operating-points-v2 = <&mdp_opp_table>; + power-domains = <&rpmpd MSM8998_VDDMX>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + dpu_intf1_out: endpoint { + remote-endpoint = <&dsi0_in>; + }; + }; + + port@1 { + reg = <1>; + dpu_intf2_out: endpoint { + remote-endpoint = <&dsi1_in>; + }; + }; + }; + + mdp_opp_table: mdp-opp-table { + compatible = "operating-points-v2"; + + opp-19200000 { + opp-hz = /bits/ 64 <19200000>; + required-opps = <&rpmpd_opp_min_svs>; + }; + + opp-150000000 { + opp-hz = /bits/ 64 <150000000>; + required-opps = <&rpmpd_opp_low_svs>; + }; + + opp-275000000 { + opp-hz = /bits/ 64 <275000000>; + required-opps = <&rpmpd_opp_svs>; + }; + + opp-330000000 { + opp-hz = /bits/ 64 <330000000>; + required-opps = <&rpmpd_opp_nom>; + }; + + opp-412500000 { + opp-hz = /bits/ 64 <412500000>; + required-opps = <&rpmpd_opp_turbo>; + }; + }; + }; + + dsi0: dsi@c994000 { + compatible = "qcom,mdss-dsi-ctrl"; + reg = <0x0c994000 0x400>; + reg-names = "dsi_ctrl"; + + clocks = <&mmcc MDSS_BYTE0_CLK>, + <&mmcc MDSS_BYTE0_INTF_CLK>, + <&mmcc MNOC_AHB_CLK>, + <&mmcc MDSS_AHB_CLK>, + <&mmcc MDSS_AXI_CLK>, + <&mmcc MISC_AHB_CLK>, + <&mmcc MDSS_PCLK0_CLK>, + <&mmcc MDSS_ESC0_CLK>; + clock-names = "byte", + "byte_intf", + "mnoc", + "iface", + "bus", + "core_mmss", + "pixel", + "core"; + + interrupt-parent = <&mdss>; + interrupts = <4 IRQ_TYPE_LEVEL_HIGH>; + operating-points-v2 = <&dsi_opp_table>; + phys = <&dsi0_phy>; + phy-names = "dsi"; + power-domains = <&rpmpd MSM8998_VDDCX>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + dsi0_in: endpoint { + remote-endpoint = <&dpu_intf1_out>; + }; + }; + + port@1 { + reg = <1>; + dsi0_out: endpoint { + }; + }; + }; + }; + + dsi0_phy: dsi-phy@c994400 { + compatible = "qcom,dsi-phy-10nm-8998"; + reg = <0x0c994400 0x200>, + <0x0c994600 0x280>, + <0x0c994a00 0x1c0>; + reg-names = "dsi_phy", + "dsi_phy_lane", + "dsi_pll"; + + #clock-cells = <1>; + #phy-cells = <0>; + + clocks = <&mmcc MDSS_AHB_CLK>, + <&rpmcc RPM_SMD_XO_CLK_SRC>; + clock-names = "iface", "ref"; + power-domains = <&rpmpd MSM8998_VDDMX>; + + status = "disabled"; + }; + + dsi1: dsi@c996000 { + compatible = "qcom,mdss-dsi-ctrl"; + reg = <0x0c996000 0x400>; + reg-names = "dsi_ctrl"; + + clocks = <&mmcc MDSS_BYTE1_CLK>, + <&mmcc MDSS_BYTE1_INTF_CLK>, + <&mmcc MNOC_AHB_CLK>, + <&mmcc MISC_AHB_CLK>, + <&mmcc MDSS_PCLK1_CLK>, + <&mmcc MDSS_ESC1_CLK>, + <&mmcc MDSS_AHB_CLK>, + <&mmcc MDSS_AXI_CLK>; + clock-names = "byte", + "byte_intf", + "mnoc", + "iface_mmss", + "pixel", + "core", + "iface", + "bus"; + + interrupt-parent = <&mdss>; + interrupts = <5 IRQ_TYPE_LEVEL_HIGH>; + operating-points-v2 = <&dsi_opp_table>; + phys = <&dsi1_phy>; + phy-names = "dsi"; + power-domains = <&rpmpd MSM8998_VDDCX>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + dsi1_in: endpoint { + remote-endpoint = <&dpu_intf2_out>; + }; + }; + + port@1 { + reg = <1>; + dsi1_out: endpoint { + }; + }; + }; + }; + + dsi1_phy: dsi-phy@c996400 { + compatible = "qcom,dsi-phy-10nm-8998"; + reg = <0x0c996400 0x200>, + <0x0c996600 0x280>, + <0x0c996a00 0x10e>; + reg-names = "dsi_phy", + "dsi_phy_lane", + "dsi_pll"; + + #clock-cells = <1>; + #phy-cells = <0>; + + clocks = <&mmcc MDSS_AHB_CLK>, + <&rpmcc RPM_SMD_XO_CLK_SRC>; + clock-names = "iface", "ref"; + power-domains = <&rpmpd MSM8998_VDDMX>; + + status = "disabled"; + }; + }; + mmss_smmu: iommu@cd00000 { compatible = "qcom,msm8998-smmu-v2", "qcom,smmu-v2"; reg = <0x0cd00000 0x40000>; From ad14529669f2447416b3432e1e4111dc734c48f8 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 4 Jan 2021 18:17:58 +0100 Subject: [PATCH 0252/1823] arm64: dts: msm8998: Add interconnect nodes This SoC features Network-on-Chip (NoC) and Bus Integrated Memory Controller (BIMC) interconnects: add the required nodes now that the driver is present. Signed-off-by: AngeloGioacchino Del Regno --- arch/arm64/boot/dts/qcom/msm8998.dtsi | 68 +++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi index c1b0048f56e4..26a77d5959fe 100644 --- a/arch/arm64/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -843,6 +844,15 @@ }; }; + bimc: interconnect@1008000 { + compatible = "qcom,msm8998-bimc"; + reg = <0x01008000 0x78000>; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&rpmcc RPM_SMD_BIMC_CLK>, + <&rpmcc RPM_SMD_BIMC_A_CLK>; + }; + tsens0: thermal@10ab000 { compatible = "qcom,msm8998-tsens", "qcom,tsens-v2"; reg = <0x010ab000 0x1000>, /* TM */ @@ -865,6 +875,33 @@ #thermal-sensor-cells = <1>; }; + cnoc: interconnect@1500000 { + compatible = "qcom,msm8998-cnoc"; + reg = <0x01500000 0x10000>; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&rpmcc RPM_SMD_CNOC_CLK>, + <&rpmcc RPM_SMD_CNOC_A_CLK>; + }; + + snoc: interconnect@1625000 { + compatible = "qcom,msm8998-snoc"; + reg = <0x01625000 0x6100>; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&rpmcc RPM_SMD_SNOC_CLK>, + <&rpmcc RPM_SMD_SNOC_A_CLK>; + }; + + a1noc: interconnect@1669000 { + compatible = "qcom,msm8998-a1noc"; + reg = <0x01669000 0x5020>; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&rpmcc RPM_SMD_AGGR1_NOC_CLK>, + <&rpmcc RPM_SMD_AGGR1_NOC_A_CLK>; + }; + anoc1_smmu: iommu@1680000 { compatible = "qcom,msm8998-smmu-v2", "qcom,smmu-v2"; reg = <0x01680000 0x10000>; @@ -899,6 +936,25 @@ ; }; + a2noc: interconnect@1705000 { + compatible = "qcom,msm8998-a2noc"; + reg = <0x01705000 0xa090>; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&rpmcc RPM_SMD_AGGR2_NOC_CLK>, + <&rpmcc RPM_SMD_AGGR2_NOC_A_CLK>; + }; + + mnoc: interconnect@1744000 { + compatible = "qcom,msm8998-mnoc"; + reg = <0x01744000 0xb010>; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a", "iface"; + clocks = <&rpmcc RPM_SMD_MMAXI_CLK>, + <&rpmcc RPM_SMD_MMAXI_A_CLK>, + <&mmcc AHB_CLK_SRC>; + }; + pcie0: pci@1c00000 { compatible = "qcom,pcie-msm8996"; reg = <0x01c00000 0x2000>, @@ -2736,6 +2792,18 @@ }; }; + gnoc: interconnect@17900000 { + compatible = "qcom,msm8998-gnoc"; + reg = <0x17900000 0xe000>; + #interconnect-cells = <1>; + /* + * This one apparently features no clocks, + * so let's not mess with the driver needlessly + */ + clock-names = "bus", "bus_a"; + clocks = <&xo>, <&xo>; + }; + apcs_glb: mailbox@17911000 { compatible = "qcom,msm8998-apcs-hmss-global"; reg = <0x17911000 0x1000>; From 62b32a69730b0ca78e40ab71650e78dd181ef056 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 20 Jan 2021 15:27:42 +0100 Subject: [PATCH 0253/1823] arm64: dts: msm8998: Wire up interconnects and OPPs to sdhci port 2 Wire up the OPP table and interconnects to the SDHCI port 2 to improve performance and power consumption. Signed-off-by: AngeloGioacchino Del Regno --- arch/arm64/boot/dts/qcom/msm8998.dtsi | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi index 26a77d5959fe..3389957032dd 100644 --- a/arch/arm64/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi @@ -2136,6 +2136,7 @@ reg = <0x0c0a4900 0x314>, <0x0c0a4000 0x800>; reg-names = "hc", "core"; + interconnects = <&a2noc MASTER_SDCC_2 &a2noc SLAVE_A2NOC_SNOC>; interrupts = , ; interrupt-names = "hc_irq", "pwr_irq"; @@ -2146,6 +2147,26 @@ <&xo>; bus-width = <4>; status = "disabled"; + + sdhc2_opp_table: sdhc2-opp-table { + compatible = "operating-points-v2"; + + opp-50000000 { + opp-hz = /bits/ 64 <50000000>; + opp-peak-kBps = <400000>; + opp-avg-kBps = <200000>; + }; + opp-100000000 { + opp-hz = /bits/ 64 <100000000>; + opp-peak-kBps = <800000>; + opp-avg-kBps = <400000>; + }; + opp-200000000 { + opp-hz = /bits/ 64 <200000000>; + opp-peak-kBps = <800000>; + opp-avg-kBps = <800000>; + }; + }; }; blsp1_dma: dma-controller@c144000 { From 5633d34633e3970b6a7a81705ccf958ce2d9237c Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Mon, 14 Dec 2020 17:19:11 +0100 Subject: [PATCH 0254/1823] arm64: dts: qcom: pm8998: Add VREF_1P25 and REF_GND VADC channels Signed-off-by: Konrad Dybcio --- arch/arm64/boot/dts/qcom/pm8998.dtsi | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/pm8998.dtsi b/arch/arm64/boot/dts/qcom/pm8998.dtsi index d09f2954b6f9..ed68f44a7f11 100644 --- a/arch/arm64/boot/dts/qcom/pm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/pm8998.dtsi @@ -78,6 +78,16 @@ #size-cells = <0>; #io-channel-cells = <1>; + adc-chan@0 { + reg = ; + label = "ref_gnd"; + }; + + adc-chan@1 { + reg = ; + label = "vref_1p25"; + }; + adc-chan@6 { reg = ; label = "die_temp"; From 4f3aac898264fd64dd491d77cb763d1d3b032188 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Fri, 4 Dec 2020 23:04:52 +0100 Subject: [PATCH 0255/1823] arm64: dts: qcom: Enable panel etc. on MSM8998 F(x)tec Pro1 QX1000 (JAMI: fixup for 5.16 & 6.0-rc2) --- .../boot/dts/qcom/msm8998-fxtec-pro1.dts | 287 ++++++++++++++++++ 1 file changed, 287 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/msm8998-fxtec-pro1.dts b/arch/arm64/boot/dts/qcom/msm8998-fxtec-pro1.dts index 429ba57e20f7..76a44d9049c9 100644 --- a/arch/arm64/boot/dts/qcom/msm8998-fxtec-pro1.dts +++ b/arch/arm64/boot/dts/qcom/msm8998-fxtec-pro1.dts @@ -25,6 +25,15 @@ serial1 = &blsp1_uart3; }; + /* This part enables graphical output via bootloader-enabled display */ + chosen { + bootargs = "earlycon=tty0 console=tty0 clk_ignore_unused"; + + #address-cells = <2>; + #size-cells = <2>; + ranges; + }; + /* * Until we hook up type-c detection, we * have to stick with this. But it works. @@ -53,6 +62,8 @@ gpio-kb-extra-keys { compatible = "gpio-keys"; label = "Keyboard extra keys"; + #address-cells = <1>; + #size-cells = <0>; pinctrl-names = "default"; pinctrl-0 = <&gpio_kb_pins_extra>; @@ -105,6 +116,39 @@ }; }; + gpio_keyboard: gpio-keyboard { + compatible = "gpio-fastmatrix-keyboard"; + label = "F(x)Tec Pro1 Hardware Keyboard"; + row-gpios = + <&gpioext0 0 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>, + <&gpioext0 1 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>, + <&gpioext0 2 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>, + <&gpioext0 3 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>, + <&gpioext0 4 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>, + <&gpioext0 5 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>, + <&gpioext0 6 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>, + <&gpioext0 7 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>; + + col-gpios = + <&gpioext0 8 GPIO_ACTIVE_LOW>, + <&gpioext0 9 GPIO_ACTIVE_LOW>, + <&gpioext0 10 GPIO_ACTIVE_LOW>, + <&gpioext0 11 GPIO_ACTIVE_LOW>, + <&gpioext0 12 GPIO_ACTIVE_LOW>, + <&gpioext0 13 GPIO_ACTIVE_LOW>, + <&gpioext0 14 GPIO_ACTIVE_LOW>, + <&gpioext0 15 GPIO_ACTIVE_LOW>; + keypad,num-rows = <8>; + keypad,num-columns = <8>; + + pinctrl-names = "default"; + pinctrl-0 = <&keyboard_pins_col>, <&keyboard_pins_row>; + + autorescan-ms = <5>; + debounce-delay-ms = <1>; + col-scan-delay-us = <1500>; + }; + gpio-keys { compatible = "gpio-keys"; label = "Side buttons"; @@ -182,6 +226,53 @@ }; }; + disp_vcc_vreg: disp-vcc-regulator { + compatible = "regulator-fixed"; + regulator-name = "disp_vcc"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + disp_vddio_vreg: disp-vddio-regulator { + compatible = "regulator-fixed"; + regulator-name = "disp_vddio"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + regulator-allow-set-load; + }; + + disp_vci_vreg: disp-vci-regulator { + compatible = "regulator-fixed"; + regulator-name = "disp_vci"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + regulator-boot-on; + regulator-always-on; + regulator-allow-set-load; + }; + + disp_elvdd_vreg: disp-elvdd-regulator { + compatible = "regulator-fixed"; + regulator-name = "disp_elvdd"; + regulator-min-microvolt = <4600000>; + regulator-max-microvolt = <4600000>; + regulator-boot-on; + regulator-always-on; + }; + + disp_elvss_vreg: disp-elvss-regulator { + compatible = "regulator-fixed"; + regulator-name = "disp_elvss"; + regulator-min-microvolt = <2400000>; + regulator-max-microvolt = <2400000>; + regulator-boot-on; + regulator-always-on; + }; + ts_vio_vreg: ts-vio-vreg { compatible = "regulator-fixed"; regulator-name = "ts_vio_reg"; @@ -241,8 +332,61 @@ status = "okay"; }; +&adreno_gpu { + status = "okay"; + + zap-shader { + memory-region = <&zap_shader_region>; + }; +}; + +&adreno_smmu { + status = "okay"; +}; + +&apc_cprh { + status = "okay"; +}; + +&blsp1_i2c6 { + status = "okay"; + + gpioext0: gpio-expander@58 { + compatible = "awinic,aw9523-pinctrl"; + reg = <0x58>; + interrupt-parent = <&tlmm>; + interrupts = <50 IRQ_TYPE_EDGE_FALLING>; + + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&gpioext0 0 0 16>; + interrupt-controller; + #interrupt-cells = <2>; + + pinctrl-0 = <&gpio_expander_int_n>, <&gpio_expander_rst_n>; + pinctrl-names = "default"; + reset-gpios = <&tlmm 51 GPIO_ACTIVE_HIGH>; + + keyboard_pins_col: keyboard-matrix-col-pins { + pins = "gpio8", "gpio9", "gpio10", "gpio11", + "gpio12", "gpio13", "gpio14", "gpio15"; + function = "gpio"; + output-low; + }; + keyboard_pins_row: keyboard-matrix-row-pins { + pins = "gpio0", "gpio1", "gpio2", "gpio3", + "gpio4", "gpio5", "gpio6", "gpio7"; + function = "gpio"; + drive-open-drain; + input-enable; + }; + }; +}; + &blsp2_i2c1 { status = "okay"; + qcom,noise-reject-scl = <3>; + qcom,noise-reject-sda = <3>; touchscreen@14 { compatible = "goodix,gt9286"; @@ -257,6 +401,108 @@ }; }; +&cpufreq_hw { + status = "okay"; +}; + +&dsi0 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + vdd-supply = <&vreg_l1a_0p875>; + vdda-supply = <&vreg_l2a_1p2>; + + panel: panel@0 { + compatible = "boe,bf060y8m-aj0"; + reg = <0>; + + reset-gpios = <&tlmm 94 GPIO_ACTIVE_LOW>; + disp-te-gpios = <&tlmm 10 GPIO_ACTIVE_LOW>; + no-hpd; + + vcc-supply = <&disp_vcc_vreg>; + vddio-supply = <&disp_vddio_vreg>; + vci-supply = <&disp_vci_vreg>; + elvdd-supply = <&disp_elvdd_vreg>; + elvss-supply = <&disp_elvss_vreg>; + + pinctrl-names = "default"; + pinctrl-0 = <&panel_reset_n &disp_en_default &mdp_vsync_n>; + port { + panel_in: endpoint { + remote-endpoint = <&dsi0_out>; + }; + }; + }; +}; + +&dsi0_phy { + status = "okay"; +}; + +&dsi0_out { + remote-endpoint = <&panel_in>; + data-lanes = <0 1 2 3>; +}; + +&gcc { + status = "okay"; +}; + +&gpucc { + status = "okay"; +}; + +&gpio_keyboard { + linux,keymap = < + MATRIX_KEY(0, 0, KEY_F1) MATRIX_KEY(1, 0, KEY_H) + MATRIX_KEY(2, 0, KEY_B) MATRIX_KEY(3, 0, KEY_7) + MATRIX_KEY(4, 0, KEY_UP) MATRIX_KEY(5, 0, KEY_ENTER) + MATRIX_KEY(6, 0, KEY_Y) MATRIX_KEY(7, 0, KEY_COMMA) + MATRIX_KEY(0, 1, KEY_3) MATRIX_KEY(1, 1, KEY_S) + MATRIX_KEY(2, 1, KEY_Z) MATRIX_KEY(3, 1, KEY_M) + MATRIX_KEY(4, 1, KEY_I) MATRIX_KEY(5, 1, KEY_9) + MATRIX_KEY(6, 1, KEY_W) MATRIX_KEY(7, 1, KEY_J) + MATRIX_KEY(0, 2, KEY_LEFT) MATRIX_KEY(1, 2, KEY_G) + MATRIX_KEY(2, 2, KEY_V) MATRIX_KEY(3, 2, KEY_6) + MATRIX_KEY(4, 2, KEY_RIGHT) MATRIX_KEY(5, 2, KEY_DELETE) + MATRIX_KEY(6, 2, KEY_T) MATRIX_KEY(7, 2, KEY_DOT) + MATRIX_KEY(0, 3, KEY_SLASH) MATRIX_KEY(1, 3, KEY_A) + MATRIX_KEY(2, 3, KEY_RIGHTBRACE) MATRIX_KEY(3, 3, KEY_HOMEPAGE) + MATRIX_KEY(4, 3, KEY_P) MATRIX_KEY(5, 3, KEY_MINUS) + MATRIX_KEY(6, 3, KEY_Q) MATRIX_KEY(7, 3, KEY_L) + MATRIX_KEY(0, 4, KEY_BACKSPACE) MATRIX_KEY(1, 4, KEY_D) + MATRIX_KEY(2, 4, KEY_X) MATRIX_KEY(3, 4, KEY_K) + MATRIX_KEY(4, 4, KEY_SEMICOLON) MATRIX_KEY(5, 4, KEY_EQUAL) + MATRIX_KEY(6, 4, KEY_E) MATRIX_KEY(7, 4, KEY_APOSTROPHE) + MATRIX_KEY(0, 5, KEY_CAPSLOCK) MATRIX_KEY(1, 5, KEY_BACKSLASH) + MATRIX_KEY(2, 5, KEY_LEFTBRACE) MATRIX_KEY(3, 5, KEY_DOWN) + MATRIX_KEY(4, 5, KEY_O) MATRIX_KEY(5, 5, KEY_0) + MATRIX_KEY(6, 5, KEY_GRAVE) MATRIX_KEY(7, 5, KEY_K) + MATRIX_KEY(0, 6, KEY_SPACE) MATRIX_KEY(1, 6, KEY_F) + MATRIX_KEY(2, 6, KEY_C) MATRIX_KEY(3, 6, KEY_N) + MATRIX_KEY(4, 6, KEY_U) MATRIX_KEY(5, 6, KEY_8) + MATRIX_KEY(6, 6, KEY_R) MATRIX_KEY(7, 6, KEY_5) + MATRIX_KEY(0, 7, KEY_ESC) MATRIX_KEY(1, 7, KEY_1) + MATRIX_KEY(2, 7, KEY_RESERVED) MATRIX_KEY(3, 7, KEY_RESERVED) + MATRIX_KEY(4, 7, KEY_2) MATRIX_KEY(5, 7, KEY_4) + MATRIX_KEY(6, 7, KEY_TAB) MATRIX_KEY(7, 7, KEY_RESERVED) + >; +}; + +&mdss { + status = "okay"; +}; + +&mdss_mdp { + status = "okay"; +}; + +&mmcc { + status = "okay"; +}; + &etf { status = "okay"; }; @@ -342,7 +588,21 @@ }; }; +/* HACK! Push GPU voltage high until GPU CPR is hooked up */ +&pm8005_s1 { + regulator-min-microvolt = <988000>; + regulator-max-microvolt = <1100000>; +}; + &pm8998_gpio { + unknown_pin_a: unk-active-state { + pins = "gpio5"; + function = "normal"; + input-enable; + bias-pull-up; + qcom,drive-strength = ; + }; + vol_up_pin_a: vol-up-active-state { pins = "gpio6"; function = "normal"; @@ -630,6 +890,26 @@ bias-pull-up; }; + gpio_expander_int_n: gpio-exp-intn-def { + pins = "gpio50"; + function = "gpio"; + drive-strength = <2>; + input-enable; + }; + + gpio_expander_rst_n: gpio-exp-rst-def { + pins = "gpio51"; + function = "gpio"; + drive-strength = <8>; + }; + + disp_en_default: disp-en { + pins = "gpio62"; + function = "gpio"; + drive-strength = <8>; + bias-disable; + }; + ts_vio_default: ts-vio-def { pins = "gpio81"; function = "gpio"; @@ -658,6 +938,13 @@ bias-disable; drive-strength = <8>; }; + + panel_reset_n: panel-rst-n { + pins = "gpio94"; + function = "gpio"; + drive-strength = <8>; + bias-disable; + }; }; &sdhc2 { From 75b6187a2b150bdca6a5c3913b85b89bed1765f9 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Thu, 21 Jan 2021 20:05:15 +0100 Subject: [PATCH 0256/1823] arm64: dts: msm8998: Wire up interconnects to MDP and GPU Wire up the interconnects to both the MDP and the Adreno GPU in order to get the right balance between performance and power consumption of both devices. --- arch/arm64/boot/dts/qcom/msm8998.dtsi | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi index 3389957032dd..fe9cf081c989 100644 --- a/arch/arm64/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi @@ -1438,6 +1438,8 @@ "rbcpr", "core"; + interconnects = <&bimc MASTER_OXILI &bimc SLAVE_EBI>; + interconnect-names = "gfx-mem"; interrupts = <0 300 IRQ_TYPE_LEVEL_HIGH>; iommus = <&adreno_smmu 0>; operating-points-v2 = <&gpu_opp_table>; @@ -1449,42 +1451,55 @@ opp-710000097 { opp-hz = /bits/ 64 <710000097>; opp-level = ; + opp-peak-kBps = <14432000>; + opp-avg-kBps = <14400000>; opp-supported-hw = <0xFF>; }; opp-670000048 { opp-hz = /bits/ 64 <670000048>; opp-level = ; + opp-peak-kBps = <14432000>; + opp-avg-kBps = <14390000>; opp-supported-hw = <0xFF>; }; - opp-596000097 { opp-hz = /bits/ 64 <596000097>; opp-level = ; + opp-peak-kBps = <14432000>; + opp-avg-kBps = <12440000>; opp-supported-hw = <0xFF>; }; opp-515000097 { opp-hz = /bits/ 64 <515000097>; opp-level = ; + opp-peak-kBps = <14432000>; + opp-avg-kBps = <10368000>; opp-supported-hw = <0xFF>; }; opp-414000000 { opp-hz = /bits/ 64 <414000000>; opp-level = ; + opp-peak-kBps = <12440000>; + opp-avg-kBps = <8136000>; opp-supported-hw = <0xFF>; }; opp-342000000 { opp-hz = /bits/ 64 <342000000>; opp-level = ; + opp-peak-kBps = <8136000>; + opp-avg-kBps = <6144000>; opp-supported-hw = <0xFF>; }; opp-257000000 { opp-hz = /bits/ 64 <257000000>; opp-level = ; + opp-peak-kBps = <6144000>; + opp-avg-kBps = <3296000>; opp-supported-hw = <0xFF>; }; }; @@ -2519,6 +2534,10 @@ assigned-clocks = <&mmcc MDSS_MDP_CLK>; assigned-clock-rates = <300000000>; + interconnects = <&mnoc MASTER_MDP_P0 &bimc SLAVE_EBI>, + <&mnoc MASTER_MDP_P1 &bimc SLAVE_EBI>; + interconnect-names = "mdp0-mem", + "mdp1-mem"; interrupts = ; interrupt-controller; iommus = <&mmss_smmu 0>; From da725ffc9e338a7c86a4c6f7797dc3aad0f8271f Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 23 Dec 2020 12:10:53 +0100 Subject: [PATCH 0257/1823] arm64: dts: msm8998: Add SAW, CPRh and CPUFREQ to enable CPU scaling Add the SAW (SPM), CPR-Hardened, CPUFREQ-HW nodes and relative OPP tables (and also assign them to the CPU nodes, as required) in order to enable CPU scaling on the MSM8998 SoC. The CPR-Hardened and CPUFREQ-HW nodes are disabled by default as to not change the previous default behavior. Since the drivers are not yet accounting for speed-binning, these OPPs are referred to the most common binning for this chip, which I have found on six phones from Sony and one from FxTec (silver bin0, perf bin2). At least until speed-binning gets done in the cpufreq-hw and CPR drivers, users should enable CPR-Hardened and CPUFREQ in their own board DT. This is done like that because these drivers are really big, so the idea is to keep the "base" version easier (but perfectly working), before adding speed-binning "complications", which may... or may not be necessary. Signed-off-by: AngeloGioacchino Del Regno [Fixed up for 5.18 & 6.0-rc2 by Jami] --- arch/arm64/boot/dts/qcom/msm8998.dtsi | 721 +++++++++++++++++++++++++- 1 file changed, 720 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi index fe9cf081c989..762cd4497126 100644 --- a/arch/arm64/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi @@ -10,6 +10,15 @@ #include #include +/* Macro for CPR-Hardened OPP entries - Example phandle: cprh_opp0 */ +#define CPRH_OPP_ENTRY(lvl, _fuselevel, _oadj, _cadj) \ + cprh_opp##lvl##: opp-##lvl { \ + opp-level = ; \ + qcom,opp-fuse-level = <_fuselevel>; \ + qcom,opp-oloop-vadj = <_oadj>; \ + qcom,opp-cloop-vadj = <_cadj>; \ + } + / { interrupt-parent = <&intc>; @@ -136,8 +145,13 @@ reg = <0x0 0x0>; enable-method = "psci"; capacity-dmips-mhz = <1024>; + clocks = <&xo>; cpu-idle-states = <&LITTLE_CPU_SLEEP_0 &LITTLE_CPU_SLEEP_1>; next-level-cache = <&L2_0>; + operating-points-v2 = <&cpu_silver_opp_table>; + power-domains = <&apc_cprh 0>; + power-domain-names = "cprh"; + qcom,freq-domain = <&cpufreq_hw 0>; L2_0: l2-cache { compatible = "cache"; cache-level = <2>; @@ -150,8 +164,13 @@ reg = <0x0 0x1>; enable-method = "psci"; capacity-dmips-mhz = <1024>; + clocks = <&xo>; cpu-idle-states = <&LITTLE_CPU_SLEEP_0 &LITTLE_CPU_SLEEP_1>; next-level-cache = <&L2_0>; + operating-points-v2 = <&cpu_silver_opp_table>; + power-domains = <&apc_cprh 0>; + power-domain-names = "cprh"; + qcom,freq-domain = <&cpufreq_hw 0>; }; CPU2: cpu@2 { @@ -160,8 +179,13 @@ reg = <0x0 0x2>; enable-method = "psci"; capacity-dmips-mhz = <1024>; + clocks = <&xo>; cpu-idle-states = <&LITTLE_CPU_SLEEP_0 &LITTLE_CPU_SLEEP_1>; next-level-cache = <&L2_0>; + operating-points-v2 = <&cpu_silver_opp_table>; + power-domains = <&apc_cprh 0>; + power-domain-names = "cprh"; + qcom,freq-domain = <&cpufreq_hw 0>; }; CPU3: cpu@3 { @@ -170,8 +194,13 @@ reg = <0x0 0x3>; enable-method = "psci"; capacity-dmips-mhz = <1024>; + clocks = <&xo>; cpu-idle-states = <&LITTLE_CPU_SLEEP_0 &LITTLE_CPU_SLEEP_1>; next-level-cache = <&L2_0>; + operating-points-v2 = <&cpu_silver_opp_table>; + power-domains = <&apc_cprh 0>; + power-domain-names = "cprh"; + qcom,freq-domain = <&cpufreq_hw 0>; }; CPU4: cpu@100 { @@ -180,8 +209,13 @@ reg = <0x0 0x100>; enable-method = "psci"; capacity-dmips-mhz = <1536>; + clocks = <&xo>; cpu-idle-states = <&BIG_CPU_SLEEP_0 &BIG_CPU_SLEEP_1>; next-level-cache = <&L2_1>; + operating-points-v2 = <&cpu_gold_opp_table>; + power-domains = <&apc_cprh 1>; + power-domain-names = "cprh"; + qcom,freq-domain = <&cpufreq_hw 1>; L2_1: l2-cache { compatible = "cache"; cache-level = <2>; @@ -194,8 +228,13 @@ reg = <0x0 0x101>; enable-method = "psci"; capacity-dmips-mhz = <1536>; + clocks = <&xo>; cpu-idle-states = <&BIG_CPU_SLEEP_0 &BIG_CPU_SLEEP_1>; next-level-cache = <&L2_1>; + operating-points-v2 = <&cpu_gold_opp_table>; + power-domains = <&apc_cprh 1>; + power-domain-names = "cprh"; + qcom,freq-domain = <&cpufreq_hw 1>; }; CPU6: cpu@102 { @@ -204,8 +243,13 @@ reg = <0x0 0x102>; enable-method = "psci"; capacity-dmips-mhz = <1536>; + clocks = <&xo>; cpu-idle-states = <&BIG_CPU_SLEEP_0 &BIG_CPU_SLEEP_1>; next-level-cache = <&L2_1>; + operating-points-v2 = <&cpu_gold_opp_table>; + power-domains = <&apc_cprh 1>; + power-domain-names = "cprh"; + qcom,freq-domain = <&cpufreq_hw 1>; }; CPU7: cpu@103 { @@ -214,8 +258,13 @@ reg = <0x0 0x103>; enable-method = "psci"; capacity-dmips-mhz = <1536>; + clocks = <&xo>; cpu-idle-states = <&BIG_CPU_SLEEP_0 &BIG_CPU_SLEEP_1>; next-level-cache = <&L2_1>; + operating-points-v2 = <&cpu_gold_opp_table>; + power-domains = <&apc_cprh 1>; + power-domain-names = "cprh"; + qcom,freq-domain = <&cpufreq_hw 1>; }; cpu-map { @@ -303,6 +352,378 @@ }; }; + cpu_gold_opp_table: cpu-gold-opp-table { + compatible = "operating-points-v2"; + opp-shared; + + opp-2361600000 { + opp-hz = /bits/ 64 <2361600000>; + required-opps = <&cprh_opp30>; + qcom,pll-override = <0x0a620062>; + qcom,spare-data = <3>; + }; + opp-2342400000 { + opp-hz = /bits/ 64 <2342400000>; + required-opps = <&cprh_opp29>; + qcom,pll-override = <0x0a620062>; + qcom,spare-data = <3>; + }; + opp-2323200000 { + opp-hz = /bits/ 64 <2323200000>; + required-opps = <&cprh_opp28>; + qcom,pll-override = <0x0a610061>; + qcom,spare-data = <3>; + }; + opp-2265600000 { + opp-hz = /bits/ 64 <2265600000>; + required-opps = <&cprh_opp27>; + qcom,pll-override = <0x0a5e005e>; + qcom,spare-data = <3>; + }; + opp-2208000000 { + opp-hz = /bits/ 64 <2208000000>; + required-opps = <&cprh_opp26>; + qcom,pll-override = <0x0a5c005c>; + qcom,spare-data = <3>; + }; + opp-2112000000 { + opp-hz = /bits/ 64 <2112000000>; + required-opps = <&cprh_opp25>; + qcom,pll-override = <0x0a580058>; + qcom,spare-data = <3>; + }; + opp-2035200000 { + opp-hz = /bits/ 64 <2035200000>; + required-opps = <&cprh_opp24>; + qcom,pll-override = <0x09550055>; + qcom,spare-data = <3>; + }; + opp-1958400000 { + opp-hz = /bits/ 64 <1958400000>; + required-opps = <&cprh_opp23>; + qcom,pll-override = <0x09520052>; + qcom,spare-data = <2>; + }; + opp-1881600000 { + opp-hz = /bits/ 64 <1881600000>; + required-opps = <&cprh_opp22>; + qcom,pll-override = <0x094e004e>; + qcom,spare-data = <2>; + }; + opp-1804800000 { + opp-hz = /bits/ 64 <1804800000>; + required-opps = <&cprh_opp21>; + qcom,pll-override = <0x084b004b>; + qcom,spare-data = <2>; + }; + opp-1728000000 { + opp-hz = /bits/ 64 <1728000000>; + required-opps = <&cprh_opp20>; + qcom,pll-override = <0x08480048>; + qcom,spare-data = <2>; + }; + opp-1651200000 { + opp-hz = /bits/ 64 <1651200000>; + required-opps = <&cprh_opp19>; + qcom,pll-override = <0x07450045>; + qcom,spare-data = <2>; + }; + opp-1574400000 { + opp-hz = /bits/ 64 <1574400000>; + required-opps = <&cprh_opp18>; + qcom,pll-override = <0x07420042>; + qcom,spare-data = <2>; + }; + opp-1497600000 { + opp-hz = /bits/ 64 <1497600000>; + required-opps = <&cprh_opp17>; + qcom,pll-override = <0x073e003e>; + qcom,spare-data = <2>; + }; + opp-1420800000 { + opp-hz = /bits/ 64 <1420800000>; + required-opps = <&cprh_opp16>; + qcom,pll-override = <0x063b003b>; + qcom,spare-data = <2>; + }; + opp-1344000000 { + opp-hz = /bits/ 64 <1344000000>; + required-opps = <&cprh_opp15>; + qcom,pll-override = <0x06380038>; + qcom,spare-data = <2>; + }; + + opp-1267200000 { + opp-hz = /bits/ 64 <1267200000>; + required-opps = <&cprh_opp14>; + qcom,pll-override = <0x06350035>; + qcom,spare-data = <2>; + }; + opp-1190400000 { + opp-hz = /bits/ 64 <1190400000>; + required-opps = <&cprh_opp13>; + qcom,pll-override = <0x05320032>; + qcom,spare-data = <2>; + }; + opp-1132800000 { + opp-hz = /bits/ 64 <1132800000>; + required-opps = <&cprh_opp12>; + qcom,pll-override = <0x052f002f>; + qcom,spare-data = <1>; + }; + opp-1056000000 { + opp-hz = /bits/ 64 <1056000000>; + required-opps = <&cprh_opp11>; + qcom,pll-override = <0x052c002c>; + qcom,spare-data = <1>; + }; + opp-979200000 { + opp-hz = /bits/ 64 <979200000>; + required-opps = <&cprh_opp10>; + qcom,pll-override = <0x4290029>; + qcom,spare-data = <1>; + }; + opp-902400000 { + opp-hz = /bits/ 64 <902400000>; + required-opps = <&cprh_opp9>; + qcom,pll-override = <0x4260026>; + qcom,spare-data = <1>; + }; + opp-806400000 { + opp-hz = /bits/ 64 <806400000>; + required-opps = <&cprh_opp8>; + qcom,pll-override = <0x3200022>; + qcom,pll-div = <1>; + qcom,spare-data = <1>; + }; + opp-729600000 { + opp-hz = /bits/ 64 <729600000>; + required-opps = <&cprh_opp7>; + qcom,pll-override = <0x3200020>; + qcom,pll-div = <1>; + qcom,spare-data = <1>; + }; + opp-652800000 { + opp-hz = /bits/ 64 <652800000>; + required-opps = <&cprh_opp6>; + qcom,pll-override = <0x3200020>; + qcom,pll-div = <1>; + qcom,spare-data = <1>; + }; + opp-576000000 { + opp-hz = /bits/ 64 <576000000>; + required-opps = <&cprh_opp5>; + qcom,pll-override = <0x2200020>; + qcom,pll-div = <1>; + qcom,spare-data = <1>; + }; + opp-499200000 { + opp-hz = /bits/ 64 <499200000>; + required-opps = <&cprh_opp4>; + qcom,pll-override = <0x2200020>; + qcom,pll-div = <1>; + qcom,spare-data = <1>; + }; + opp-422400000 { + opp-hz = /bits/ 64 <422400000>; + required-opps = <&cprh_opp3>; + qcom,pll-override = <0x2200020>; + qcom,pll-div = <1>; + qcom,spare-data = <1>; + }; + opp-345600000 { + opp-hz = /bits/ 64 <345600000>; + required-opps = <&cprh_opp2>; + qcom,pll-override = <0x1200020>; + qcom,pll-div = <1>; + qcom,spare-data = <1>; + }; + opp-300000000 { + opp-hz = /bits/ 64 <300000000>; + required-opps = <&cprh_opp1>; + qcom,pll-override = <0x1200020>; + qcom,spare-data = <1>; + }; + }; + + cpu_silver_opp_table: cpu-silver-opp-table { + compatible = "operating-points-v2"; + opp-shared; + + opp-1900800000 { + opp-hz = /bits/ 64 <1900800000>; + required-opps = <&cprh_opp22>; + qcom,pll-override = <0x094f004f>; + qcom,spare-data = <3>; + }; + opp-1824000000 { + opp-hz = /bits/ 64 <1824000000>; + required-opps = <&cprh_opp21>; + qcom,pll-override = <0x084c004c>; + qcom,spare-data = <3>; + }; + opp-1747200000 { + opp-hz = /bits/ 64 <1747200000>; + required-opps = <&cprh_opp20>; + qcom,pll-override = <0x08490049>; + qcom,spare-data = <2>; + }; + opp-1670400000 { + opp-hz = /bits/ 64 <1670400000>; + required-opps = <&cprh_opp19>; + qcom,pll-override = <0x08460046>; + qcom,spare-data = <2>; + }; + opp-1555200000 { + opp-hz = /bits/ 64 <1555200000>; + required-opps = <&cprh_opp18>; + qcom,pll-override = <0x07410041>; + qcom,spare-data = <2>; + }; + opp-1478400000 { + opp-hz = /bits/ 64 <1478400000>; + required-opps = <&cprh_opp17>; + qcom,pll-override = <0x073e003e>; + qcom,spare-data = <2>; + }; + opp-1401600000 { + opp-hz = /bits/ 64 <1401600000>; + required-opps = <&cprh_opp16>; + qcom,pll-override = <0x063a003a>; + qcom,spare-data = <2>; + }; + opp-1324800000 { + opp-hz = /bits/ 64 <1324800000>; + required-opps = <&cprh_opp15>; + qcom,pll-override = <0x06370037>; + qcom,spare-data = <2>; + }; + opp-1248000000 { + opp-hz = /bits/ 64 <1248000000>; + required-opps = <&cprh_opp14>; + qcom,pll-override = <0x05340034>; + qcom,spare-data = <2>; + }; + opp-1171200000 { + opp-hz = /bits/ 64 <1171200000>; + required-opps = <&cprh_opp13>; + qcom,pll-override = <0x05310031>; + qcom,spare-data = <2>; + }; + opp-1094400000 { + opp-hz = /bits/ 64 <1094400000>; + required-opps = <&cprh_opp12>; + qcom,pll-override = <0x052e002e>; + qcom,spare-data = <2>; + }; + opp-1036800000 { + opp-hz = /bits/ 64 <1036800000>; + required-opps = <&cprh_opp11>; + qcom,pll-override = <0x042b002b>; + qcom,spare-data = <1>; + }; + opp-960000000 { + opp-hz = /bits/ 64 <960000000>; + required-opps = <&cprh_opp10>; + qcom,pll-override = <0x4280028>; + qcom,spare-data = <1>; + }; + opp-883200000 { + opp-hz = /bits/ 64 <883200000>; + required-opps = <&cprh_opp9>; + qcom,pll-override = <0x4250025>; + qcom,spare-data = <1>; + }; + opp-825600000 { + opp-hz = /bits/ 64 <825600000>; + required-opps = <&cprh_opp8>; + qcom,pll-override = <0x3200022>; + qcom,spare-data = <1>; + }; + opp-748800000 { + opp-hz = /bits/ 64 <748800000>; + required-opps = <&cprh_opp7>; + qcom,pll-override = <0x3200020>; + qcom,pll-div = <1>; + qcom,spare-data = <1>; + }; + opp-672000000 { + opp-hz = /bits/ 64 <672000000>; + required-opps = <&cprh_opp6>; + qcom,pll-override = <0x3200020>; + qcom,pll-div = <1>; + qcom,spare-data = <1>; + }; + opp-595200000 { + opp-hz = /bits/ 64 <595200000>; + required-opps = <&cprh_opp5>; + qcom,pll-override = <0x2200020>; + qcom,pll-div = <1>; + qcom,spare-data = <1>; + }; + opp-518400000 { + opp-hz = /bits/ 64 <518400000>; + required-opps = <&cprh_opp4>; + qcom,pll-override = <0x2200020>; + qcom,pll-div = <1>; + qcom,spare-data = <1>; + }; + opp-441600000 { + opp-hz = /bits/ 64 <441600000>; + required-opps = <&cprh_opp3>; + qcom,pll-override = <0x2200020>; + qcom,pll-div = <1>; + qcom,spare-data = <1>; + }; + opp-364800000 { + opp-hz = /bits/ 64 <364800000>; + required-opps = <&cprh_opp2>; + qcom,pll-override = <0x1200020>; + qcom,pll-div = <1>; + qcom,spare-data = <1>; + }; + opp-300000000 { + opp-hz = /bits/ 64 <300000000>; + required-opps = <&cprh_opp1>; + qcom,pll-override = <0x1200020>; + }; + }; + + cprh_opp_table: cpr-opp-table { + compatible = "operating-points-v2-qcom-level"; + + CPRH_OPP_ENTRY(1, 1, 0, 0); + CPRH_OPP_ENTRY(2, 1, 0, 0); + CPRH_OPP_ENTRY(3, 1, 0, 0); + CPRH_OPP_ENTRY(4, 1, 0, 0); + CPRH_OPP_ENTRY(5, 1, 0, 0); + CPRH_OPP_ENTRY(6, 1, 0, 0); + CPRH_OPP_ENTRY(7, 1, 0, 0); + CPRH_OPP_ENTRY(8, 1, 0, 0); + CPRH_OPP_ENTRY(9, 2, 0, 0); + CPRH_OPP_ENTRY(10, 2, 0, 0); + CPRH_OPP_ENTRY(11, 2, 0, 0); + CPRH_OPP_ENTRY(12, 3 2, (-12000) (-8000), (-10000) (-10000)); + CPRH_OPP_ENTRY(13, 3, (-16000) (-16000), (-11000) (-10000)); + CPRH_OPP_ENTRY(14, 3, (-16000) (-12000), (-12000) (-11000)); + CPRH_OPP_ENTRY(15, 3, (-12000) (-16000), (-13000) (-12000)); + CPRH_OPP_ENTRY(16, 3, (-12000) (-16000), (-14000) (-12000)); + CPRH_OPP_ENTRY(17, 3, (-16000) (-12000), (-14000) (-13000)); + CPRH_OPP_ENTRY(18, 3, (-16000) (-16000), (-15000) (-14000)); + CPRH_OPP_ENTRY(19, 4 3, (-20000) (-16000), (-21000) (-14000)); + CPRH_OPP_ENTRY(20, 4 3, (-24000) (-16000), (-24000) (-15000)); + CPRH_OPP_ENTRY(21, 4, (-28000) (-24000), (-26000) (-16000)); + CPRH_OPP_ENTRY(22, 4, (-28000) (-16000), (-28000) (-16000)); + CPRH_OPP_ENTRY(23, 4, 0 (-20000), 0 (-17000)); + CPRH_OPP_ENTRY(24, 4, 0 (-16000), 0 (-15000)); + CPRH_OPP_ENTRY(25, 4, 0 (-12000), 0 (-14000)); + CPRH_OPP_ENTRY(26, 4, 0 (-28000), 0 (-27000)); + CPRH_OPP_ENTRY(27, 4, 0 (-28000), 0 (-27000)); + CPRH_OPP_ENTRY(28, 4, 0 (-28000), 0 (-28000)); + CPRH_OPP_ENTRY(29, 4, 0 (-28000), 0 (-28000)); + CPRH_OPP_ENTRY(30, 4, 0 (-28000), 0 (-28000)); + }; + firmware { scm { compatible = "qcom,scm-msm8998", "qcom,scm"; @@ -834,7 +1255,7 @@ qfprom: qfprom@784000 { compatible = "qcom,msm8998-qfprom", "qcom,qfprom"; - reg = <0x00784000 0x621c>; + reg = <0x00784000 0x221c>; #address-cells = <1>; #size-cells = <1>; @@ -842,6 +1263,186 @@ reg = <0x23a 0x1>; bits = <0 4>; }; + + cpr_efuse_speedbin: speedbin@133 { + reg = <0x133 0x2>; + bits = <5 3>; + }; + + cpr_fuse_revision: cpr_fusing_rev@13e { + reg = <0x13E 0x1>; + bits = <3 3>; + }; + + /* CPR Ring Oscillator: Power Cluster */ + cpr_ro_sel0_pwrcl: rosel0_pwrcl@219 { + reg = <0x219 0x2>; + bits = <4 4>; + }; + + cpr_ro_sel1_pwrcl: rosel1_pwrcl@219 { + reg = <0x219 0x1>; + bits = <0 4>; + }; + + cpr_ro_sel2_pwrcl: rosel2_pwrcl@218 { + reg = <0x218 0x2>; + bits = <4 4>; + }; + + cpr_ro_sel3_pwrcl: rosel3_pwrcl@218 { + reg = <0x218 0x1>; + bits = <0 4>; + }; + + /* CPR Init Voltage: Power Cluster */ + cpr_init_voltage0_pwrcl: ivolt0_pwrcl@21c { + reg = <0x21C 0x2>; + bits = <2 6>; + }; + + cpr_init_voltage1_pwrcl: ivolt1_pwrcl@21b { + reg = <0x21B 0x2>; + bits = <4 6>; + }; + + cpr_init_voltage2_pwrcl: ivolt2_pwrcl@21a { + reg = <0x21A 0x2>; + bits = <6 6>; + }; + + cpr_init_voltage3_pwrcl: ivolt3_pwrcl@21a { + reg = <0x21A 0x1>; + bits = <0 6>; + }; + + /* CPR Target Quotients: Power Cluster */ + cpr_quot0_pwrcl: quot0_pwrcl@222 { + reg = <0x222 0x3>; + bits = <2 12>; + }; + + cpr_quot1_pwrcl: quot1_pwrcl@220 { + reg = <0x220 0x3>; + bits = <6 12>; + }; + + cpr_quot2_pwrcl: quot2_pwrcl@21f { + reg = <0x21F 0x2>; + bits = <2 11>; + }; + + cpr_quot3_pwrcl: quot3_pwrcl@21d { + reg = <0x21D 0x3>; + bits = <6 12>; + }; + + /* CPR Quotient Offsets: Power Cluster */ + cpr_quot_offset1_pwrcl: qoff1_pwrcl@227 { + reg = <0x227 0x2>; + bits = <7 6>; + }; + + cpr_quot_offset2_pwrcl: qoff2_pwrcl@227 { + reg = <0x227 0x1>; + bits = <0 7>; + }; + + cpr_quot_offset3_pwrcl: qoff3_pwrcl@226 { + reg = <0x226 0x2>; + bits = <1 7>; + }; + + /* CPR Aging Quotient Offsets: Power Cluster */ + cpr_aging_quot_off_pwrcl: qoff_aging_pwrcl@228 { + reg = <0x228 0x2>; + bits = <6 8>; + }; + + /* CPR Ring Oscillator: Performance Cluster */ + cpr_ro_sel0_perfcl: rosel0_perfcl@22b { + reg = <0x22B 0x1>; + bits = <2 4>; + }; + + cpr_ro_sel1_perfcl: rosel1_perfcl@22a { + reg = <0x22A 0x2>; + bits = <6 4>; + }; + + cpr_ro_sel2_perfcl: rosel2_perfcl@22a { + reg = <0x22A 0x1>; + bits = <2 4>; + }; + + cpr_ro_sel3_perfcl: rosel3_perfcl@229 { + reg = <0x229 0x2>; + bits = <6 4>; + }; + + /* CPR Init Voltage: Performance Cluster */ + cpr_init_voltage0_perfcl: ivolt0_perfcl@22e { + reg = <0x22E 0x1>; + bits = <0 6>; + }; + + cpr_init_voltage1_perfcl: ivolt1_perfcl@22d { + reg = <0x22D 0x2>; + bits = <2 6>; + }; + + cpr_init_voltage2_perfcl: ivolt2_perfcl@22c { + reg = <0x22C 0x2>; + bits = <4 6>; + }; + + cpr_init_voltage3_perfcl: ivolt3_perfcl@22b { + reg = <0x22B 0x2>; + bits = <6 6>; + }; + + /* CPR Target Quotients: Performance Cluster */ + cpr_quot0_perfcl: quot0_perfcl@234 { + reg = <0x234 0x2>; + bits = <0 12>; + }; + + cpr_quot1_perfcl: quot1_perfcl@232 { + reg = <0x232 0x2>; + bits = <4 12>; + }; + + cpr_quot2_perfcl: quot2_perfcl@231 { + reg = <0x231 0x2>; + bits = <0 12>; + }; + + cpr_quot3_perfcl: quot3_perfcl@22f { + reg = <0x22F 0x2>; + bits = <4 11>; + }; + + /* CPR Quotient Offsets: Performance Cluster */ + cpr_quot_offset1_perfcl: qoff1_perfcl@239 { + reg = <0x239 0x2>; + bits = <5 3>; + }; + + cpr_quot_offset2_perfcl: qoff2_perfcl@238 { + reg = <0x238 0x2>; + bits = <6 7>; + }; + + cpr_quot_offset3_perfcl: qoff3_perfcl@237 { + reg = <0x237 0x2>; + bits = <7 7>; + }; + + /* CPR Aging Quotient Offsets: Performance Cluster */ + cpr_aging_quot_off_perfcl: qoff_aging_perfcl@23b { + reg = <0x23b 0x2>; + bits = <1 8>; + }; }; bimc: interconnect@1008000 { @@ -2844,6 +3445,38 @@ clocks = <&xo>, <&xo>; }; + power-controller@17812000 { + compatible = "qcom,msm8998-gold-saw2-v4.1-l2", + "qcom,saw2"; + reg = <0x017812000 0x1000>; + }; + + power-controller@17912000 { + compatible = "qcom,msm8998-silver-saw2-v4.1-l2", + "qcom,saw2"; + reg = <0x017912000 0x1000>; + }; + + cpufreq_hw: cpufreq_hw@17814800 { + compatible = "qcom,cpufreq-hw-8998"; + reg = <0x017914800 0x100>, <0x017814800 0x100>, + <0x0179c0000 0x1000>, <0x0179c1000 0x1000>, + <0x0179c2000 0x1000>, <0x0179c3000 0x1000>; + reg-names = "osm-acd0", "osm-acd1", + "osm-domain0", "freq-domain0", + "osm-domain1", "freq-domain1"; + + assigned-clocks = <&gcc HMSS_GPLL0_CLK_SRC>; + assigned-clock-rates = <300000000>; + + clocks = <&rpmcc RPM_SMD_XO_A_CLK_SRC>, + <&gcc HMSS_GPLL0_CLK_SRC>; + clock-names = "xo", "alternate"; + + #freq-domain-cells = <1>; + status = "disabled"; + }; + apcs_glb: mailbox@17911000 { compatible = "qcom,msm8998-apcs-hmss-global"; reg = <0x17911000 0x1000>; @@ -2909,6 +3542,92 @@ }; }; + /* Gold and Silver cluster */ + apc_cprh: power-controller@179c8000 { + compatible = "qcom,msm8998-cprh"; + reg = <0x0179c8000 0x4000>, <0x0179c4000 0x4000>; + + assigned-clocks = <&gcc GCC_HMSS_RBCPR_CLK>; + assigned-clock-rates = <19200000>; + clocks = <&gcc GCC_HMSS_RBCPR_CLK>; + clock-names = "ref"; + + operating-points-v2 = <&cprh_opp_table>; + power-domains = <&rpmpd MSM8998_VDDCX_AO>; + #power-domain-cells = <1>; + status = "disabled"; + + nvmem-cells = <&cpr_efuse_speedbin>, + <&cpr_fuse_revision>, + <&cpr_quot0_pwrcl>, + <&cpr_quot1_pwrcl>, + <&cpr_quot2_pwrcl>, + <&cpr_quot3_pwrcl>, + <&cpr_quot_offset1_pwrcl>, + <&cpr_quot_offset2_pwrcl>, + <&cpr_quot_offset3_pwrcl>, + <&cpr_init_voltage0_pwrcl>, + <&cpr_init_voltage1_pwrcl>, + <&cpr_init_voltage2_pwrcl>, + <&cpr_init_voltage3_pwrcl>, + <&cpr_ro_sel0_pwrcl>, + <&cpr_ro_sel1_pwrcl>, + <&cpr_ro_sel2_pwrcl>, + <&cpr_ro_sel3_pwrcl>, + <&cpr_aging_quot_off_pwrcl>, + <&cpr_quot0_perfcl>, + <&cpr_quot1_perfcl>, + <&cpr_quot2_perfcl>, + <&cpr_quot3_perfcl>, + <&cpr_quot_offset1_perfcl>, + <&cpr_quot_offset2_perfcl>, + <&cpr_quot_offset3_perfcl>, + <&cpr_init_voltage0_perfcl>, + <&cpr_init_voltage1_perfcl>, + <&cpr_init_voltage2_perfcl>, + <&cpr_init_voltage3_perfcl>, + <&cpr_ro_sel0_perfcl>, + <&cpr_ro_sel1_perfcl>, + <&cpr_ro_sel2_perfcl>, + <&cpr_ro_sel3_perfcl>, + <&cpr_aging_quot_off_perfcl>; + + nvmem-cell-names = "cpr_speed_bin", + "cpr_fuse_revision", + "cpr0_quotient1", + "cpr0_quotient2", + "cpr0_quotient3", + "cpr0_quotient4", + "cpr0_quotient_offset2", + "cpr0_quotient_offset3", + "cpr0_quotient_offset4", + "cpr0_init_voltage1", + "cpr0_init_voltage2", + "cpr0_init_voltage3", + "cpr0_init_voltage4", + "cpr0_ring_osc1", + "cpr0_ring_osc2", + "cpr0_ring_osc3", + "cpr0_ring_osc4", + "cpr0_aging_quotient", + "cpr1_quotient1", + "cpr1_quotient2", + "cpr1_quotient3", + "cpr1_quotient4", + "cpr1_quotient_offset2", + "cpr1_quotient_offset3", + "cpr1_quotient_offset4", + "cpr1_init_voltage1", + "cpr1_init_voltage2", + "cpr1_init_voltage3", + "cpr1_init_voltage4", + "cpr1_ring_osc1", + "cpr1_ring_osc2", + "cpr1_ring_osc3", + "cpr1_ring_osc4", + "cpr1_aging_quotient"; + }; + intc: interrupt-controller@17a00000 { compatible = "arm,gic-v3"; reg = <0x17a00000 0x10000>, /* GICD */ From 0615adf07d2c0bb5e146e17a2f362946eccc70e5 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 13 Jan 2021 19:22:24 +0100 Subject: [PATCH 0258/1823] i2c: qup: Introduce SCL/SDA noise rejection Some I2C devices may be glitchy due to electrical noise coming from the device itself or because of possible board design issues. To overcome this issue, the QUP's I2C in Qualcomm SoCs supports a noise rejection setting for both SCL and SDA lines. Introduce a setting for noise rejection through device properties, "qcom,noise-reject-sda" and "qcom,noise-reject-scl", which will be used to set the level of noise rejection sensitivity. If the properties are not specified, noise rejection will not be enabled. Signed-off-by: AngeloGioacchino Del Regno --- drivers/i2c/busses/i2c-qup.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c index 2e153f2f71b6..4b55f0b64e10 100644 --- a/drivers/i2c/busses/i2c-qup.c +++ b/drivers/i2c/busses/i2c-qup.c @@ -39,6 +39,8 @@ #define QUP_MX_READ_CNT 0x208 #define QUP_IN_FIFO_BASE 0x218 #define QUP_I2C_CLK_CTL 0x400 +#define QUP_I2C_CLK_CTL_SDA_NR GENMASK(27, 26) +#define QUP_I2C_CLK_CTL_SCL_NR GENMASK(25, 24) #define QUP_I2C_STATUS 0x404 #define QUP_I2C_MASTER_GEN 0x408 @@ -1663,6 +1665,7 @@ static int qup_i2c_probe(struct platform_device *pdev) int ret, fs_div, hs_div; u32 src_clk_freq = DEFAULT_SRC_CLK; u32 clk_freq = DEFAULT_CLK_FREQ; + u32 noise_reject_scl = 0, noise_reject_sda = 0; int blocks; bool is_qup_v1; @@ -1860,6 +1863,19 @@ nodma: qup->clk_ctl = ((fs_div / 2) << 16) | (hs_div << 8) | (fs_div & 0xff); } + /* SCL/SDA Noise rejection (optional) */ + ret = device_property_read_u32(qup->dev, "qcom,noise-reject-scl", + &noise_reject_scl); + if (ret == 0) + qup->clk_ctl |= FIELD_PREP(QUP_I2C_CLK_CTL_SCL_NR, + noise_reject_scl); + + ret = device_property_read_u32(qup->dev, "qcom,noise-reject-sda", + &noise_reject_sda); + if (ret == 0) + qup->clk_ctl |= FIELD_PREP(QUP_I2C_CLK_CTL_SDA_NR, + noise_reject_sda); + /* * Time it takes for a byte to be clocked out on the bus. * Each byte takes 9 clock cycles (8 bits + 1 ack). From e3dd0053222d86874eae894faebd6ef30d267fbb Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 13 Jan 2021 19:24:33 +0100 Subject: [PATCH 0259/1823] dt-bindings: i2c: qcom,i2c-qup: Document noise rejection properties Document the new noise rejection properties "qcom,noise-reject-sda" and "qcom,noise-reject-scl". Signed-off-by: AngeloGioacchino Del Regno --- .../devicetree/bindings/i2c/qcom,i2c-qup.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Documentation/devicetree/bindings/i2c/qcom,i2c-qup.yaml b/Documentation/devicetree/bindings/i2c/qcom,i2c-qup.yaml index f43947514d48..520f113c1c0a 100644 --- a/Documentation/devicetree/bindings/i2c/qcom,i2c-qup.yaml +++ b/Documentation/devicetree/bindings/i2c/qcom,i2c-qup.yaml @@ -55,6 +55,20 @@ properties: reg: maxItems: 1 + qcom,noise-reject-sda: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Noise rejection level for the SDA line. + minimum: 0 + maximum: 3 + default: 0 + + qcom,noise-reject-scl: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Noise rejection level for the SCL line. + minimum: 0 + maximum: 3 + default: 0 + required: - compatible - clock-names From 0b98021c39d279d9315929030f123b69d161da90 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 20 Jan 2021 13:32:32 +0100 Subject: [PATCH 0260/1823] dt-bindings: interconnect: Add bindings for Qualcomm MSM8998 NoC Add the bindings for the Qualcomm MSM8998 NoC interconnects. Signed-off-by: AngeloGioacchino Del Regno --- .../bindings/interconnect/qcom,msm8998.yaml | 158 ++++++++++++++++++ .../dt-bindings/interconnect/qcom,msm8998.h | 128 ++++++++++++++ 2 files changed, 286 insertions(+) create mode 100644 Documentation/devicetree/bindings/interconnect/qcom,msm8998.yaml create mode 100644 include/dt-bindings/interconnect/qcom,msm8998.h diff --git a/Documentation/devicetree/bindings/interconnect/qcom,msm8998.yaml b/Documentation/devicetree/bindings/interconnect/qcom,msm8998.yaml new file mode 100644 index 000000000000..3bea90cbe053 --- /dev/null +++ b/Documentation/devicetree/bindings/interconnect/qcom,msm8998.yaml @@ -0,0 +1,158 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interconnect/qcom,msm8998.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm MSM8998 Network-On-Chip interconnect + +maintainers: + - AngeloGioacchino Del Regno + - Konrad Dybcio + +description: | + The Qualcomm MSM8998 interconnect providers support adjusting the + bandwidth requirements between the various NoC fabrics. + +properties: + reg: + maxItems: 1 + + compatible: + enum: + - qcom,msm8998-a1noc + - qcom,msm8998-a2noc + - qcom,msm8998-bimc + - qcom,msm8998-cnoc + - qcom,msm8998-gnoc + - qcom,msm8998-mnoc + - qcom,msm8998-snoc + + '#interconnect-cells': + const: 1 + + clocks: + minItems: 1 + maxItems: 3 + + clock-names: + minItems: 1 + maxItems: 3 + +required: + - compatible + - reg + - '#interconnect-cells' + - clock-names + - clocks + +additionalProperties: false + +allOf: + - if: + properties: + compatible: + contains: + enum: + - qcom,msm8998-mnoc + then: + properties: + clocks: + items: + - description: Bus Clock. + - description: Bus A Clock. + - description: CPU-NoC High-performance Bus Clock. + clock-names: + items: + - const: bus + - const: bus_a + - const: iface + + - if: + properties: + compatible: + contains: + enum: + - qcom,msm8998-a2noc + - qcom,msm8998-bimc + - qcom,msm8998-cnoc + - qcom,msm8998-gnoc + - qcom,msm8998-snoc + then: + properties: + clocks: + items: + - description: Bus Clock. + - description: Bus A Clock. + clock-names: + items: + - const: bus + - const: bus_a + +examples: + - | + #include + #include + + bimc: interconnect@1008000 { + compatible = "qcom,msm8998-bimc"; + reg = <0x01008000 0x78000>; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&rpmcc RPM_SMD_BIMC_CLK>, + <&rpmcc RPM_SMD_BIMC_A_CLK>; + }; + + cnoc: interconnect@1500000 { + compatible = "qcom,msm8998-cnoc"; + reg = <0x01500000 0x10000>; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&rpmcc RPM_SMD_CNOC_CLK>, + <&rpmcc RPM_SMD_CNOC_A_CLK>; + }; + + snoc: interconnect@1625000 { + compatible = "qcom,msm8998-snoc"; + reg = <0x01625000 0x6100>; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&rpmcc RPM_SMD_SNOC_CLK>, + <&rpmcc RPM_SMD_SNOC_A_CLK>; + }; + + a1noc: interconnect@1669000 { + compatible = "qcom,msm8998-a1noc"; + reg = <0x01669000 0x5020>; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&rpmcc RPM_SMD_AGGR1_NOC_CLK>, + <&rpmcc RPM_SMD_AGGR1_NOC_A_CLK>; + }; + + a2noc: interconnect@1705000 { + compatible = "qcom,msm8998-a2noc"; + reg = <0x01705000 0xa090>; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&rpmcc RPM_SMD_AGGR2_NOC_CLK>, + <&rpmcc RPM_SMD_AGGR2_NOC_A_CLK>; + }; + + mnoc: interconnect@1744000 { + compatible = "qcom,msm8998-mnoc"; + reg = <0x01744000 0xb010>; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a", "iface"; + clocks = <&rpmcc RPM_SMD_MMAXI_CLK>, + <&rpmcc RPM_SMD_MMAXI_A_CLK>, + <&mmcc AHB_CLK_SRC>; + }; + + gnoc: interconnect@17900000 { + compatible = "qcom,msm8998-gnoc"; + reg = <0x17900000 0xe000>; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&xo_board>, <&xo_board>; + }; diff --git a/include/dt-bindings/interconnect/qcom,msm8998.h b/include/dt-bindings/interconnect/qcom,msm8998.h new file mode 100644 index 000000000000..5b94403ef178 --- /dev/null +++ b/include/dt-bindings/interconnect/qcom,msm8998.h @@ -0,0 +1,128 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* MSM8998 interconnect IDs */ + +#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_MSM8998_H +#define __DT_BINDINGS_INTERCONNECT_QCOM_MSM8998_H + +/* A1NOC */ +#define MASTER_PCIE_0 0 +#define MASTER_USB3 1 +#define MASTER_UFS 2 +#define MASTER_BLSP_2 3 +#define SLAVE_A1NOC_SNOC 4 + +/* A2NOC */ +#define MASTER_IPA 0 +#define MASTER_CNOC_A2NOC 1 +#define MASTER_SDCC_2 2 +#define MASTER_SDCC_4 3 +#define MASTER_TSIF 4 +#define MASTER_BLSP_1 5 +#define MASTER_CRVIRT_A2NOC 6 +#define MASTER_CRYPTO_C0 7 +#define SLAVE_A2NOC_SNOC 8 +#define SLAVE_CRVIRT_A2NOC 9 + +/* BIMC */ +#define MASTER_GNOC_BIMC 0 +#define MASTER_OXILI 1 +#define MASTER_MNOC_BIMC 2 +#define MASTER_SNOC_BIMC 3 +#define SLAVE_EBI 4 +#define SLAVE_HMSS_L3 5 +#define SLAVE_BIMC_SNOC_0 6 +#define SLAVE_BIMC_SNOC_1 7 + +/* CNOC */ +#define MASTER_SNOC_CNOC 0 +#define MASTER_QDSS_DAP 1 +#define SLAVE_CNOC_A2NOC 2 +#define SLAVE_SSC_CFG 3 +#define SLAVE_MPM 4 +#define SLAVE_PMIC_ARB 5 +#define SLAVE_TLMM_NORTH 6 +#define SLAVE_PIMEM_CFG 7 +#define SLAVE_IMEM_CFG 8 +#define SLAVE_MESSAGE_RAM 9 +#define SLAVE_SKL 10 +#define SLAVE_BIMC_CFG 11 +#define SLAVE_PRNG 12 +#define SLAVE_A2NOC_CFG 13 +#define SLAVE_IPA 14 +#define SLAVE_TCSR 15 +#define SLAVE_SNOC_CFG 16 +#define SLAVE_CLK_CTL 17 +#define SLAVE_GLM 18 +#define SLAVE_SPDM 19 +#define SLAVE_GPUSS_CFG 20 +#define SLAVE_CNOC_MNOC_CFG 21 +#define SLAVE_QM_CFG 22 +#define SLAVE_MSS_CFG 23 +#define SLAVE_UFS_CFG 24 +#define SLAVE_TLMM_WEST 25 +#define SLAVE_A1NOC_CFG 26 +#define SLAVE_AHB2PHY 27 +#define SLAVE_BLSP_2 28 +#define SLAVE_PDM 29 +#define SLAVE_USB3_0 30 +#define SLAVE_A1NOC_SMMU_CFG 31 +#define SLAVE_BLSP_1 32 +#define SLAVE_SDCC_2 33 +#define SLAVE_SDCC_4 34 +#define SLAVE_TSIF 35 +#define SLAVE_QDSS_CFG 36 +#define SLAVE_TLMM_EAST 37 +#define SLAVE_CNOC_MNOC_MMSS_CFG 38 +#define SLAVE_SRVC_CNOC 39 + +/* GNOC */ +#define MASTER_APSS_PROC 0 +#define SLAVE_GNOC_BIMC 1 + +/* MNOC */ +#define MASTER_CNOC_MNOC_CFG 0 +#define MASTER_CPP 1 +#define MASTER_JPEG 2 +#define MASTER_MDP_P0 3 +#define MASTER_MDP_P1 4 +#define MASTER_ROTATOR 5 +#define MASTER_VENUS 6 +#define MASTER_VFE 7 +#define MASTER_VENUS_VMEM 8 +#define SLAVE_MNOC_BIMC 9 +#define SLAVE_VMEM 10 +#define SLAVE_SRVC_MNOC 11 +#define MASTER_CNOC_MNOC_MMSS_CFG 12 +#define SLAVE_CAMERA_CFG 13 +#define SLAVE_CAMERA_THROTTLE_CFG 14 +#define SLAVE_MISC_CFG 15 +#define SLAVE_VENUS_THROTTLE_CFG 16 +#define SLAVE_VENUS_CFG 17 +#define SLAVE_VMEM_CFG 18 +#define SLAVE_MMSS_CLK_XPU_CFG 19 +#define SLAVE_MMSS_CLK_CFG 20 +#define SLAVE_DISPLAY_CFG 21 +#define SLAVE_DISPLAY_THROTTLE_CFG 22 +#define SLAVE_SMMU_CFG 23 + +/* SNOC */ +#define MASTER_HMSS 0 +#define MASTER_QDSS_BAM 1 +#define MASTER_SNOC_CFG 2 +#define MASTER_BIMC_SNOC_0 3 +#define MASTER_BIMC_SNOC_1 4 +#define MASTER_A1NOC_SNOC 5 +#define MASTER_A2NOC_SNOC 6 +#define MASTER_QDSS_ETR 7 +#define SLAVE_HMSS 8 +#define SLAVE_LPASS 9 +#define SLAVE_WLAN 10 +#define SLAVE_SNOC_BIMC 11 +#define SLAVE_SNOC_CNOC 12 +#define SLAVE_IMEM 13 +#define SLAVE_PIMEM 14 +#define SLAVE_QDSS_STM 15 +#define SLAVE_PCIE_0 16 +#define SLAVE_SRVC_SNOC 17 + +#endif From 0b2c00208c943a09c6966fd1501f8b009f3ab8ba Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Wed, 20 Jan 2021 13:33:59 +0100 Subject: [PATCH 0261/1823] interconnect: qcom: Add MSM8998 interconnect provider driver Introduce a driver for the Qualcomm interconnect busses found in the MSM/APQ8998 SoCs. The topology consists of several NoCs that are controlled by a remote processor that collects the aggregated bandwidth for each master-slave pairs. On a note, these chips are managing the "bus QoS" in a "hybrid" fashion: some of the paths in the topology are managed through and by, of course) the RPM uC, while some others are "AP Owned", meaning that the AP shall do direct writes to the appropriate QoS registers for the specific paths and ports, instead of sending an indication to the RPM and leaving the job to that one. Co-authored-by: AngeloGioacchino Del Regno Signed-off-by: Konrad Dybcio Signed-off-by: AngeloGioacchino Del Regno (JAMI: fixup for 5.17) --- drivers/interconnect/qcom/Kconfig | 9 + drivers/interconnect/qcom/Makefile | 2 + drivers/interconnect/qcom/msm8998.c | 981 ++++++++++++++++++++++++++++ 3 files changed, 992 insertions(+) create mode 100644 drivers/interconnect/qcom/msm8998.c diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig index 1a1c941635a2..1109f1204317 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -44,6 +44,15 @@ config INTERCONNECT_QCOM_MSM8996 This is a driver for the Qualcomm Network-on-Chip on msm8996-based platforms. +config INTERCONNECT_QCOM_MSM8998 + tristate "Qualcomm MSM8998 interconnect driver" + depends on INTERCONNECT_QCOM + depends on QCOM_SMD_RPM + select INTERCONNECT_QCOM_SMD_RPM + help + This is a driver for the Qualcomm Network-on-Chip on msm8998-based + platforms. + config INTERCONNECT_QCOM_OSM_L3 tristate "Qualcomm OSM L3 interconnect driver" depends on INTERCONNECT_QCOM || COMPILE_TEST diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile index 8e357528185d..e15357515f5d 100644 --- a/drivers/interconnect/qcom/Makefile +++ b/drivers/interconnect/qcom/Makefile @@ -8,6 +8,7 @@ qnoc-msm8916-objs := msm8916.o qnoc-msm8939-objs := msm8939.o qnoc-msm8974-objs := msm8974.o qnoc-msm8996-objs := msm8996.o +qnoc-msm8998-objs := msm8998.o icc-osm-l3-objs := osm-l3.o qnoc-qcm2290-objs := qcm2290.o qnoc-qcs404-objs := qcs404.o @@ -32,6 +33,7 @@ obj-$(CONFIG_INTERCONNECT_QCOM_MSM8916) += qnoc-msm8916.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8939) += qnoc-msm8939.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8974) += qnoc-msm8974.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8996) += qnoc-msm8996.o +obj-$(CONFIG_INTERCONNECT_QCOM_MSM8998) += qnoc-msm8998.o obj-$(CONFIG_INTERCONNECT_QCOM_OSM_L3) += icc-osm-l3.o obj-$(CONFIG_INTERCONNECT_QCOM_QCM2290) += qnoc-qcm2290.o obj-$(CONFIG_INTERCONNECT_QCOM_QCS404) += qnoc-qcs404.o diff --git a/drivers/interconnect/qcom/msm8998.c b/drivers/interconnect/qcom/msm8998.c new file mode 100644 index 000000000000..764c2fc18a62 --- /dev/null +++ b/drivers/interconnect/qcom/msm8998.c @@ -0,0 +1,981 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Qualcomm MSM8998 Network-on-Chip (NoC) QoS driver + * Copyright (c) 2020, AngeloGioacchino Del Regno + * + * Copyright (C) 2020, Konrad Dybcio + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "smd-rpm.h" + +#define RPM_BUS_MASTER_REQ 0x73616d62 +#define RPM_BUS_SLAVE_REQ 0x766c7362 + +/* BIMC QoS */ +#define M_BKE_REG_BASE(n) (0x300 + (0x4000 * n)) +#define M_BKE_EN_ADDR(n) (M_BKE_REG_BASE(n)) +#define M_BKE_HEALTH_CFG_ADDR(i, n) (M_BKE_REG_BASE(n) + 0x40 + (0x4 * i)) + +#define M_BKE_HEALTH_CFG_LIMITCMDS_MASK 0x80000000 +#define M_BKE_HEALTH_CFG_AREQPRIO_MASK 0x300 +#define M_BKE_HEALTH_CFG_PRIOLVL_MASK 0x3 +#define M_BKE_HEALTH_CFG_AREQPRIO_SHIFT 0x8 +#define M_BKE_HEALTH_CFG_LIMITCMDS_SHIFT 0x1f + +#define M_BKE_EN_EN_BMASK 0x1 + +/* Valid for both NoC and BIMC */ +#define NOC_QOS_MODE_FIXED 0x0 +#define NOC_QOS_MODE_LIMITER 0x1 +#define NOC_QOS_MODE_BYPASS 0x2 + +/* NoC QoS */ +#define NOC_PERM_MODE_FIXED 1 +#define NOC_PERM_MODE_BYPASS (1 << NOC_QOS_MODE_BYPASS) + +#define NOC_QOS_PRIORITYn_ADDR(n) (0x8 + (n * 0x1000)) +#define NOC_QOS_PRIORITY_MASK 0xf +#define NOC_QOS_PRIORITY_P1_SHIFT 0x2 +#define NOC_QOS_PRIORITY_P0_SHIFT 0x3 + +#define NOC_QOS_MODEn_ADDR(n) (0xc + (n * 0x1000)) +#define NOC_QOS_MODEn_MASK 0x3 + +enum { + MSM8998_MASTER_IPA = 1, + MSM8998_MASTER_CNOC_A2NOC, + MSM8998_MASTER_SDCC_2, + MSM8998_MASTER_SDCC_4, + MSM8998_MASTER_BLSP_1, + MSM8998_MASTER_BLSP_2, + MSM8998_MASTER_UFS, + MSM8998_MASTER_USB_HS, + MSM8998_MASTER_USB3, + MSM8998_MASTER_CRYPTO_C0, + MSM8998_MASTER_GNOC_BIMC, + MSM8998_MASTER_OXILI, + MSM8998_MASTER_MNOC_BIMC, + MSM8998_MASTER_SNOC_BIMC, + MSM8998_MASTER_PIMEM, + MSM8998_MASTER_SNOC_CNOC, + MSM8998_MASTER_QDSS_DAP, + MSM8998_MASTER_APPS_PROC, + MSM8998_MASTER_CNOC_MNOC_MMSS_CFG, + MSM8998_MASTER_CNOC_MNOC_CFG, + MSM8998_MASTER_CPP, + MSM8998_MASTER_JPEG, + MSM8998_MASTER_MDP_P0, + MSM8998_MASTER_MDP_P1, + MSM8998_MASTER_VENUS, + MSM8998_MASTER_VFE, + MSM8998_MASTER_QDSS_ETR, + MSM8998_MASTER_QDSS_BAM, + MSM8998_MASTER_SNOC_CFG, + MSM8998_MASTER_BIMC_SNOC, + MSM8998_MASTER_A1NOC_SNOC, + MSM8998_MASTER_A2NOC_SNOC, + MSM8998_MASTER_GNOC_SNOC, + MSM8998_MASTER_PCIE_0, + MSM8998_MASTER_A2NOC_TSIF, + MSM8998_MASTER_CRVIRT_A2NOC, + MSM8998_MASTER_ROTATOR, + MSM8998_MASTER_VENUS_VMEM, + MSM8998_MASTER_HMSS, + MSM8998_MASTER_BIMC_SNOC_0, + MSM8998_MASTER_BIMC_SNOC_1, + + MSM8998_SLAVE_A1NOC_SNOC, + MSM8998_SLAVE_A2NOC_SNOC, + MSM8998_SLAVE_EBI, + MSM8998_SLAVE_HMSS_L3, + MSM8998_SLAVE_CNOC_A2NOC, + MSM8998_SLAVE_MPM, + MSM8998_SLAVE_PMIC_ARB, + MSM8998_SLAVE_TLMM_NORTH, + MSM8998_SLAVE_TCSR, + MSM8998_SLAVE_PIMEM_CFG, + MSM8998_SLAVE_IMEM_CFG, + MSM8998_SLAVE_MESSAGE_RAM, + MSM8998_SLAVE_GLM, + MSM8998_SLAVE_BIMC_CFG, + MSM8998_SLAVE_PRNG, + MSM8998_SLAVE_SPDM, + MSM8998_SLAVE_QDSS_CFG, + MSM8998_SLAVE_CNOC_MNOC_CFG, + MSM8998_SLAVE_SNOC_CFG, + MSM8998_SLAVE_QM_CFG, + MSM8998_SLAVE_CLK_CTL, + MSM8998_SLAVE_MSS_CFG, + MSM8998_SLAVE_UFS_CFG, + MSM8998_SLAVE_A2NOC_CFG, + MSM8998_SLAVE_A2NOC_SMMU_CFG, + MSM8998_SLAVE_GPUSS_CFG, + MSM8998_SLAVE_AHB2PHY, + MSM8998_SLAVE_BLSP_1, + MSM8998_SLAVE_SDCC_2, + MSM8998_SLAVE_SDCC_4, + MSM8998_SLAVE_BLSP_2, + MSM8998_SLAVE_PDM, + MSM8998_SLAVE_CNOC_MNOC_MMSS_CFG, + MSM8998_SLAVE_USB_HS, + MSM8998_SLAVE_USB3_0, + MSM8998_SLAVE_SRVC_CNOC, + MSM8998_SLAVE_GNOC_BIMC, + MSM8998_SLAVE_GNOC_SNOC, + MSM8998_SLAVE_CAMERA_CFG, + MSM8998_SLAVE_CAMERA_THROTTLE_CFG, + MSM8998_SLAVE_MISC_CFG, + MSM8998_SLAVE_VENUS_THROTTLE_CFG, + MSM8998_SLAVE_VENUS_CFG, + MSM8998_SLAVE_MMSS_CLK_XPU_CFG, + MSM8998_SLAVE_MMSS_CLK_CFG, + MSM8998_SLAVE_MNOC_MPU_CFG, + MSM8998_SLAVE_DISPLAY_CFG, + MSM8998_SLAVE_CSI_PHY_CFG, + MSM8998_SLAVE_DISPLAY_THROTTLE_CFG, + MSM8998_SLAVE_SMMU_CFG, + MSM8998_SLAVE_MNOC_BIMC, + MSM8998_SLAVE_SRVC_MNOC, + MSM8998_SLAVE_HMSS, + MSM8998_SLAVE_LPASS, + MSM8998_SLAVE_WLAN, + MSM8998_SLAVE_CDSP, + MSM8998_SLAVE_IPA, + MSM8998_SLAVE_SNOC_BIMC, + MSM8998_SLAVE_SNOC_CNOC, + MSM8998_SLAVE_IMEM, + MSM8998_SLAVE_PIMEM, + MSM8998_SLAVE_QDSS_STM, + MSM8998_SLAVE_SRVC_SNOC, + MSM8998_SLAVE_BIMC_SNOC_0, + MSM8998_SLAVE_BIMC_SNOC_1, + MSM8998_SLAVE_SSC_CFG, + MSM8998_SLAVE_SKL, + MSM8998_SLAVE_TLMM_WEST, + MSM8998_SLAVE_A1NOC_CFG, + MSM8998_SLAVE_A1NOC_SMMU_CFG, + MSM8998_SLAVE_TSIF, + MSM8998_SLAVE_TLMM_EAST, + MSM8998_SLAVE_CRVIRT_A2NOC, + MSM8998_SLAVE_VMEM_CFG, + MSM8998_SLAVE_VMEM, + MSM8998_SLAVE_PCIE_0, +}; + +#define to_qcom_provider(_provider) \ + container_of(_provider, struct qcom_icc_provider, provider) + +static const struct clk_bulk_data bus_clocks[] = { + { .id = "bus" }, + { .id = "bus_a" }, +}; + +static const struct clk_bulk_data bus_mm_clocks[] = { + { .id = "bus" }, + { .id = "bus_a" }, + { .id = "iface" }, +}; + +/** + * struct qcom_icc_provider - Qualcomm specific interconnect provider + * @provider: generic interconnect provider + * @bus_clks: the clk_bulk_data table of bus clocks + * @num_clks: the total number of clk_bulk_data entries + * @is_bimc_node: indicates whether to use bimc specific setting + * @mmio: NoC base iospace + */ +struct qcom_icc_provider { + struct icc_provider provider; + struct clk_bulk_data *bus_clks; + int num_clks; + bool is_bimc_node; + struct regmap *regmap; + void __iomem *mmio; +}; + +#define MSM8998_MAX_LINKS 38 + +/** + * struct qcom_icc_qos - Qualcomm specific interconnect QoS parameters + * @areq_prio: node requests priority + * @prio_level: priority level for bus communication + * @limit_commands: activate/deactivate limiter mode during runtime + * @ap_owned: indicates if the node is owned by the AP or by the RPM + * @qos_mode: default qos mode for this node + * @qos_port: qos port number for finding qos registers of this node + */ +struct qcom_icc_qos { + u32 areq_prio; + u32 prio_level; + bool limit_commands; + bool ap_owned; + int qos_mode; + int qos_port; +}; + +/** + * struct qcom_icc_node - Qualcomm specific interconnect nodes + * @name: the node name used in debugfs + * @id: a unique node identifier + * @links: an array of nodes where we can go next while traversing + * @num_links: the total number of @links + * @buswidth: width of the interconnect between a node and the bus (bytes) + * @mas_rpm_id: RPM id for devices that are bus masters + * @slv_rpm_id: RPM id for devices that are bus slaves + * @qos: NoC QoS setting parameters + * @rate: current bus clock rate in Hz + */ +struct qcom_icc_node { + unsigned char *name; + u16 id; + u16 links[MSM8998_MAX_LINKS]; + u16 num_links; + u16 buswidth; + int mas_rpm_id; + int slv_rpm_id; + struct qcom_icc_qos qos; + u64 rate; +}; + +struct qcom_icc_desc { + struct qcom_icc_node **nodes; + size_t num_nodes; + const struct regmap_config *regmap_cfg; +}; + +#define DEFINE_QNODE(_name, _id, _buswidth, _mas_rpm_id, _slv_rpm_id, \ + _ap_owned, _qos_mode, _qos_prio, _qos_port, ...) \ + static struct qcom_icc_node _name = { \ + .name = #_name, \ + .id = _id, \ + .buswidth = _buswidth, \ + .mas_rpm_id = _mas_rpm_id, \ + .slv_rpm_id = _slv_rpm_id, \ + .qos.ap_owned = _ap_owned, \ + .qos.qos_mode = _qos_mode, \ + .qos.areq_prio = _qos_prio, \ + .qos.prio_level = _qos_prio, \ + .qos.qos_port = _qos_port, \ + .num_links = ARRAY_SIZE(((int[]){ __VA_ARGS__ })), \ + .links = { __VA_ARGS__ }, \ + } + +/* masters */ +DEFINE_QNODE(mas_pcie_0, MSM8998_MASTER_PCIE_0, 16, 45, -1, true, NOC_QOS_MODE_FIXED, 1, 1, MSM8998_SLAVE_A1NOC_SNOC); +DEFINE_QNODE(mas_usb3, MSM8998_MASTER_USB3, 16, 32, -1, true, NOC_QOS_MODE_FIXED, 1, 2, MSM8998_SLAVE_A1NOC_SNOC); +DEFINE_QNODE(mas_ufs, MSM8998_MASTER_UFS, 16, 68, -1, true, NOC_QOS_MODE_FIXED, 1, 2, MSM8998_SLAVE_A1NOC_SNOC); +DEFINE_QNODE(mas_blsp_2, MSM8998_MASTER_BLSP_2, 16, 39, -1, false, NOC_QOS_MODE_FIXED, 0, 4, MSM8998_SLAVE_A1NOC_SNOC); +DEFINE_QNODE(mas_cnoc_a2noc, MSM8998_MASTER_CNOC_A2NOC, 8, 146, -1, true, -1, 0, -1, MSM8998_SLAVE_A2NOC_SNOC); +DEFINE_QNODE(mas_ipa, MSM8998_MASTER_IPA, 8, 59, -1, true, NOC_QOS_MODE_FIXED, 1, 1, MSM8998_SLAVE_A2NOC_SNOC); +DEFINE_QNODE(mas_sdcc_2, MSM8998_MASTER_SDCC_2, 8, 35, -1, false, NOC_QOS_MODE_FIXED, 0, 6, MSM8998_SLAVE_A2NOC_SNOC); +DEFINE_QNODE(mas_sdcc_4, MSM8998_MASTER_SDCC_4, 8, 36, -1, false, NOC_QOS_MODE_FIXED, 0, 7, MSM8998_SLAVE_A2NOC_SNOC); +DEFINE_QNODE(mas_tsif, MSM8998_MASTER_A2NOC_TSIF, 4, 37, -1, true, -1, 0, -1, MSM8998_SLAVE_A2NOC_SNOC); +DEFINE_QNODE(mas_blsp_1, MSM8998_MASTER_BLSP_1, 16, 41, -1, false, NOC_QOS_MODE_FIXED, 0, 8, MSM8998_SLAVE_A2NOC_SNOC); +DEFINE_QNODE(mas_crvirt_a2noc, MSM8998_MASTER_CRVIRT_A2NOC, 8, 145, -1, false, NOC_QOS_MODE_FIXED, 0, 9, MSM8998_SLAVE_A2NOC_SNOC); +DEFINE_QNODE(mas_gnoc_bimc, MSM8998_MASTER_GNOC_BIMC, 8, 144, -1, true, NOC_QOS_MODE_FIXED, 0, 0, MSM8998_SLAVE_EBI, MSM8998_SLAVE_BIMC_SNOC_0); +DEFINE_QNODE(mas_oxili, MSM8998_MASTER_OXILI, 8, 6, -1, true, NOC_QOS_MODE_BYPASS, 0, 1, MSM8998_SLAVE_BIMC_SNOC_1, MSM8998_SLAVE_HMSS_L3, MSM8998_SLAVE_EBI, MSM8998_SLAVE_BIMC_SNOC_0); +DEFINE_QNODE(mas_mnoc_bimc, MSM8998_MASTER_MNOC_BIMC, 8, 2, -1, true, NOC_QOS_MODE_BYPASS, 0, 2, MSM8998_SLAVE_BIMC_SNOC_1, MSM8998_SLAVE_HMSS_L3, MSM8998_SLAVE_EBI, MSM8998_SLAVE_BIMC_SNOC_0); +DEFINE_QNODE(mas_snoc_bimc, MSM8998_MASTER_SNOC_BIMC, 8, 3, -1, false, NOC_QOS_MODE_BYPASS, 0, 3, MSM8998_SLAVE_HMSS_L3, MSM8998_SLAVE_EBI); +DEFINE_QNODE(mas_snoc_cnoc, MSM8998_MASTER_SNOC_CNOC, 8, 52, -1, true, -1, 0, -1, MSM8998_SLAVE_SKL, MSM8998_SLAVE_BLSP_2, MSM8998_SLAVE_MESSAGE_RAM, MSM8998_SLAVE_TLMM_WEST, MSM8998_SLAVE_TSIF, MSM8998_SLAVE_MPM, MSM8998_SLAVE_BIMC_CFG, MSM8998_SLAVE_TLMM_EAST, MSM8998_SLAVE_SPDM, MSM8998_SLAVE_PIMEM_CFG, MSM8998_SLAVE_A1NOC_SMMU_CFG, MSM8998_SLAVE_BLSP_1, MSM8998_SLAVE_CLK_CTL, MSM8998_SLAVE_PRNG, MSM8998_SLAVE_USB3_0, MSM8998_SLAVE_QDSS_CFG, MSM8998_SLAVE_QM_CFG, MSM8998_SLAVE_A2NOC_CFG, MSM8998_SLAVE_PMIC_ARB, MSM8998_SLAVE_UFS_CFG, MSM8998_SLAVE_SRVC_CNOC, MSM8998_SLAVE_AHB2PHY, MSM8998_SLAVE_IPA, MSM8998_SLAVE_GLM, MSM8998_SLAVE_SNOC_CFG, MSM8998_SLAVE_SSC_CFG, MSM8998_SLAVE_SDCC_2, MSM8998_SLAVE_SDCC_4, MSM8998_SLAVE_PDM, MSM8998_SLAVE_CNOC_MNOC_MMSS_CFG, MSM8998_SLAVE_CNOC_MNOC_CFG, MSM8998_SLAVE_MSS_CFG, MSM8998_SLAVE_IMEM_CFG, MSM8998_SLAVE_A1NOC_CFG, MSM8998_SLAVE_GPUSS_CFG, MSM8998_SLAVE_TCSR, MSM8998_SLAVE_TLMM_NORTH); +DEFINE_QNODE(mas_qdss_dap, MSM8998_MASTER_QDSS_DAP, 8, 49, -1, true, -1, 0, -1, MSM8998_SLAVE_SKL, MSM8998_SLAVE_BLSP_2, MSM8998_SLAVE_MESSAGE_RAM, MSM8998_SLAVE_TLMM_WEST, MSM8998_SLAVE_TSIF, MSM8998_SLAVE_MPM, MSM8998_SLAVE_BIMC_CFG, MSM8998_SLAVE_TLMM_EAST, MSM8998_SLAVE_SPDM, MSM8998_SLAVE_PIMEM_CFG, MSM8998_SLAVE_A1NOC_SMMU_CFG, MSM8998_SLAVE_BLSP_1, MSM8998_SLAVE_CLK_CTL, MSM8998_SLAVE_PRNG, MSM8998_SLAVE_USB3_0, MSM8998_SLAVE_QDSS_CFG, MSM8998_SLAVE_QM_CFG, MSM8998_SLAVE_A2NOC_CFG, MSM8998_SLAVE_PMIC_ARB, MSM8998_SLAVE_UFS_CFG, MSM8998_SLAVE_SRVC_CNOC, MSM8998_SLAVE_AHB2PHY, MSM8998_SLAVE_IPA, MSM8998_SLAVE_GLM, MSM8998_SLAVE_SNOC_CFG, MSM8998_SLAVE_SDCC_2, MSM8998_SLAVE_SDCC_4, MSM8998_SLAVE_PDM, MSM8998_SLAVE_CNOC_MNOC_MMSS_CFG, MSM8998_SLAVE_CNOC_MNOC_CFG, MSM8998_SLAVE_MSS_CFG, MSM8998_SLAVE_IMEM_CFG, MSM8998_SLAVE_A1NOC_CFG, MSM8998_SLAVE_GPUSS_CFG, MSM8998_SLAVE_SSC_CFG, MSM8998_SLAVE_TCSR, MSM8998_SLAVE_TLMM_NORTH, MSM8998_SLAVE_CNOC_A2NOC); +DEFINE_QNODE(mas_crypto, MSM8998_MASTER_CRYPTO_C0, 650, 23, -1, false, -1, 0, -1, MSM8998_MASTER_CRVIRT_A2NOC); +DEFINE_QNODE(mas_apss_proc, MSM8998_MASTER_APPS_PROC, 32, 0, -1, true, -1, 0, -1, MSM8998_SLAVE_GNOC_BIMC); +DEFINE_QNODE(mas_cnoc_mnoc_mmss_cfg, MSM8998_MASTER_CNOC_MNOC_MMSS_CFG, 8, 4, -1, true, -1, 0, -1, MSM8998_SLAVE_CAMERA_THROTTLE_CFG, MSM8998_SLAVE_VENUS_CFG, MSM8998_SLAVE_MISC_CFG, MSM8998_SLAVE_CAMERA_CFG, MSM8998_SLAVE_DISPLAY_THROTTLE_CFG, MSM8998_SLAVE_VENUS_THROTTLE_CFG, MSM8998_SLAVE_DISPLAY_CFG, MSM8998_SLAVE_MMSS_CLK_CFG, MSM8998_SLAVE_VMEM_CFG, MSM8998_SLAVE_MMSS_CLK_XPU_CFG, MSM8998_SLAVE_SMMU_CFG); +DEFINE_QNODE(mas_cnoc_mnoc_cfg, MSM8998_MASTER_CNOC_MNOC_CFG, 8, 5, -1, true, -1, 0, -1, MSM8998_SLAVE_SRVC_MNOC); +DEFINE_QNODE(mas_cpp, MSM8998_MASTER_CPP, 32, 115, -1, true, NOC_QOS_MODE_BYPASS, 0, 5, MSM8998_SLAVE_MNOC_BIMC); +DEFINE_QNODE(mas_jpeg, MSM8998_MASTER_JPEG, 32, 7, -1, true, NOC_QOS_MODE_BYPASS, 0, 7, MSM8998_SLAVE_MNOC_BIMC); +DEFINE_QNODE(mas_mdp_p0, MSM8998_MASTER_MDP_P0, 32, 8, -1, true, NOC_QOS_MODE_BYPASS, 0, 1, MSM8998_SLAVE_MNOC_BIMC); /* vrail-comp???? */ +DEFINE_QNODE(mas_mdp_p1, MSM8998_MASTER_MDP_P1, 32, 61, -1, true, NOC_QOS_MODE_BYPASS, 0, 2, MSM8998_SLAVE_MNOC_BIMC); /* vrail-comp??? */ +DEFINE_QNODE(mas_rotator, MSM8998_MASTER_ROTATOR, 32, 120, -1, true, NOC_QOS_MODE_BYPASS, 0, 0, MSM8998_SLAVE_MNOC_BIMC); +DEFINE_QNODE(mas_venus, MSM8998_MASTER_VENUS, 32, 9, -1, true, NOC_QOS_MODE_BYPASS, 0, 3, MSM8998_SLAVE_MNOC_BIMC); +DEFINE_QNODE(mas_vfe, MSM8998_MASTER_VFE, 32, 11, -1, true, NOC_QOS_MODE_BYPASS, 0, 6, MSM8998_SLAVE_MNOC_BIMC); +DEFINE_QNODE(mas_venus_vmem, MSM8998_MASTER_VENUS_VMEM, 32, 121, -1, true, -1, 0, -1, MSM8998_SLAVE_VMEM); +DEFINE_QNODE(mas_hmss, MSM8998_MASTER_HMSS, 16, 118, -1, true, NOC_QOS_MODE_FIXED, 1, 3, MSM8998_SLAVE_PIMEM, MSM8998_SLAVE_IMEM, MSM8998_SLAVE_SNOC_BIMC); +DEFINE_QNODE(mas_qdss_bam, MSM8998_MASTER_QDSS_BAM, 16, 19, -1, true, NOC_QOS_MODE_FIXED, 1, 1, MSM8998_SLAVE_IMEM, MSM8998_SLAVE_PIMEM, MSM8998_SLAVE_SNOC_CNOC, MSM8998_SLAVE_SNOC_BIMC); +DEFINE_QNODE(mas_snoc_cfg, MSM8998_MASTER_SNOC_CFG, 16, 20, -1, false, -1, 0, -1, MSM8998_SLAVE_SRVC_SNOC); +DEFINE_QNODE(mas_bimc_snoc_0, MSM8998_MASTER_BIMC_SNOC_0, 16, 21, -1, false, -1, 0, -1, MSM8998_SLAVE_PIMEM, MSM8998_SLAVE_LPASS, MSM8998_SLAVE_HMSS, MSM8998_SLAVE_WLAN, MSM8998_SLAVE_SNOC_CNOC, MSM8998_SLAVE_IMEM, MSM8998_SLAVE_QDSS_STM); +DEFINE_QNODE(mas_bimc_snoc_1, MSM8998_MASTER_BIMC_SNOC_1, 16, 109, -1, true, -1, 0, -1, MSM8998_SLAVE_PCIE_0); +DEFINE_QNODE(mas_a1noc_snoc, MSM8998_MASTER_A1NOC_SNOC, 16, 111, -1, false, -1, 0, -1, MSM8998_SLAVE_PIMEM, MSM8998_SLAVE_PCIE_0, MSM8998_SLAVE_LPASS, MSM8998_SLAVE_HMSS, MSM8998_SLAVE_SNOC_BIMC, MSM8998_SLAVE_SNOC_CNOC, MSM8998_SLAVE_IMEM, MSM8998_SLAVE_QDSS_STM); +DEFINE_QNODE(mas_a2noc_snoc, MSM8998_MASTER_A2NOC_SNOC, 16, 112, -1, false, -1, 0, -1, MSM8998_SLAVE_PIMEM, MSM8998_SLAVE_PCIE_0, MSM8998_SLAVE_LPASS, MSM8998_SLAVE_HMSS, MSM8998_SLAVE_SNOC_BIMC, MSM8998_SLAVE_WLAN, MSM8998_SLAVE_SNOC_CNOC, MSM8998_SLAVE_IMEM, MSM8998_SLAVE_QDSS_STM); +DEFINE_QNODE(mas_qdss_etr, MSM8998_MASTER_QDSS_ETR, 16, 31, -1, true, NOC_QOS_MODE_FIXED, 1, 2, MSM8998_SLAVE_IMEM, MSM8998_MASTER_PIMEM, MSM8998_SLAVE_SNOC_CNOC, MSM8998_SLAVE_SNOC_BIMC); + +/* slaves */ +DEFINE_QNODE(slv_a1noc_snoc, MSM8998_SLAVE_A1NOC_SNOC, 16, -1, 142, false, -1, 0, -1, MSM8998_MASTER_A1NOC_SNOC); +DEFINE_QNODE(slv_a2noc_snoc, MSM8998_SLAVE_A2NOC_SNOC, 16, -1, 143, false, -1, 0, -1, MSM8998_MASTER_A2NOC_SNOC); +DEFINE_QNODE(slv_ebi, MSM8998_SLAVE_EBI, 8, -1, 0, false, -1, 0, -1, 0); +DEFINE_QNODE(slv_hmss_l3, MSM8998_SLAVE_HMSS_L3, 8, -1, 160, false, -1, 0, -1, 0); +DEFINE_QNODE(slv_bimc_snoc_0, MSM8998_SLAVE_BIMC_SNOC_0, 8, -1, 2, false, -1, 0, -1, MSM8998_MASTER_BIMC_SNOC_0); +DEFINE_QNODE(slv_bimc_snoc_1, MSM8998_SLAVE_BIMC_SNOC_1, 8, -1, 138, true, -1, 0, -1, MSM8998_MASTER_BIMC_SNOC_1); +DEFINE_QNODE(slv_cnoc_a2noc, MSM8998_SLAVE_CNOC_A2NOC, 4, -1, 208, true, -1, 0, -1, MSM8998_MASTER_CNOC_A2NOC); +DEFINE_QNODE(slv_ssc_cfg, MSM8998_SLAVE_SSC_CFG, 4, -1, 177, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_mpm, MSM8998_SLAVE_MPM, 4, -1, 62, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_pmic_arb, MSM8998_SLAVE_PMIC_ARB, 4, -1, 59, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_tlmm_north, MSM8998_SLAVE_TLMM_NORTH, 4, -1, 214, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_pimem_cfg, MSM8998_SLAVE_PIMEM_CFG, 4, -1, 167, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_imem_cfg, MSM8998_SLAVE_IMEM_CFG, 4, -1, 54, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_message_ram, MSM8998_SLAVE_MESSAGE_RAM, 4, -1, 55, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_skl, MSM8998_SLAVE_SKL, 4, -1, 196, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_bimc_cfg, MSM8998_SLAVE_BIMC_CFG, 4, -1, 56, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_prng, MSM8998_SLAVE_PRNG, 4, -1, 44, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_a2noc_cfg, MSM8998_SLAVE_A2NOC_CFG, 4, -1, 150, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_ipa, MSM8998_SLAVE_IPA, 4, -1, 183, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_tcsr, MSM8998_SLAVE_TCSR, 4, -1, 50, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_snoc_cfg, MSM8998_SLAVE_SNOC_CFG, 4, -1, 70, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_clk_ctl, MSM8998_SLAVE_CLK_CTL, 4, -1, 47, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_glm, MSM8998_SLAVE_GLM, 4, -1, 209, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_spdm, MSM8998_SLAVE_SPDM, 4, -1, 60, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_gpuss_cfg, MSM8998_SLAVE_GPUSS_CFG, 4, -1, 11, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_cnoc_mnoc_cfg, MSM8998_SLAVE_BLSP_1, 4, -1, 66, true, -1, 0, -1, MSM8998_MASTER_CNOC_MNOC_CFG); +DEFINE_QNODE(slv_qm_cfg, MSM8998_SLAVE_QM_CFG, 4, -1, 212, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_mss_cfg, MSM8998_SLAVE_MSS_CFG, 4, -1, 48, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_ufs_cfg, MSM8998_SLAVE_UFS_CFG, 4, -1, 92, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_tlmm_west, MSM8998_SLAVE_TLMM_WEST, 4, -1, 215, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_a1noc_cfg, MSM8998_SLAVE_A1NOC_CFG, 4, -1, 147, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_ahb2phy, MSM8998_SLAVE_AHB2PHY, 4, -1, 163, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_blsp_2, MSM8998_SLAVE_BLSP_2, 4, -1, 37, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_pdm, MSM8998_SLAVE_PDM, 4, -1, 41, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_usb3_0, MSM8998_SLAVE_USB3_0, 4, -1, 22, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_a1noc_smmu_cfg, MSM8998_SLAVE_A1NOC_SMMU_CFG, 8, -1, 149, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_blsp_1, MSM8998_SLAVE_BLSP_1, 4, -1, 39, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_sdcc_2, MSM8998_SLAVE_SDCC_2, 4, -1, 33, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_sdcc_4, MSM8998_SLAVE_SDCC_4, 4, -1, 34, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_tsif, MSM8998_SLAVE_TSIF, 4, -1, 35, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_qdss_cfg, MSM8998_SLAVE_QDSS_CFG, 4, -1, 63, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_tlmm_east, MSM8998_SLAVE_TLMM_EAST, 4, -1, 213, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_cnoc_mnoc_mmss_cfg, MSM8998_SLAVE_CNOC_MNOC_MMSS_CFG, 8, -1, 58, true, -1, 0, -1, MSM8998_MASTER_CNOC_MNOC_MMSS_CFG); +DEFINE_QNODE(slv_srvc_cnoc, MSM8998_SLAVE_SRVC_CNOC, 4, -1, 76, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_crvirt_a2noc, MSM8998_SLAVE_CRVIRT_A2NOC, 8, -1, 207, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_gnoc_bimc, MSM8998_SLAVE_GNOC_BIMC, 32, -1, 210, true, -1, 0, -1, MSM8998_MASTER_GNOC_BIMC); +DEFINE_QNODE(slv_camera_cfg, MSM8998_SLAVE_CAMERA_CFG, 8, -1, 3, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_camera_throttle_cfg, MSM8998_SLAVE_CAMERA_THROTTLE_CFG, 8, -1, 154, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_misc_cfg, MSM8998_SLAVE_MISC_CFG, 8, -1, 8, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_venus_throttle_cfg, MSM8998_SLAVE_VENUS_THROTTLE_CFG, 8, -1, 178, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_venus_cfg, MSM8998_SLAVE_VENUS_CFG, 8, -1, 10, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_vmem_cfg, MSM8998_SLAVE_VMEM_CFG, 8, -1, 180, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_mmss_clk_xpu_cfg, MSM8998_SLAVE_MMSS_CLK_XPU_CFG, 8, -1, 13, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_mmss_clk_cfg, MSM8998_SLAVE_MMSS_CLK_CFG, 8, -1, 12, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_display_cfg, MSM8998_SLAVE_DISPLAY_CFG, 8, -1, 4, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_display_throttle_cfg, MSM8998_SLAVE_DISPLAY_THROTTLE_CFG, 4, -1, 156, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_smmu_cfg, MSM8998_SLAVE_SMMU_CFG, 8, -1, 205, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_mnoc_bimc, MSM8998_SLAVE_MNOC_BIMC, 32, -1, 16, true, -1, 0, -1, MSM8998_MASTER_MNOC_BIMC); +DEFINE_QNODE(slv_vmem, MSM8998_SLAVE_VMEM, 32, -1, 179, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_srvc_mnoc, MSM8998_SLAVE_SRVC_MNOC, 8, -1, 17, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_hmss, MSM8998_SLAVE_HMSS, 16, -1, 20, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_lpass, MSM8998_SLAVE_LPASS, 16, -1, 21, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_wlan, MSM8998_SLAVE_WLAN, 16, -1, 206, false, -1, 0, -1, 0); +DEFINE_QNODE(slv_snoc_bimc, MSM8998_SLAVE_SNOC_BIMC, 32, -1, 24, false, -1, 0, -1, MSM8998_MASTER_SNOC_BIMC); +DEFINE_QNODE(slv_snoc_cnoc, MSM8998_SLAVE_SNOC_CNOC, 16, -1, 25, false, -1, 0, -1, MSM8998_MASTER_SNOC_CNOC); +DEFINE_QNODE(slv_imem, MSM8998_SLAVE_IMEM, 16, -1, 26, false, -1, 0, -1, 0); +DEFINE_QNODE(slv_pimem, MSM8998_SLAVE_PIMEM, 16, -1, 166, false, -1, 0, -1, 0); +DEFINE_QNODE(slv_qdss_stm, MSM8998_SLAVE_QDSS_STM, 16, -1, 30, false, -1, 0, -1, 0); +DEFINE_QNODE(slv_pcie_0, MSM8998_SLAVE_PCIE_0, 16, -1, 84, true, -1, 0, -1, 0); +DEFINE_QNODE(slv_srvc_snoc, MSM8998_SLAVE_SRVC_SNOC, 16, -1, 29, false, -1, 0, -1, 0); + +static struct qcom_icc_node *msm8998_a1noc_nodes[] = { + [MASTER_PCIE_0] = &mas_pcie_0, + [MASTER_USB3] = &mas_usb3, + [MASTER_UFS] = &mas_ufs, + [MASTER_BLSP_2] = &mas_blsp_2, + [SLAVE_A1NOC_SNOC] = &slv_a1noc_snoc, +}; + +static const struct regmap_config msm8998_a1noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x60000, + .fast_io = true, +}; + +static struct qcom_icc_desc msm8998_a1noc = { + .nodes = msm8998_a1noc_nodes, + .num_nodes = ARRAY_SIZE(msm8998_a1noc_nodes), + .regmap_cfg = &msm8998_a1noc_regmap_config, +}; + +static struct qcom_icc_node *msm8998_a2noc_nodes[] = { + [MASTER_IPA] = &mas_ipa, + [MASTER_CNOC_A2NOC] = &mas_cnoc_a2noc, + [MASTER_SDCC_2] = &mas_sdcc_2, + [MASTER_SDCC_4] = &mas_sdcc_4, + [MASTER_TSIF] = &mas_tsif, + [MASTER_BLSP_1] = &mas_blsp_1, + [MASTER_CRVIRT_A2NOC] = &mas_crvirt_a2noc, + [MASTER_CRYPTO_C0] = &mas_crypto, + [SLAVE_A2NOC_SNOC] = &slv_a2noc_snoc, + [SLAVE_CRVIRT_A2NOC] = &slv_crvirt_a2noc, +}; + +static const struct regmap_config msm8998_a2noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x60000, + .fast_io = true, +}; + +static struct qcom_icc_desc msm8998_a2noc = { + .nodes = msm8998_a2noc_nodes, + .num_nodes = ARRAY_SIZE(msm8998_a2noc_nodes), + .regmap_cfg = &msm8998_a2noc_regmap_config, +}; + +static struct qcom_icc_node *msm8998_bimc_nodes[] = { + [MASTER_GNOC_BIMC] = &mas_gnoc_bimc, + [MASTER_OXILI] = &mas_oxili, + [MASTER_MNOC_BIMC] = &mas_mnoc_bimc, + [MASTER_SNOC_BIMC] = &mas_snoc_bimc, + [SLAVE_EBI] = &slv_ebi, + [SLAVE_HMSS_L3] = &slv_hmss_l3, + [SLAVE_BIMC_SNOC_0] = &slv_bimc_snoc_0, + [SLAVE_BIMC_SNOC_1] = &slv_bimc_snoc_1, +}; + +static const struct regmap_config msm8998_bimc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x80000, + .fast_io = true, +}; + +static struct qcom_icc_desc msm8998_bimc = { + .nodes = msm8998_bimc_nodes, + .num_nodes = ARRAY_SIZE(msm8998_bimc_nodes), + .regmap_cfg = &msm8998_bimc_regmap_config, +}; + +static struct qcom_icc_node *msm8998_cnoc_nodes[] = { + [MASTER_SNOC_CNOC] = &mas_snoc_cnoc, + [MASTER_QDSS_DAP] = &mas_qdss_dap, + [SLAVE_CNOC_A2NOC] = &slv_cnoc_a2noc, + [SLAVE_SSC_CFG] = &slv_ssc_cfg, + [SLAVE_MPM] = &slv_mpm, + [SLAVE_PMIC_ARB] = &slv_pmic_arb, + [SLAVE_TLMM_NORTH] = &slv_tlmm_north, + [SLAVE_PIMEM_CFG] = &slv_pimem_cfg, + [SLAVE_IMEM_CFG] = &slv_imem_cfg, + [SLAVE_MESSAGE_RAM] = &slv_message_ram, + [SLAVE_SKL] = &slv_skl, + [SLAVE_BIMC_CFG] = &slv_bimc_cfg, + [SLAVE_PRNG] = &slv_prng, + [SLAVE_A2NOC_CFG] = &slv_a2noc_cfg, + [SLAVE_IPA] = &slv_ipa, + [SLAVE_TCSR] = &slv_tcsr, + [SLAVE_SNOC_CFG] = &slv_snoc_cfg, + [SLAVE_CLK_CTL] = &slv_clk_ctl, + [SLAVE_GLM] = &slv_glm, + [SLAVE_SPDM] = &slv_spdm, + [SLAVE_GPUSS_CFG] = &slv_gpuss_cfg, + [SLAVE_CNOC_MNOC_CFG] = &slv_cnoc_mnoc_cfg, + [SLAVE_QM_CFG] = &slv_qm_cfg, + [SLAVE_MSS_CFG] = &slv_mss_cfg, + [SLAVE_UFS_CFG] = &slv_ufs_cfg, + [SLAVE_TLMM_WEST] = &slv_tlmm_west, + [SLAVE_A1NOC_CFG] = &slv_a1noc_cfg, + [SLAVE_AHB2PHY] = &slv_ahb2phy, + [SLAVE_BLSP_2] = &slv_blsp_2, + [SLAVE_PDM] = &slv_pdm, + [SLAVE_USB3_0] = &slv_usb3_0, + [SLAVE_A1NOC_SMMU_CFG] = &slv_a1noc_smmu_cfg, + [SLAVE_BLSP_1] = &slv_blsp_1, + [SLAVE_SDCC_2] = &slv_sdcc_2, + [SLAVE_SDCC_4] = &slv_sdcc_4, + [SLAVE_TSIF] = &slv_tsif, + [SLAVE_QDSS_CFG] = &slv_qdss_cfg, + [SLAVE_TLMM_EAST] = &slv_tlmm_east, + [SLAVE_CNOC_MNOC_MMSS_CFG] = &slv_cnoc_mnoc_mmss_cfg, + [SLAVE_SRVC_CNOC] = &slv_srvc_cnoc, +}; + +static const struct regmap_config msm8998_cnoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x10000, + .fast_io = true, +}; + +static struct qcom_icc_desc msm8998_cnoc = { + .nodes = msm8998_cnoc_nodes, + .num_nodes = ARRAY_SIZE(msm8998_cnoc_nodes), + .regmap_cfg = &msm8998_cnoc_regmap_config, +}; + +static struct qcom_icc_node *msm8998_gnoc_nodes[] = { + [MASTER_APSS_PROC] = &mas_apss_proc, + [SLAVE_GNOC_BIMC] = &slv_gnoc_bimc, +}; + +static const struct regmap_config msm8998_gnoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x10000, + .fast_io = true, +}; + +static struct qcom_icc_desc msm8998_gnoc = { + .nodes = msm8998_gnoc_nodes, + .num_nodes = ARRAY_SIZE(msm8998_gnoc_nodes), + .regmap_cfg = &msm8998_gnoc_regmap_config, +}; + +static struct qcom_icc_node *msm8998_mnoc_nodes[] = { + [MASTER_CNOC_MNOC_CFG] = &mas_cnoc_mnoc_cfg, + [MASTER_CPP] = &mas_cpp, + [MASTER_JPEG] = &mas_jpeg, + [MASTER_MDP_P0] = &mas_mdp_p0, + [MASTER_MDP_P1] = &mas_mdp_p1, + [MASTER_ROTATOR] = &mas_rotator, + [MASTER_VENUS] = &mas_venus, + [MASTER_VFE] = &mas_vfe, + [MASTER_VENUS_VMEM] = &mas_venus_vmem, + [SLAVE_MNOC_BIMC] = &slv_mnoc_bimc, + [SLAVE_VMEM] = &slv_vmem, + [SLAVE_SRVC_MNOC] = &slv_srvc_mnoc, + [MASTER_CNOC_MNOC_MMSS_CFG] = &mas_cnoc_mnoc_mmss_cfg, + [SLAVE_CAMERA_CFG] = &slv_camera_cfg, + [SLAVE_CAMERA_THROTTLE_CFG] = &slv_camera_throttle_cfg, + [SLAVE_MISC_CFG] = &slv_misc_cfg, + [SLAVE_VENUS_THROTTLE_CFG] = &slv_venus_throttle_cfg, + [SLAVE_VENUS_CFG] = &slv_venus_cfg, + [SLAVE_VMEM_CFG] = &slv_vmem_cfg, + [SLAVE_MMSS_CLK_XPU_CFG] = &slv_mmss_clk_xpu_cfg, + [SLAVE_MMSS_CLK_CFG] = &slv_mmss_clk_cfg, + [SLAVE_DISPLAY_CFG] = &slv_display_cfg, + [SLAVE_DISPLAY_THROTTLE_CFG] = &slv_display_throttle_cfg, + [SLAVE_SMMU_CFG] = &slv_smmu_cfg, +}; + +static const struct regmap_config msm8998_mnoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x10000, + .fast_io = true, +}; + +static struct qcom_icc_desc msm8998_mnoc = { + .nodes = msm8998_mnoc_nodes, + .num_nodes = ARRAY_SIZE(msm8998_mnoc_nodes), + .regmap_cfg = &msm8998_mnoc_regmap_config, +}; + +static struct qcom_icc_node *msm8998_snoc_nodes[] = { + [MASTER_HMSS] = &mas_hmss, + [MASTER_QDSS_BAM] = &mas_qdss_bam, + [MASTER_SNOC_CFG] = &mas_snoc_cfg, + [MASTER_BIMC_SNOC_0] = &mas_bimc_snoc_0, + [MASTER_BIMC_SNOC_1] = &mas_bimc_snoc_1, + [MASTER_A1NOC_SNOC] = &mas_a1noc_snoc, + [MASTER_A2NOC_SNOC] = &mas_a2noc_snoc, + [MASTER_QDSS_ETR] = &mas_qdss_etr, + [SLAVE_HMSS] = &slv_hmss, + [SLAVE_LPASS] = &slv_lpass, + [SLAVE_WLAN] = &slv_wlan, + [SLAVE_SNOC_BIMC] = &slv_snoc_bimc, + [SLAVE_SNOC_CNOC] = &slv_snoc_cnoc, + [SLAVE_IMEM] = &slv_imem, + [SLAVE_PIMEM] = &slv_pimem, + [SLAVE_QDSS_STM] = &slv_qdss_stm, + [SLAVE_PCIE_0] = &slv_pcie_0, + [SLAVE_SRVC_SNOC] = &slv_srvc_snoc, +}; + +static const struct regmap_config msm8998_snoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x40000, + .fast_io = true, +}; + +static struct qcom_icc_desc msm8998_snoc = { + .nodes = msm8998_snoc_nodes, + .num_nodes = ARRAY_SIZE(msm8998_snoc_nodes), + .regmap_cfg = &msm8998_snoc_regmap_config, +}; + +static int qcom_icc_bimc_set_qos_health(struct regmap *rmap, + struct qcom_icc_qos *qos, + int regnum) +{ + u32 val; + u32 mask; + + val = qos->prio_level; + mask = M_BKE_HEALTH_CFG_PRIOLVL_MASK; + + val |= qos->areq_prio << M_BKE_HEALTH_CFG_AREQPRIO_SHIFT; + mask |= M_BKE_HEALTH_CFG_AREQPRIO_MASK; + + /* LIMITCMDS is not present on M_BKE_HEALTH_3 */ + if (regnum != 3) { + val |= qos->limit_commands << M_BKE_HEALTH_CFG_LIMITCMDS_SHIFT; + mask |= M_BKE_HEALTH_CFG_LIMITCMDS_MASK; + } + + return regmap_update_bits(rmap, + M_BKE_HEALTH_CFG_ADDR(regnum, qos->qos_port), + mask, val); +} + +static int qcom_icc_set_bimc_qos(struct icc_node *src, u64 max_bw, + bool bypass_mode) +{ + struct qcom_icc_provider *qp; + struct qcom_icc_node *qn; + struct icc_provider *provider; + u32 mode = NOC_QOS_MODE_BYPASS; + u32 val = 0; + int i, rc = 0; + + qn = src->data; + provider = src->provider; + qp = to_qcom_provider(provider); + + if (qn->qos.qos_mode != -1) + mode = qn->qos.qos_mode; + + /* + * QoS Priority: The QoS Health parameters are getting considered + * only if we are NOT in Bypass Mode. + */ + if (mode != NOC_QOS_MODE_BYPASS) { + for (i = 3; i >= 0; i--) { + rc = qcom_icc_bimc_set_qos_health(qp->regmap, + &qn->qos, i); + if (rc) + return rc; + } + + /* Set BKE_EN to 1 when Fixed, Regulator or Limiter Mode */ + val = 1; + } + + return regmap_update_bits(qp->regmap, M_BKE_EN_ADDR(qn->qos.qos_port), + M_BKE_EN_EN_BMASK, val); +} + +static int qcom_icc_noc_set_qos_priority(struct regmap *rmap, + struct qcom_icc_qos *qos) +{ + u32 val; + int rc; + + /* Must be updated one at a time, P1 first, P0 last */ + val = qos->areq_prio << NOC_QOS_PRIORITY_P1_SHIFT; + rc = regmap_update_bits(rmap, NOC_QOS_PRIORITYn_ADDR(qos->qos_port), + NOC_QOS_PRIORITY_MASK, val); + if (rc) + return rc; + + val = qos->prio_level << NOC_QOS_PRIORITY_P0_SHIFT; + return regmap_update_bits(rmap, NOC_QOS_PRIORITYn_ADDR(qos->qos_port), + NOC_QOS_PRIORITY_MASK, val); +} + +static int qcom_icc_set_noc_qos(struct icc_node *src, u64 max_bw) +{ + struct qcom_icc_provider *qp; + struct qcom_icc_node *qn; + struct icc_provider *provider; + u32 mode = NOC_QOS_MODE_BYPASS; + int rc = 0; + + qn = src->data; + provider = src->provider; + qp = to_qcom_provider(provider); + + if (qn->qos.qos_port < 0) { + dev_dbg(src->provider->dev, + "NoC QoS: Skipping %s: vote aggregated on parent.\n", + qn->name); + return 0; + } + + if (qn->qos.qos_mode != -1) + mode = qn->qos.qos_mode; + + if (mode == NOC_QOS_MODE_FIXED) { + dev_dbg(src->provider->dev, "NoC QoS: %s: Set Fixed mode\n", + qn->name); + rc = qcom_icc_noc_set_qos_priority(qp->regmap, &qn->qos); + if (rc) + return rc; + } else if (mode == NOC_QOS_MODE_BYPASS) { + dev_dbg(src->provider->dev, "NoC QoS: %s: Set Bypass mode\n", + qn->name); + } + + return regmap_update_bits(qp->regmap, + NOC_QOS_MODEn_ADDR(qn->qos.qos_port), + NOC_QOS_MODEn_MASK, mode); +} + +static int qcom_icc_qos_set(struct icc_node *node, u64 sum_bw) +{ + struct qcom_icc_provider *qp = to_qcom_provider(node->provider); + struct qcom_icc_node *qn = node->data; + + dev_dbg(node->provider->dev, "Setting QoS for %s\n", qn->name); + + if (qp->is_bimc_node) + return qcom_icc_set_bimc_qos(node, sum_bw, + (qn->qos.qos_mode == NOC_QOS_MODE_BYPASS)); + + return qcom_icc_set_noc_qos(node, sum_bw); +} + +static int qcom_icc_rpm_set(int mas_rpm_id, int slv_rpm_id, u64 sum_bw) +{ + int ret = 0; + + if (mas_rpm_id != -1) { + ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE, + RPM_BUS_MASTER_REQ, + mas_rpm_id, + sum_bw); + if (ret) { + pr_err("qcom_icc_rpm_smd_send mas %d error %d\n", + mas_rpm_id, ret); + return ret; + } + } + + if (slv_rpm_id != -1) { + ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE, + RPM_BUS_SLAVE_REQ, + slv_rpm_id, + sum_bw); + if (ret) { + pr_err("qcom_icc_rpm_smd_send slv %d error %d\n", + slv_rpm_id, ret); + return ret; + } + } + + return ret; +} + +static int qcom_icc_set(struct icc_node *src, struct icc_node *dst) +{ + struct qcom_icc_provider *qp; + struct qcom_icc_node *qn; + struct icc_provider *provider; + struct icc_node *n; + u64 sum_bw; + u64 max_peak_bw; + u64 rate; + u32 agg_avg = 0; + u32 agg_peak = 0; + int ret, i; + + qn = src->data; + provider = src->provider; + qp = to_qcom_provider(provider); + + list_for_each_entry(n, &provider->nodes, node_list) + provider->aggregate(n, 0, n->avg_bw, n->peak_bw, + &agg_avg, &agg_peak); + + sum_bw = icc_units_to_bps(agg_avg); + max_peak_bw = icc_units_to_bps(agg_peak); + + if (!qn->qos.ap_owned) { + /* send bandwidth request message to the RPM processor */ + ret = qcom_icc_rpm_set(qn->mas_rpm_id, qn->slv_rpm_id, sum_bw); + if (ret) + return ret; + } else if (qn->qos.qos_mode != -1) { + /* set bandwidth directly from the AP */ + ret = qcom_icc_qos_set(src, sum_bw); + if (ret) + return ret; + } + + rate = max(sum_bw, max_peak_bw); + + do_div(rate, qn->buswidth); + + if (qn->rate == rate) + return 0; + + for (i = 0; i < qp->num_clks; i++) { + ret = clk_set_rate(qp->bus_clks[i].clk, rate); + if (ret) { + pr_err("%s clk_set_rate error: %d\n", + qp->bus_clks[i].id, ret); + return ret; + } + } + + qn->rate = rate; + + return 0; +} + +static int qnoc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct qcom_icc_desc *desc; + struct icc_onecell_data *data; + struct icc_provider *provider; + struct qcom_icc_node **qnodes; + struct qcom_icc_provider *qp; + struct icc_node *node; + struct resource *res; + size_t num_nodes, i; + int ret; + + /* wait for the RPM proxy */ + if (!qcom_icc_rpm_smd_available()) + return -EPROBE_DEFER; + + desc = of_device_get_match_data(dev); + if (!desc) + return -EINVAL; + + qnodes = desc->nodes; + num_nodes = desc->num_nodes; + + qp = devm_kzalloc(dev, sizeof(*qp), GFP_KERNEL); + if (!qp) + return -ENOMEM; + + data = devm_kzalloc(dev, struct_size(data, nodes, num_nodes), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + if (of_device_is_compatible(dev->of_node, "qcom,msm8998-mnoc")) { + qp->bus_clks = devm_kmemdup(dev, bus_mm_clocks, + sizeof(bus_mm_clocks), GFP_KERNEL); + qp->num_clks = ARRAY_SIZE(bus_mm_clocks); + } else { + if (of_device_is_compatible(dev->of_node, "qcom,msm8998-bimc")) + qp->is_bimc_node = true; + + qp->bus_clks = devm_kmemdup(dev, bus_clocks, sizeof(bus_clocks), + GFP_KERNEL); + qp->num_clks = ARRAY_SIZE(bus_clocks); + } + if (!qp->bus_clks) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + qp->mmio = devm_ioremap_resource(dev, res); + if (IS_ERR(qp->mmio)) { + dev_err(dev, "Cannot ioremap interconnect bus resource\n"); + return PTR_ERR(qp->mmio); + } + + qp->regmap = devm_regmap_init_mmio(dev, qp->mmio, desc->regmap_cfg); + if (IS_ERR(qp->regmap)) { + dev_err(dev, "Cannot regmap interconnect bus resource\n"); + return PTR_ERR(qp->regmap); + } + + ret = devm_clk_bulk_get(dev, qp->num_clks, qp->bus_clks); + if (ret) + return ret; + + ret = clk_bulk_prepare_enable(qp->num_clks, qp->bus_clks); + if (ret) + return ret; + + provider = &qp->provider; + INIT_LIST_HEAD(&provider->nodes); + provider->dev = dev; + provider->set = qcom_icc_set; + provider->aggregate = icc_std_aggregate; + provider->xlate = of_icc_xlate_onecell; + provider->data = data; + + ret = icc_provider_add(provider); + if (ret) { + dev_err(dev, "error adding interconnect provider: %d\n", ret); + clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); + return ret; + } + + for (i = 0; i < num_nodes; i++) { + size_t j; + + node = icc_node_create(qnodes[i]->id); + if (IS_ERR(node)) { + ret = PTR_ERR(node); + goto err; + } + + node->name = qnodes[i]->name; + node->data = qnodes[i]; + icc_node_add(node, provider); + + for (j = 0; j < qnodes[i]->num_links; j++) + icc_link_create(node, qnodes[i]->links[j]); + + data->nodes[i] = node; + } + data->num_nodes = num_nodes; + platform_set_drvdata(pdev, qp); + + return 0; +err: + icc_nodes_remove(provider); + clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); + icc_provider_del(provider); + + return ret; +} + +static int qnoc_remove(struct platform_device *pdev) +{ + struct qcom_icc_provider *qp = platform_get_drvdata(pdev); + + icc_nodes_remove(&qp->provider); + clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); + return icc_provider_del(&qp->provider); +} + +static const struct of_device_id msm8998_noc_of_match[] = { + { .compatible = "qcom,msm8998-a1noc", .data = &msm8998_a1noc }, + { .compatible = "qcom,msm8998-a2noc", .data = &msm8998_a2noc }, + { .compatible = "qcom,msm8998-bimc", .data = &msm8998_bimc }, + { .compatible = "qcom,msm8998-cnoc", .data = &msm8998_cnoc }, + { .compatible = "qcom,msm8998-gnoc", .data = &msm8998_gnoc }, + { .compatible = "qcom,msm8998-mnoc", .data = &msm8998_mnoc }, + { .compatible = "qcom,msm8998-snoc", .data = &msm8998_snoc }, + { }, +}; +MODULE_DEVICE_TABLE(of, msm8998_noc_of_match); + +static struct platform_driver msm8998_noc_driver = { + .probe = qnoc_probe, + .remove = qnoc_remove, + .driver = { + .name = "qnoc-msm8998", + .of_match_table = msm8998_noc_of_match, + .sync_state = icc_sync_state, + }, +}; +module_platform_driver(msm8998_noc_driver); +MODULE_DESCRIPTION("Qualcomm msm8998 NoC driver"); +MODULE_LICENSE("GPL v2"); From b76ed0ff8e2d3e572735772adcbc85589cdc7c10 Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Sun, 27 Feb 2022 01:26:28 +0000 Subject: [PATCH 0262/1823] interconnect: qcom: msm8998: fix a typo'd QNODE Fixes a WARN generated by referencing the same node id multiple times --- drivers/interconnect/qcom/msm8998.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/interconnect/qcom/msm8998.c b/drivers/interconnect/qcom/msm8998.c index 764c2fc18a62..94ba8f6aca06 100644 --- a/drivers/interconnect/qcom/msm8998.c +++ b/drivers/interconnect/qcom/msm8998.c @@ -338,7 +338,7 @@ DEFINE_QNODE(slv_clk_ctl, MSM8998_SLAVE_CLK_CTL, 4, -1, 47, true, -1, 0, -1, 0); DEFINE_QNODE(slv_glm, MSM8998_SLAVE_GLM, 4, -1, 209, true, -1, 0, -1, 0); DEFINE_QNODE(slv_spdm, MSM8998_SLAVE_SPDM, 4, -1, 60, true, -1, 0, -1, 0); DEFINE_QNODE(slv_gpuss_cfg, MSM8998_SLAVE_GPUSS_CFG, 4, -1, 11, true, -1, 0, -1, 0); -DEFINE_QNODE(slv_cnoc_mnoc_cfg, MSM8998_SLAVE_BLSP_1, 4, -1, 66, true, -1, 0, -1, MSM8998_MASTER_CNOC_MNOC_CFG); +DEFINE_QNODE(slv_cnoc_mnoc_cfg, MSM8998_SLAVE_CNOC_MNOC_CFG, 4, -1, 66, true, -1, 0, -1, MSM8998_MASTER_CNOC_MNOC_CFG); DEFINE_QNODE(slv_qm_cfg, MSM8998_SLAVE_QM_CFG, 4, -1, 212, true, -1, 0, -1, 0); DEFINE_QNODE(slv_mss_cfg, MSM8998_SLAVE_MSS_CFG, 4, -1, 48, true, -1, 0, -1, 0); DEFINE_QNODE(slv_ufs_cfg, MSM8998_SLAVE_UFS_CFG, 4, -1, 92, true, -1, 0, -1, 0); From 90a10c96508e18f07543ed6488bf21ebaa6cf142 Mon Sep 17 00:00:00 2001 From: Jami Kettunen Date: Tue, 18 Oct 2022 15:46:47 +0300 Subject: [PATCH 0263/1823] interconnect: msm8998: Ignore return value of icc_provider_del() in .remove() A similar fix for sm8450 was done in fa80a2994d35, fixes compilation on v6.1-rc1. --- drivers/interconnect/qcom/msm8998.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/interconnect/qcom/msm8998.c b/drivers/interconnect/qcom/msm8998.c index 94ba8f6aca06..e565cc0f3afb 100644 --- a/drivers/interconnect/qcom/msm8998.c +++ b/drivers/interconnect/qcom/msm8998.c @@ -952,7 +952,9 @@ static int qnoc_remove(struct platform_device *pdev) icc_nodes_remove(&qp->provider); clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); - return icc_provider_del(&qp->provider); + icc_provider_del(&qp->provider); + + return 0; } static const struct of_device_id msm8998_noc_of_match[] = { From 3809f25c85125d6ab482dfe0fb787ca6282d8e28 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Fri, 4 Dec 2020 22:34:58 +0100 Subject: [PATCH 0264/1823] pinctrl: Add driver for Awinic AW9523/B I2C GPIO Expander The Awinic AW9523(B) is a multi-function I2C gpio expander in a TQFN-24L package, featuring PWM (max 37mA per pin, or total max power 3.2Watts) for LED driving capability. It has two ports with 8 pins per port (for a total of 16 pins), configurable as either PWM with 1/256 stepping or GPIO input/output, 1.8V logic input; each GPIO can be configured as input or output independently from each other. This IC also has an internal interrupt controller, which is capable of generating an interrupt for each GPIO, depending on the configuration, and will raise an interrupt on the INTN pin to advertise this to an external interrupt controller. Signed-off-by: AngeloGioacchino Del Regno --- drivers/pinctrl/Kconfig | 17 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-aw9523.c | 1122 ++++++++++++++++++++++++++++++ 3 files changed, 1140 insertions(+) create mode 100644 drivers/pinctrl/pinctrl-aw9523.c diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index f71fefff400f..4db0b6d3fd49 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -127,6 +127,23 @@ config PINCTRL_AXP209 selected. Say Y to enable pinctrl and GPIO support for the AXP209 PMIC. +config PINCTRL_AW9523 + bool "Awinic AW9523/AW9523B I2C GPIO expander pinctrl driver" + depends on OF && I2C + select PINMUX + select PINCONF + select GENERIC_PINCONF + select GPIOLIB + select GPIOLIB_IRQCHIP + select REGMAP + help + The Awinic AW9523/AW9523B is a multi-function I2C GPIO + expander with PWM functionality. This driver bundles a + pinctrl driver to select the function muxing and a GPIO + driver to handle GPIO, when the GPIO function is selected. + + Say yes to enable pinctrl and GPIO support for the AW9523(B). + config PINCTRL_BM1880 bool "Bitmain BM1880 Pinctrl driver" depends on OF && (ARCH_BITMAIN || COMPILE_TEST) diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 89bfa01b5231..1788d25d22c2 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_PINCTRL_ARTPEC6) += pinctrl-artpec6.o obj-$(CONFIG_PINCTRL_AS3722) += pinctrl-as3722.o obj-$(CONFIG_PINCTRL_AT91) += pinctrl-at91.o obj-$(CONFIG_PINCTRL_AT91PIO4) += pinctrl-at91-pio4.o +obj-$(CONFIG_PINCTRL_AW9523) += pinctrl-aw9523.o obj-$(CONFIG_PINCTRL_AXP209) += pinctrl-axp209.o obj-$(CONFIG_PINCTRL_BM1880) += pinctrl-bm1880.o obj-$(CONFIG_PINCTRL_CY8C95X0) += pinctrl-cy8c95x0.o diff --git a/drivers/pinctrl/pinctrl-aw9523.c b/drivers/pinctrl/pinctrl-aw9523.c new file mode 100644 index 000000000000..bc8e1e9d6876 --- /dev/null +++ b/drivers/pinctrl/pinctrl-aw9523.c @@ -0,0 +1,1122 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Awinic AW9523B i2c pin controller driver + * Copyright (c) 2020, AngeloGioacchino Del Regno + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "pinconf.h" +#include "pinctrl-utils.h" + +#define AW9523_MAX_FUNCS 2 +#define AW9523_NUM_PORTS 2 +#define AW9523_PINS_PER_PORT 8 + +/* + * HW needs at least 20uS for reset and at least 1-2uS to recover from + * reset, but we have to account for eventual board quirks, if any: + * for this reason, keep reset asserted for 50uS and wait for 20uS + * to recover from the reset. + */ +#define AW9523_HW_RESET_US 50 +#define AW9523_HW_RESET_RECOVERY_US 20 + +/* Port 0: P0_0...P0_7 - Port 1: P1_0...P1_7 */ +#define AW9523_PIN_TO_PORT(pin) (pin >> 3) +#define AW9523_REG_IN_STATE(pin) (0x00 + AW9523_PIN_TO_PORT(pin)) +#define AW9523_REG_OUT_STATE(pin) (0x02 + AW9523_PIN_TO_PORT(pin)) +#define AW9523_REG_CONF_STATE(pin) (0x04 + AW9523_PIN_TO_PORT(pin)) +#define AW9523_REG_INTR_DIS(pin) (0x06 + AW9523_PIN_TO_PORT(pin)) +#define AW9523_REG_CHIPID 0x10 +#define AW9523_VAL_EXPECTED_CHIPID 0x23 + +#define AW9523_REG_GCR 0x11 +#define AW9523_GCR_ISEL_MASK GENMASK(0, 1) +#define AW9523_GCR_GPOMD_MASK BIT(4) + +#define AW9523_REG_PORT_MODE(pin) (0x12 + AW9523_PIN_TO_PORT(pin)) +#define AW9523_REG_SOFT_RESET 0x7f +#define AW9523_VAL_RESET 0x00 + +/* + * struct aw9523_irq - Interrupt controller structure + * @lock: mutex locking for the irq bus + * @irqchip: structure holding irqchip params + * @cached_gpio: stores the previous gpio status for bit comparison + */ +struct aw9523_irq { + struct mutex lock; + struct irq_chip *irqchip; + u16 cached_gpio; +}; + +/* + * struct aw9523_pinmux - Pin mux params + * @name: Name of the mux + * @grps: Groups of the mux + * @num_grps: Number of groups (sizeof array grps) + */ +struct aw9523_pinmux { + const char *name; + const char * const *grps; + const u8 num_grps; +}; + +/* + * struct aw9523 - Main driver structure + * @dev: device handle + * @regmap: regmap handle for current device + * @i2c_lock: Mutex lock for i2c operations + * @reset_gpio: Hardware reset (RSTN) signal GPIO + * @vio_vreg: VCC regulator (Optional) + * @pctl: pinctrl handle for current device + * @gpio: structure holding gpiochip params + * @irq: Interrupt controller structure + */ +struct aw9523 { + struct device *dev; + struct regmap *regmap; + struct mutex i2c_lock; + struct gpio_desc *reset_gpio; + struct regulator *vio_vreg; + struct pinctrl_dev *pctl; + struct gpio_chip gpio; + struct aw9523_irq *irq; +}; + +static const struct pinctrl_pin_desc aw9523_pins[] = { + /* Port 0 */ + PINCTRL_PIN(0, "gpio0"), + PINCTRL_PIN(1, "gpio1"), + PINCTRL_PIN(2, "gpio2"), + PINCTRL_PIN(3, "gpio3"), + PINCTRL_PIN(4, "gpio4"), + PINCTRL_PIN(5, "gpio5"), + PINCTRL_PIN(6, "gpio6"), + PINCTRL_PIN(7, "gpio7"), + + /* Port 1 */ + PINCTRL_PIN(8, "gpio8"), + PINCTRL_PIN(9, "gpio9"), + PINCTRL_PIN(10, "gpio10"), + PINCTRL_PIN(11, "gpio11"), + PINCTRL_PIN(12, "gpio12"), + PINCTRL_PIN(13, "gpio13"), + PINCTRL_PIN(14, "gpio14"), + PINCTRL_PIN(15, "gpio15"), +}; + +static int aw9523_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(aw9523_pins); +} + +static const char *aw9523_pinctrl_get_group_name(struct pinctrl_dev *pctldev, + unsigned int selector) +{ + return aw9523_pins[selector].name; +} + +static int aw9523_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned int selector, + const unsigned int **pins, + unsigned int *num_pins) +{ + *pins = &aw9523_pins[selector].number; + *num_pins = 1; + return 0; +} + +static const struct pinctrl_ops aw9523_pinctrl_ops = { + .get_groups_count = aw9523_pinctrl_get_groups_count, + .get_group_pins = aw9523_pinctrl_get_group_pins, + .get_group_name = aw9523_pinctrl_get_group_name, + .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, + .dt_free_map = pinconf_generic_dt_free_map, +}; + +static const char * const gpio_pwm_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", + "gpio6", "gpio7", "gpio8", "gpio9", "gpio10", "gpio11", + "gpio12", "gpio13", "gpio14", "gpio15" +}; + +/* Warning: Do NOT reorder this array */ +static const struct aw9523_pinmux aw9523_pmx[] = { + { + .name = "pwm", + .grps = gpio_pwm_groups, + .num_grps = ARRAY_SIZE(gpio_pwm_groups), + }, + { + .name = "gpio", + .grps = gpio_pwm_groups, + .num_grps = ARRAY_SIZE(gpio_pwm_groups), + }, +}; + +static int aw9523_pmx_get_funcs_count(struct pinctrl_dev *pctl) +{ + return ARRAY_SIZE(aw9523_pmx); +} + +static const char *aw9523_pmx_get_fname(struct pinctrl_dev *pctl, + unsigned int sel) +{ + return aw9523_pmx[sel].name; +} + +static int aw9523_pmx_get_groups(struct pinctrl_dev *pctl, unsigned int sel, + const char * const **groups, + unsigned int * const num_groups) +{ + *groups = aw9523_pmx[sel].grps; + *num_groups = aw9523_pmx[sel].num_grps; + return 0; +} + +static int aw9523_pmx_set_mux(struct pinctrl_dev *pctl, unsigned int fsel, + unsigned int grp) +{ + struct aw9523 *awi = pinctrl_dev_get_drvdata(pctl); + int ret, pin = aw9523_pins[grp].number % AW9523_PINS_PER_PORT; + + if (fsel >= ARRAY_SIZE(aw9523_pmx)) + return -EINVAL; + + /* + * This maps directly to the aw9523_pmx array: programming a + * high bit means "gpio" and a low bit means "pwm". + */ + mutex_lock(&awi->i2c_lock); + ret = regmap_update_bits(awi->regmap, AW9523_REG_PORT_MODE(pin), + BIT(pin), (fsel ? BIT(pin) : 0)); + mutex_unlock(&awi->i2c_lock); + return ret; +} + +static const struct pinmux_ops aw9523_pinmux_ops = { + .get_functions_count = aw9523_pmx_get_funcs_count, + .get_function_name = aw9523_pmx_get_fname, + .get_function_groups = aw9523_pmx_get_groups, + .set_mux = aw9523_pmx_set_mux, +}; + +static int aw9523_pcfg_param_to_reg(enum pin_config_param pcp, int pin, u8 *r) +{ + u8 reg; + + switch (pcp) { + case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_UP: + reg = AW9523_REG_IN_STATE(pin); + break; + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + case PIN_CONFIG_DRIVE_PUSH_PULL: + reg = AW9523_REG_GCR; + break; + case PIN_CONFIG_INPUT_ENABLE: + case PIN_CONFIG_OUTPUT_ENABLE: + reg = AW9523_REG_CONF_STATE(pin); + break; + case PIN_CONFIG_OUTPUT: + reg = AW9523_REG_OUT_STATE(pin); + break; + default: + return -ENOTSUPP; + } + *r = reg; + + return 0; +} + +static int aw9523_pconf_get(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *config) +{ + struct aw9523 *awi = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param = pinconf_to_config_param(*config); + int regbit = pin % AW9523_PINS_PER_PORT; + unsigned int val; + u8 reg; + int rc; + + rc = aw9523_pcfg_param_to_reg(param, pin, ®); + if (rc) + return rc; + + mutex_lock(&awi->i2c_lock); + rc = regmap_read(awi->regmap, reg, &val); + mutex_unlock(&awi->i2c_lock); + if (rc) + return rc; + + switch (param) { + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_INPUT_ENABLE: + case PIN_CONFIG_OUTPUT: + val &= BIT(regbit); + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_OUTPUT_ENABLE: + val &= BIT(regbit); + val = !val; + break; + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + if (pin >= AW9523_PINS_PER_PORT) + val = 0; + else + val = !FIELD_GET(AW9523_GCR_GPOMD_MASK, val); + break; + case PIN_CONFIG_DRIVE_PUSH_PULL: + if (pin >= AW9523_PINS_PER_PORT) + val = 1; + else + val = FIELD_GET(AW9523_GCR_GPOMD_MASK, val); + break; + default: + return -ENOTSUPP; + } + if (val < 1) + return -EINVAL; + + *config = pinconf_to_config_packed(param, !!val); + + return rc; +} + +static int aw9523_pconf_set(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *configs, unsigned int num_configs) +{ + struct aw9523 *awi = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param; + int regbit = pin % AW9523_PINS_PER_PORT; + u32 arg; + u8 reg; + unsigned int mask, val; + int i, rc; + + mutex_lock(&awi->i2c_lock); + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); + + rc = aw9523_pcfg_param_to_reg(param, pin, ®); + if (rc) + goto end; + + switch (param) { + case PIN_CONFIG_OUTPUT: + /* First, enable pin output */ + rc = regmap_update_bits(awi->regmap, + AW9523_REG_CONF_STATE(pin), + BIT(regbit), 0); + if (rc) + goto end; + + /* Then, fall through to config output level */ + fallthrough; + case PIN_CONFIG_OUTPUT_ENABLE: + arg = !arg; + fallthrough; + case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_INPUT_ENABLE: + mask = BIT(regbit); + val = arg ? BIT(regbit) : 0; + break; + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + /* Open-Drain is supported only on port 0 */ + if (pin >= AW9523_PINS_PER_PORT) { + rc = -ENOTSUPP; + goto end; + } + mask = AW9523_GCR_GPOMD_MASK; + val = 0; + break; + case PIN_CONFIG_DRIVE_PUSH_PULL: + /* Port 1 is always Push-Pull */ + if (pin >= AW9523_PINS_PER_PORT) { + mask = 0; + val = 0; + continue; + } + mask = AW9523_GCR_GPOMD_MASK; + val = AW9523_GCR_GPOMD_MASK; + break; + default: + rc = -ENOTSUPP; + goto end; + } + + rc = regmap_update_bits(awi->regmap, reg, mask, val); + if (rc) + goto end; + } +end: + mutex_unlock(&awi->i2c_lock); + return rc; +} + +static const struct pinconf_ops aw9523_pinconf_ops = { + .pin_config_get = aw9523_pconf_get, + .pin_config_set = aw9523_pconf_set, + .is_generic = true, +}; + +/* + * aw9523_get_pin_direction - Get pin direction + * @regmap: Regmap structure + * @pin: gpiolib pin number + * @n: pin index in port register + * + * Return: Pin direction for success or negative number for error + */ +static int aw9523_get_pin_direction(struct regmap *regmap, u8 pin, u8 n) +{ + int ret; + + ret = regmap_test_bits(regmap, AW9523_REG_CONF_STATE(pin), BIT(n)); + if (ret < 0) + return ret; + + return ret ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT; +} + +/* + * aw9523_get_port_state - Get input or output state for entire port + * @regmap: Regmap structure + * @pin: gpiolib pin number + * @regbit: hw pin index, used to retrieve port number + * @state: returned port state + * + * Return: Zero for success or negative number for error + */ +static int aw9523_get_port_state(struct regmap *regmap, u8 pin, + u8 regbit, unsigned int *state) +{ + u8 reg; + int dir; + + dir = aw9523_get_pin_direction(regmap, pin, regbit); + if (dir < 0) + return dir; + + if (dir == GPIO_LINE_DIRECTION_IN) + reg = AW9523_REG_IN_STATE(pin); + else + reg = AW9523_REG_OUT_STATE(pin); + + return regmap_read(regmap, reg, state); +} + +static int aw9523_gpio_irq_type(struct irq_data *d, unsigned int type) +{ + switch (type) { + case IRQ_TYPE_NONE: + case IRQ_TYPE_EDGE_BOTH: + return 0; + default: + return -EINVAL; + }; +} + +/* + * aw9523_irq_mask - Mask interrupt + * @d: irq data + * + * Sets which interrupt to mask in the bitmap; + * The interrupt will be masked when unlocking the irq bus. + */ +static void aw9523_irq_mask(struct irq_data *d) +{ + struct aw9523 *awi = gpiochip_get_data(irq_data_get_irq_chip_data(d)); + unsigned int n = d->hwirq % AW9523_PINS_PER_PORT; + + regmap_update_bits(awi->regmap, + AW9523_REG_INTR_DIS(d->hwirq), + BIT(n), BIT(n)); +} + +/* + * aw9523_irq_unmask - Unmask interrupt + * @d: irq data + * + * Sets which interrupt to unmask in the bitmap; + * The interrupt will be masked when unlocking the irq bus. + */ +static void aw9523_irq_unmask(struct irq_data *d) +{ + struct aw9523 *awi = gpiochip_get_data(irq_data_get_irq_chip_data(d)); + unsigned int n = d->hwirq % AW9523_PINS_PER_PORT; + + regmap_update_bits(awi->regmap, + AW9523_REG_INTR_DIS(d->hwirq), + BIT(n), 0); +} + +static irqreturn_t aw9523_irq_thread_func(int irq, void *dev_id) +{ + struct aw9523 *awi = (struct aw9523 *)dev_id; + unsigned long n, val = 0; + unsigned long changed_gpio; + unsigned int tmp, port_pin, i, ret; + + for (i = 0; i < AW9523_NUM_PORTS; i++) { + port_pin = i * AW9523_PINS_PER_PORT; + ret = regmap_read(awi->regmap, + AW9523_REG_IN_STATE(port_pin), + &tmp); + if (ret) + return ret; + val |= (u8)tmp << (i * 8); + } + + /* Handle GPIO input release interrupt as well */ + changed_gpio = awi->irq->cached_gpio ^ val; + awi->irq->cached_gpio = val; + + /* + * To avoid up to four *slow* i2c reads from any driver hooked + * up to our interrupts, just check for the irq_find_mapping + * result: if the interrupt is not mapped, then we don't want + * to care about it. + */ + for_each_set_bit(n, &changed_gpio, awi->gpio.ngpio) { + tmp = irq_find_mapping(awi->gpio.irq.domain, n); + if (tmp <= 0) + continue; + handle_nested_irq(tmp); + } + + return IRQ_HANDLED; +} + +/* + * aw9523_irq_bus_lock - Grab lock for interrupt operation + * @d: irq data + */ +static void aw9523_irq_bus_lock(struct irq_data *d) +{ + struct aw9523 *awi = gpiochip_get_data(irq_data_get_irq_chip_data(d)); + + mutex_lock(&awi->irq->lock); + regcache_cache_only(awi->regmap, true); +} + +/* + * aw9523_irq_bus_sync_unlock - Synchronize state and unlock + * @d: irq data + * + * Writes the interrupt mask bits (found in the bit map) to the + * hardware, then unlocks the bus. + */ +static void aw9523_irq_bus_sync_unlock(struct irq_data *d) +{ + struct aw9523 *awi = gpiochip_get_data(irq_data_get_irq_chip_data(d)); + + regcache_cache_only(awi->regmap, false); + regcache_sync(awi->regmap); + mutex_unlock(&awi->irq->lock); +} + +static int aw9523_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + struct aw9523 *awi = gpiochip_get_data(chip); + u8 regbit = offset % AW9523_PINS_PER_PORT; + int ret; + + mutex_lock(&awi->i2c_lock); + ret = aw9523_get_pin_direction(awi->regmap, offset, regbit); + mutex_unlock(&awi->i2c_lock); + + return ret; +} + +static int aw9523_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct aw9523 *awi = gpiochip_get_data(chip); + u8 regbit = offset % AW9523_PINS_PER_PORT; + unsigned int val; + int ret; + + mutex_lock(&awi->i2c_lock); + ret = aw9523_get_port_state(awi->regmap, offset, regbit, &val); + mutex_unlock(&awi->i2c_lock); + if (ret) + return ret; + + return !!(val & BIT(regbit)); +} + +/** + * _aw9523_gpio_get_multiple - Get I/O state for an entire port + * @regmap: Regmap structure + * @pin: gpiolib pin number + * @regbit: hw pin index, used to retrieve port number + * @state: returned port I/O state + * + * Return: Zero for success or negative number for error + */ +static int _aw9523_gpio_get_multiple(struct aw9523 *awi, u8 regbit, + u8 *state, u8 mask) +{ + u32 dir_in, val; + u8 m; + int ret; + + /* Registers are 8-bits wide */ + ret = regmap_read(awi->regmap, AW9523_REG_CONF_STATE(regbit), &dir_in); + if (ret) + return ret; + *state = 0; + + m = mask & dir_in; + if (m) { + ret = regmap_read(awi->regmap, AW9523_REG_IN_STATE(regbit), + &val); + if (ret) + return ret; + *state |= (u8)val & m; + } + + m = mask & ~dir_in; + if (m) { + ret = regmap_read(awi->regmap, AW9523_REG_OUT_STATE(regbit), + &val); + if (ret) + return ret; + *state |= (u8)val & m; + } + + return 0; +} + +static int aw9523_gpio_get_multiple(struct gpio_chip *chip, + unsigned long *mask, + unsigned long *bits) +{ + struct aw9523 *awi = gpiochip_get_data(chip); + u8 m, state = 0; + int ret; + + mutex_lock(&awi->i2c_lock); + + /* Port 0 (gpio 0-7) */ + m = *mask & U8_MAX; + if (m) { + ret = _aw9523_gpio_get_multiple(awi, 0, &state, m); + if (ret) + goto out; + } + *bits = state; + + /* Port 1 (gpio 8-15) */ + m = (*mask >> 8) & U8_MAX; + if (m) { + ret = _aw9523_gpio_get_multiple(awi, AW9523_PINS_PER_PORT, + &state, m); + if (ret) + goto out; + + *bits |= (state << 8); + } +out: + mutex_unlock(&awi->i2c_lock); + return ret; +} + +static void aw9523_gpio_set_multiple(struct gpio_chip *chip, + unsigned long *mask, + unsigned long *bits) +{ + struct aw9523 *awi = gpiochip_get_data(chip); + u8 mask_lo, mask_hi, bits_lo, bits_hi; + unsigned int reg; + int ret = 0; + + mask_lo = *mask & U8_MAX; + mask_hi = (*mask >> 8) & U8_MAX; + mutex_lock(&awi->i2c_lock); + if (mask_hi) { + reg = AW9523_REG_OUT_STATE(AW9523_PINS_PER_PORT); + bits_hi = (*bits >> 8) & U8_MAX; + + ret = regmap_write_bits(awi->regmap, reg, mask_hi, bits_hi); + if (ret) { + dev_warn(awi->dev, "Cannot write port1 out level\n"); + goto out; + } + } + if (mask_lo) { + reg = AW9523_REG_OUT_STATE(0); + bits_lo = *bits & U8_MAX; + ret = regmap_write_bits(awi->regmap, reg, mask_lo, bits_lo); + if (ret) + dev_warn(awi->dev, "Cannot write port0 out level\n"); + } +out: + mutex_unlock(&awi->i2c_lock); +} + +static void aw9523_gpio_set(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct aw9523 *awi = gpiochip_get_data(chip); + u8 regbit = offset % AW9523_PINS_PER_PORT; + + mutex_lock(&awi->i2c_lock); + regmap_update_bits(awi->regmap, AW9523_REG_OUT_STATE(offset), + BIT(regbit), value ? BIT(regbit) : 0); + mutex_unlock(&awi->i2c_lock); +} + + +static int aw9523_direction_input(struct gpio_chip *chip, unsigned int offset) +{ + struct aw9523 *awi = gpiochip_get_data(chip); + u8 regbit = offset % AW9523_PINS_PER_PORT; + int ret; + + mutex_lock(&awi->i2c_lock); + ret = regmap_update_bits(awi->regmap, AW9523_REG_CONF_STATE(offset), + BIT(regbit), BIT(regbit)); + mutex_unlock(&awi->i2c_lock); + + return ret; +} + +static int aw9523_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct aw9523 *awi = gpiochip_get_data(chip); + u8 regbit = offset % AW9523_PINS_PER_PORT; + int ret; + + mutex_lock(&awi->i2c_lock); + ret = regmap_update_bits(awi->regmap, AW9523_REG_OUT_STATE(offset), + BIT(regbit), value ? BIT(regbit) : 0); + if (ret) + goto end; + + ret = regmap_update_bits(awi->regmap, AW9523_REG_CONF_STATE(offset), + BIT(regbit), 0); +end: + mutex_unlock(&awi->i2c_lock); + return ret; +} + +static int aw9523_drive_reset_gpio(struct aw9523 *awi) +{ + unsigned int chip_id; + int ret; + + /* + * If the chip is already configured for any reason, then we + * will probably succeed in sending the soft reset signal to + * the hardware through I2C: this operation takes less time + * compared to a full HW reset and it gives the same results. + */ + ret = regmap_write(awi->regmap, AW9523_REG_SOFT_RESET, 0); + if (ret == 0) + goto done; + + dev_dbg(awi->dev, "Cannot execute soft reset: trying hard reset\n"); + ret = gpiod_direction_output(awi->reset_gpio, 0); + if (ret) + return ret; + + /* The reset pulse has to be longer than 20uS due to deglitch */ + usleep_range(AW9523_HW_RESET_US, AW9523_HW_RESET_US + 1); + + ret = gpiod_direction_output(awi->reset_gpio, 1); + if (ret) + return ret; +done: + /* The HW needs at least 1uS to reliably recover after reset */ + usleep_range(AW9523_HW_RESET_RECOVERY_US, + AW9523_HW_RESET_RECOVERY_US + 1); + + /* Check the ChipID */ + ret = regmap_read(awi->regmap, AW9523_REG_CHIPID, &chip_id); + if (ret) { + dev_err(awi->dev, "Cannot read Chip ID: %d\n", ret); + return ret; + } + if (chip_id != AW9523_VAL_EXPECTED_CHIPID) { + dev_err(awi->dev, "Bad ChipID; read 0x%x, expected 0x%x\n", + chip_id, AW9523_VAL_EXPECTED_CHIPID); + return -EINVAL; + } + + return 0; +} + +static int aw9523_hw_reset(struct aw9523 *awi) +{ + int ret, max_retries = 2; + + /* Sometimes the chip needs more than one reset cycle */ + do { + ret = aw9523_drive_reset_gpio(awi); + if (ret == 0) + break; + max_retries--; + } while (max_retries); + + return ret; +} + +static int aw9523_init_gpiochip(struct aw9523 *awi, unsigned int npins) +{ + struct device *dev = awi->dev; + struct gpio_chip *gpiochip = &awi->gpio; + + gpiochip->label = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL); + if (!gpiochip->label) + return -ENOMEM; + + gpiochip->base = -1; + gpiochip->ngpio = npins; + gpiochip->get_direction = aw9523_gpio_get_direction; + gpiochip->direction_input = aw9523_direction_input; + gpiochip->direction_output = aw9523_direction_output; + gpiochip->get = aw9523_gpio_get; + gpiochip->get_multiple = aw9523_gpio_get_multiple; + gpiochip->set = aw9523_gpio_set; + gpiochip->set_multiple = aw9523_gpio_set_multiple; + gpiochip->set_config = gpiochip_generic_config; + gpiochip->parent = dev; + gpiochip->of_node = dev->of_node; + gpiochip->owner = THIS_MODULE; + gpiochip->can_sleep = false; + + return 0; +} + +static int aw9523_init_irq(struct aw9523 *awi, int irq) +{ + struct device *dev = awi->dev; + struct gpio_irq_chip *gpioirq; + struct irq_chip *irqchip; + int ret; + + if (!device_property_read_bool(dev, "interrupt-controller")) + return 0; + + irqchip = devm_kzalloc(dev, sizeof(*irqchip), GFP_KERNEL); + if (!irqchip) + return -ENOMEM; + + awi->irq = devm_kzalloc(dev, sizeof(*awi->irq), GFP_KERNEL); + if (!awi->irq) + return -ENOMEM; + + irqchip->name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL); + if (!irqchip->name) + return -ENOMEM; + + irqchip->irq_mask = aw9523_irq_mask; + irqchip->irq_unmask = aw9523_irq_unmask; + irqchip->irq_bus_lock = aw9523_irq_bus_lock; + irqchip->irq_bus_sync_unlock = aw9523_irq_bus_sync_unlock; + irqchip->irq_set_type = aw9523_gpio_irq_type; + awi->irq->irqchip = irqchip; + mutex_init(&awi->irq->lock); + + ret = devm_request_threaded_irq(dev, irq, NULL, aw9523_irq_thread_func, + IRQF_ONESHOT, dev_name(dev), awi); + if (ret) { + dev_err(dev, "Failed to request irq %d\n", irq); + return ret; + } + + gpioirq = &awi->gpio.irq; + gpioirq->chip = irqchip; + gpioirq->parent_handler = NULL; + gpioirq->num_parents = 0; + gpioirq->parents = NULL; + gpioirq->default_type = IRQ_TYPE_LEVEL_MASK; + gpioirq->handler = handle_simple_irq; + gpioirq->threaded = true; + gpioirq->first = 0; + + return 0; +} + +static bool aw9523_is_reg_hole(unsigned int reg) +{ + return (reg > AW9523_REG_PORT_MODE(AW9523_PINS_PER_PORT) && + reg < AW9523_REG_SOFT_RESET) || + (reg > AW9523_REG_INTR_DIS(AW9523_PINS_PER_PORT) && + reg < AW9523_REG_CHIPID); +} + +static bool aw9523_readable_reg(struct device *dev, unsigned int reg) +{ + /* All available registers (minus holes) can be read */ + return !aw9523_is_reg_hole(reg); +} + +static bool aw9523_volatile_reg(struct device *dev, unsigned int reg) +{ + return aw9523_is_reg_hole(reg) || + reg == AW9523_REG_IN_STATE(0) || + reg == AW9523_REG_IN_STATE(AW9523_PINS_PER_PORT) || + reg == AW9523_REG_CHIPID || + reg == AW9523_REG_SOFT_RESET; +} + +static bool aw9523_writeable_reg(struct device *dev, unsigned int reg) +{ + return !aw9523_is_reg_hole(reg) && reg != AW9523_REG_CHIPID; +} + +static bool aw9523_precious_reg(struct device *dev, unsigned int reg) +{ + /* Reading AW9523_REG_IN_STATE clears interrupt status */ + return aw9523_is_reg_hole(reg) || + reg == AW9523_REG_IN_STATE(0) || + reg == AW9523_REG_IN_STATE(AW9523_PINS_PER_PORT); +} + +static const struct regmap_config aw9523_regmap = { + .reg_bits = 8, + .val_bits = 8, + .reg_stride = 1, + + .precious_reg = aw9523_precious_reg, + .readable_reg = aw9523_readable_reg, + .volatile_reg = aw9523_volatile_reg, + .writeable_reg = aw9523_writeable_reg, + + .cache_type = REGCACHE_FLAT, + .disable_locking = true, + + .num_reg_defaults_raw = AW9523_REG_SOFT_RESET, +}; + +static int aw9523_hw_init(struct aw9523 *awi) +{ + u8 p1_pin = AW9523_PINS_PER_PORT; + unsigned int val; + int ret; + + /* No register caching during initialization */ + regcache_cache_bypass(awi->regmap, true); + + /* Bring up the chip */ + ret = aw9523_hw_reset(awi); + if (ret) { + dev_err(awi->dev, "HW Reset failed: %d\n", ret); + return ret; + } + + /* + * This is the expected chip and it is running: it's time to + * set a safe default configuration in case the user doesn't + * configure (all of the available) pins in this chip. + * P.S.: The writes order doesn't matter. + */ + + /* Set all pins as GPIO */ + ret = regmap_write(awi->regmap, AW9523_REG_PORT_MODE(0), U8_MAX); + if (ret) + return ret; + ret = regmap_write(awi->regmap, AW9523_REG_PORT_MODE(p1_pin), U8_MAX); + if (ret) + return ret; + + /* Set Open-Drain mode on Port 0 (Port 1 is always P-P) */ + ret = regmap_write(awi->regmap, AW9523_REG_GCR, 0); + if (ret) + return ret; + + /* Set all pins as inputs */ + ret = regmap_write(awi->regmap, AW9523_REG_CONF_STATE(0), U8_MAX); + if (ret) + return ret; + ret = regmap_write(awi->regmap, AW9523_REG_CONF_STATE(p1_pin), U8_MAX); + if (ret) + return ret; + + /* Disable all interrupts to avoid unreasoned wakeups */ + ret = regmap_write(awi->regmap, AW9523_REG_INTR_DIS(0), U8_MAX); + if (ret) + return ret; + ret = regmap_write(awi->regmap, AW9523_REG_INTR_DIS(p1_pin), U8_MAX); + if (ret) + return ret; + + /* Clear setup-generated interrupts by performing a port state read */ + ret = aw9523_get_port_state(awi->regmap, 0, 0, &val); + if (ret) + return ret; + ret = aw9523_get_port_state(awi->regmap, p1_pin, 0, &val); + if (ret) + return ret; + + /* Everything went fine: activate and reinitialize register cache */ + regcache_cache_bypass(awi->regmap, false); + return regmap_reinit_cache(awi->regmap, &aw9523_regmap); +} + +static int aw9523_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct pinctrl_desc *pdesc; + struct aw9523 *awi; + int ret; + + awi = devm_kzalloc(dev, sizeof(*awi), GFP_KERNEL); + if (!awi) + return -ENOMEM; + + i2c_set_clientdata(client, awi); + + awi->dev = dev; + awi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(awi->reset_gpio)) + return PTR_ERR(awi->reset_gpio); + gpiod_set_consumer_name(awi->reset_gpio, "aw9523 reset"); + + awi->regmap = devm_regmap_init_i2c(client, &aw9523_regmap); + if (IS_ERR(awi->regmap)) + return PTR_ERR(awi->regmap); + + awi->vio_vreg = devm_regulator_get_optional(dev, "vio"); + if (IS_ERR(awi->vio_vreg)) { + if (PTR_ERR(awi->vio_vreg) == -EPROBE_DEFER) + return -EPROBE_DEFER; + awi->vio_vreg = NULL; + } else { + ret = regulator_enable(awi->vio_vreg); + if (ret) + return ret; + } + + mutex_init(&awi->i2c_lock); + lockdep_set_subclass(&awi->i2c_lock, + i2c_adapter_depth(client->adapter)); + + pdesc = devm_kzalloc(dev, sizeof(*pdesc), GFP_KERNEL); + if (!pdesc) + return -ENOMEM; + + ret = aw9523_hw_init(awi); + if (ret) + goto err_disable_vregs; + + pdesc->name = dev_name(dev); + pdesc->owner = THIS_MODULE; + pdesc->pctlops = &aw9523_pinctrl_ops; + pdesc->pmxops = &aw9523_pinmux_ops; + pdesc->confops = &aw9523_pinconf_ops; + pdesc->pins = aw9523_pins; + pdesc->npins = ARRAY_SIZE(aw9523_pins); + + ret = aw9523_init_gpiochip(awi, pdesc->npins); + if (ret) + goto err_disable_vregs; + + if (client->irq) { + ret = aw9523_init_irq(awi, client->irq); + if (ret) + goto err_disable_vregs; + } + + awi->pctl = devm_pinctrl_register(dev, pdesc, awi); + if (IS_ERR(awi->pctl)) { + ret = PTR_ERR(awi->pctl); + dev_err(dev, "Cannot register pinctrl: %d", ret); + goto err_disable_vregs; + } + + ret = devm_gpiochip_add_data(dev, &awi->gpio, awi); + if (ret) + goto err_disable_vregs; + + return ret; + +err_disable_vregs: + if (awi->vio_vreg) + regulator_disable(awi->vio_vreg); + mutex_destroy(&awi->i2c_lock); + return ret; +} + +static int aw9523_remove(struct i2c_client *client) +{ + struct aw9523 *awi = i2c_get_clientdata(client); + int ret; + + if (!awi) + return 0; + + /* + * If the chip VIO is connected to a regulator that we can turn + * off, life is easy... otherwise, reinitialize the chip and + * set the pins to hardware defaults before removing the driver + * to leave it in a clean, safe and predictable state. + */ + if (awi->vio_vreg) { + regulator_disable(awi->vio_vreg); + } else { + mutex_lock(&awi->i2c_lock); + ret = aw9523_hw_init(awi); + mutex_unlock(&awi->i2c_lock); + if (ret) + return ret; + } + + mutex_destroy(&awi->i2c_lock); + return 0; +} + +static const struct i2c_device_id aw9523_i2c_id_table[] = { + { "aw9523_i2c", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aw9523_i2c_id_table); + +static const struct of_device_id of_aw9523_i2c_match[] = { + { .compatible = "awinic,aw9523-pinctrl", }, +}; +MODULE_DEVICE_TABLE(of, of_aw9523_i2c_match); + +static struct i2c_driver aw9523_driver = { + .driver = { + .name = "aw9523-pinctrl", + .of_match_table = of_aw9523_i2c_match, + }, + .probe = aw9523_probe, + .remove = aw9523_remove, + .id_table = aw9523_i2c_id_table, +}; +module_i2c_driver(aw9523_driver); + +MODULE_DESCRIPTION("Awinic AW9523 I2C GPIO Expander driver"); +MODULE_AUTHOR("AngeloGioacchino Del Regno "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:aw9523"); From b540916109b006a6d31d1590492b2221476fc0d2 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 23 Dec 2020 14:54:10 +0100 Subject: [PATCH 0265/1823] dt-bindings: pinctrl: Add bindings for Awinic AW9523/AW9523B Add bindings for the Awinic AW9523/AW9523B I2C GPIO Expander driver. Signed-off-by: AngeloGioacchino Del Regno --- .../pinctrl/awinic,aw9523-pinctrl.yaml | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/awinic,aw9523-pinctrl.yaml diff --git a/Documentation/devicetree/bindings/pinctrl/awinic,aw9523-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/awinic,aw9523-pinctrl.yaml new file mode 100644 index 000000000000..640d4d7e4cab --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/awinic,aw9523-pinctrl.yaml @@ -0,0 +1,139 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pinctrl/awinic,aw9523-pinctrl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Awinic AW9523/AW9523B I2C GPIO Expander + +maintainers: + - AngeloGioacchino Del Regno + +description: | + The Awinic AW9523/AW9523B I2C GPIO Expander featuring 16 multi-function + I/O, 256 steps PWM mode and interrupt support. + +properties: + compatible: + const: awinic,aw9523-pinctrl + + reg: + maxItems: 1 + + '#gpio-cells': + description: | + Specifying the pin number and flags, as defined in + include/dt-bindings/gpio/gpio.h + const: 2 + + gpio-controller: true + + gpio-ranges: + maxItems: 1 + + interrupt-controller: true + + interrupts: + maxItems: 1 + description: Specifies the INTN pin IRQ. + + '#interrupt-cells': + description: + Specifies the PIN numbers and Flags, as defined in defined in + include/dt-bindings/interrupt-controller/irq.h + const: 2 + + reset-gpios: + maxItems: 1 + +#PIN CONFIGURATION NODES +patternProperties: + '-pins$': + type: object + description: + Pinctrl node's client devices use subnodes for desired pin configuration. + Client device subnodes use below standard properties. + $ref: "/schemas/pinctrl/pincfg-node.yaml" + + properties: + pins: + description: + List of gpio pins affected by the properties specified in + this subnode. + items: + pattern: "^gpio([0-9]|1[0-5])$" + minItems: 1 + maxItems: 16 + + function: + description: + Specify the alternative function to be configured for the + specified pins. + + enum: [ gpio, pwm ] + + bias-disable: true + bias-pull-down: true + bias-pull-up: true + drive-open-drain: true + drive-push-pull: true + input-enable: true + input-disable: true + output-high: true + output-low: true + + required: + - pins + - function + + additionalProperties: false + +required: + - compatible + - reg + - gpio-controller + - '#gpio-cells' + - gpio-ranges + +additionalProperties: false + +examples: + # Example configuration to drive pins for a keyboard matrix + - | + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + aw9523: gpio-expander@58 { + compatible = "awinic,aw9523-pinctrl"; + reg = <0x58>; + interrupt-parent = <&tlmm>; + interrupts = <50 IRQ_TYPE_EDGE_FALLING>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&tlmm 0 0 16>; + interrupt-controller; + #interrupt-cells = <2>; + reset-gpios = <&tlmm 51 GPIO_ACTIVE_HIGH>; + + keyboard-matrix-col-pins { + pins = "gpio8", "gpio9", "gpio10", "gpio11", + "gpio12", "gpio13", "gpio14", "gpio15"; + function = "gpio"; + input-disable; + output-low; + }; + + keyboard-matrix-row-pins { + pins = "gpio0", "gpio1", "gpio2", "gpio3", + "gpio4", "gpio5", "gpio6", "gpio7"; + function = "gpio"; + bias-pull-up; + drive-open-drain; + input-enable; + }; + }; + }; From f9a159ad156028919003bd5bb2d1e69126269078 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 23 Dec 2020 15:44:37 +0100 Subject: [PATCH 0266/1823] dt-bindings: input: Add binding for gpio-fastmatrix-keyboard Add documentation for the gpio-fastmatrix-keyboard driver. Signed-off-by: AngeloGioacchino Del Regno (JAMI: merge with e7aa905a for 5.17) --- .../input/gpio-fastmatrix-keyboard.yaml | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 Documentation/devicetree/bindings/input/gpio-fastmatrix-keyboard.yaml diff --git a/Documentation/devicetree/bindings/input/gpio-fastmatrix-keyboard.yaml b/Documentation/devicetree/bindings/input/gpio-fastmatrix-keyboard.yaml new file mode 100644 index 000000000000..3830997016c0 --- /dev/null +++ b/Documentation/devicetree/bindings/input/gpio-fastmatrix-keyboard.yaml @@ -0,0 +1,129 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/gpio-fastmatrix-keyboard.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Fast modern GPIO-driven keyboard/keypad matrix driver binding + +maintainers: + - AngeloGioacchino Del Regno + +description: | + A simple common binding for matrix-connected keyboards/keypads, targeted at + defining the keys in the scope of linux key codes since that is a stable and + standardized interface at this time. + This driver uses the GPIOD API in order to support setting (and reading) an + entire array of GPIOs which is very fast (if the controller supports it) and + a requirement to support full keyboard matrices on slow external GPIO I2C + expanders, but also a great latency enhancement for faster GPIO controllers. + +allOf: + - $ref: input.yaml# + +properties: + compatible: + const: gpio-fastmatrix-keyboard + + label: + description: Descriptive name of the key. + + linux,keymap: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: | + An array of packed 1-cell entries containing the equivalent of row, + column and linux key-code as specified in dt-bindings/input/input.h + + autorescan-ms: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + Activates auto-rescanning of the matrix after receiving and processing + an event for quirky hardware that won't re-send interrupts on fast-press, + fast-depress, or multiple keys pressed events. + This time is expressed in milliseconds; if not specified, the feature is + disabled. + + col-scan-delay-us: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + Time to wait in microseconds for scan after activating a column. + If not specified, the default is 0 (no wait). + + debounce-delay-ms: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + Matrix button debouncing interval time in milliseconds. + If not specified, debouncing is disabled. + + drive-inactive-cols: + type: boolean + description: Keep direction of inactive columns as output + + col-gpios: + minItems: 2 + maxItems: 20 + + keypad,num-rows: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Number of row lines connected to the keypad controller. + + keypad,num-columns: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Number of column lines connected to the keypad controller. + + pinctrl-0: + maxItems: 1 + + pinctrl-names: + maxItems: 1 + + row-gpios: + minItems: 2 + maxItems: 20 + +required: + - compatible + - row-gpios + - col-gpios + - linux,keymap + - keypad,num-rows + - keypad,num-columns + +additionalProperties: true + +examples: + - | + #include + #include + + gpio-keyboard { + compatible = "gpio-fastmatrix-keyboard"; + label = "Keyboard over I2C Expander"; + row-gpios = + <&gpioext0 0 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>, + <&gpioext0 1 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>, + <&gpioext0 2 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>, + <&gpioext0 3 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>, + <&gpioext0 4 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>, + <&gpioext0 5 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>, + <&gpioext0 6 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>, + <&gpioext0 7 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>; + col-gpios = + <&gpioext0 8 GPIO_ACTIVE_LOW>, + <&gpioext0 9 GPIO_ACTIVE_LOW>, + <&gpioext0 10 GPIO_ACTIVE_LOW>, + <&gpioext0 11 GPIO_ACTIVE_LOW>, + <&gpioext0 12 GPIO_ACTIVE_LOW>, + <&gpioext0 13 GPIO_ACTIVE_LOW>, + <&gpioext0 14 GPIO_ACTIVE_LOW>, + <&gpioext0 15 GPIO_ACTIVE_LOW>; + + linux,keymap = < + MATRIX_KEY(0, 0, KEY_F1) MATRIX_KEY(1, 0, KEY_H) + MATRIX_KEY(2, 0, KEY_B) MATRIX_KEY(3, 0, KEY_7) + /* ... */ + >; + + keypad,num-rows = <8>; + keypad,num-columns = <8>; + }; From 0dbc76cebe5887b2a2072f5fd11e2647e7e3dfb1 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 9 Dec 2020 19:27:45 +0100 Subject: [PATCH 0267/1823] input: keyboard: Add Fast GPIO-driven keyboard/keypad matrix driver This driver is a reimplementation of matrix_keyboard, on which it is heavily based: the former was made back in 2009 and then lightly updated in 2012 to add some support for device-tree / OF. It turns out that this is not enough, nor OF is fully supported, as the global (or "clustered") interrupt is never probed, nor it made usage of the GPIOD API which, nowadays, really simplifies the job - but not only: it also provides means to set GPIO arrays for controllers that are supporting this. The latter is very important when dealing with slow GPIOs such as I2C and/or SPI expanders (and, again, not only); by using the new APIs everything fits the new systems, from simplifications of the probe/remove functions to opening possibility of using expanders to drive key matrices with or without protection diodes. But then, why wasn't the old matrix_keyboard driver modified instead of creating a new one? The problem there is that the old driver is made to support the old platform_device style and it's currently still being used by some PXA boards that are not (yet?) ported to device-tree, so it would be impossible to modernize it for good, which means that to support GPIOD (which is - really - required for the aforementioned reasons) and to fully support DT it would be necessary to wrap the old GPIO API around the new GPIOD one, creating overhead and also probably unnecessary memory usage, other than a very big driver which, at least on embedded devices (having limited resources), would be simply bad. Leaving the fact that I haven't got any old board so it's impossible for me to analyze and optimize for them. Since 98% of the users of the old driver are infact platforms that have been ported to (or are born with) DT, the introduction of a new driver that's purely made for them seemed to be the best choice, also because the expectations are (I think) that all of the old ARM-based boards will be ported to DT anyway, which will actually deprecate the good old matrix_keyboard driver. Signed-off-by: AngeloGioacchino Del Regno --- drivers/input/keyboard/Kconfig | 13 + drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/gpio_fastmatrix.c | 528 +++++++++++++++++++++++ 3 files changed, 542 insertions(+) create mode 100644 drivers/input/keyboard/gpio_fastmatrix.c diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 00292118b79b..745335e149fe 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -246,6 +246,19 @@ config KEYBOARD_GPIO To compile this driver as a module, choose M here: the module will be called gpio_keys. +config KEYBOARD_GPIO_FASTMATRIX + tristate "GPIO Keyboard" + depends on GPIOLIB + select INPUT_MATRIXKMAP + help + This driver implements support for matrix keypads and/or + keyboards connected directly to GPIO pins of a CPU, made + to also perform with slow I2C GPIO expanders and even + on matrices with no protection diodes. + + To compile this driver as a module, choose M here: the + module will be called gpio_fastmatrix + config KEYBOARD_GPIO_POLLED tristate "Polled GPIO buttons" depends on GPIOLIB diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 5f67196bb2c1..e2b63f2aca11 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_KEYBOARD_DLINK_DIR685) += dlink-dir685-touchkeys.o obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o obj-$(CONFIG_KEYBOARD_GOLDFISH_EVENTS) += goldfish_events.o obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o +obj-$(CONFIG_KEYBOARD_GPIO_FASTMATRIX) += gpio_fastmatrix.o obj-$(CONFIG_KEYBOARD_GPIO_POLLED) += gpio_keys_polled.o obj-$(CONFIG_KEYBOARD_TCA6416) += tca6416-keypad.o obj-$(CONFIG_KEYBOARD_TCA8418) += tca8418_keypad.o diff --git a/drivers/input/keyboard/gpio_fastmatrix.c b/drivers/input/keyboard/gpio_fastmatrix.c new file mode 100644 index 000000000000..ed7e028199e4 --- /dev/null +++ b/drivers/input/keyboard/gpio_fastmatrix.c @@ -0,0 +1,528 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Fast GPIO driven keyboard/keypad matrix driver + * + * Copyright (c) 2020 AngeloGioacchino Del Regno + * + * Based on matrix_keypad.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct gpio_keyboard { + struct device *dev; + struct input_dev *input_dev; + unsigned int row_shift; + + struct gpio_descs *row_gpios; + struct gpio_descs *col_gpios; + u32 autorescan_ms; + u32 debounce_ms; + u32 col_scan_us; + int clustered_irq; + + DECLARE_BITMAP(disabled_gpios, MATRIX_MAX_ROWS); + + u32 last_key_state[MATRIX_MAX_COLS]; + struct delayed_work work; + struct mutex lock; + + bool drive_inactive_cols; + bool gpio_all_disabled; + bool scan_pending; + bool stopped; +}; + +static int activate_one_column(struct gpio_keyboard *gkb, int col, bool wait) +{ + int ret; + + ret = gpiod_direction_output(gkb->col_gpios->desc[col], 1); + if (ret) + return ret; + if (wait && gkb->col_scan_us) + udelay(gkb->col_scan_us); + return 0; +} + +/* + * NOTE: If drive_inactive_cols is false, then the GPIO has to be put into + * HiZ when de-activated to cause minmal side effect when scanning other + * columns. In that case it is configured here to be input, otherwise it is + * driven with the inactive value. + */ +static int deactivate_one_column(struct gpio_keyboard *gkb, int col) +{ + gpiod_set_value_cansleep(gkb->col_gpios->desc[col], 0); + if (!gkb->drive_inactive_cols) + return gpiod_direction_input(gkb->col_gpios->desc[col]); + return 0; +} + +static int activate_all_cols(struct gpio_keyboard *gkb) +{ + unsigned long val = ULONG_MAX; + int ret, col; + + /* + * Shortcut! If we don't have to set direction, we can use + * the way faster gpiod array setting instead. + */ + if (gkb->drive_inactive_cols) { + return gpiod_set_array_value_cansleep(gkb->col_gpios->ndescs, + gkb->col_gpios->desc, + gkb->col_gpios->info, + &val); + } + + for (col = 0; col < gkb->col_gpios->ndescs; col++) { + ret = activate_one_column(gkb, col, false); + if (ret) + return ret; + } + + return 0; +} + +static int deactivate_all_cols(struct gpio_keyboard *gkb) +{ + unsigned long val = 0; + int col, ret; + + /* + * If the GPIO controller supports setting all pins at once it + * is going to be way faster, otherwise this function will fall + * back to setting all pins one at a time. + */ + ret = gpiod_set_array_value_cansleep(gkb->col_gpios->ndescs, + gkb->col_gpios->desc, + gkb->col_gpios->info, &val); + if (ret) + return ret; + + if (!gkb->drive_inactive_cols) { + for (col = 0; col < gkb->col_gpios->ndescs; col++) + gpiod_direction_input(gkb->col_gpios->desc[col]); + } + return ret; +} + +static void enable_row_irqs(struct gpio_keyboard *gkb) +{ + int i; + + if (gkb->clustered_irq > 0) + enable_irq(gkb->clustered_irq); + else { + for (i = 0; i < gkb->row_gpios->ndescs; i++) + enable_irq(gpiod_to_irq(gkb->row_gpios->desc[i])); + } +} + +static void disable_row_irqs(struct gpio_keyboard *gkb) +{ + int i; + + if (gkb->clustered_irq > 0) + disable_irq_nosync(gkb->clustered_irq); + else { + for (i = 0; i < gkb->row_gpios->ndescs; i++) + disable_irq_nosync(gpiod_to_irq(gkb->row_gpios->desc[i])); + } +} + +/* + * This gets the keys from keyboard and reports it to input subsystem + */ +static void gpio_keyboard_scan(struct work_struct *work) +{ + struct gpio_keyboard *gkb = + container_of(work, struct gpio_keyboard, work.work); + struct input_dev *input_dev = gkb->input_dev; + const unsigned short *keycodes = input_dev->keycode; + u32 *new_state; + unsigned long row_values; + int ret, row, col, code; + u32 keymask = 0; + + new_state = kzalloc(gkb->col_gpios->ndescs * sizeof(*new_state), + GFP_KERNEL); + if (!new_state) + return; + + /* de-activate all columns for scanning */ + deactivate_all_cols(gkb); + + /* assert each column and read the row status out */ + for (col = 0; col < gkb->col_gpios->ndescs; col++) { + activate_one_column(gkb, col, true); + + ret = gpiod_get_array_value_cansleep(gkb->row_gpios->ndescs, + gkb->row_gpios->desc, + gkb->row_gpios->info, + &row_values); + new_state[col] = row_values; + keymask |= new_state[col]; + + if (deactivate_one_column(gkb, col)) { + activate_all_cols(gkb); + goto end; + } + } + + activate_all_cols(gkb); + + for (col = 0; col < gkb->col_gpios->ndescs; col++) { + u32 bits_changed = gkb->last_key_state[col] ^ new_state[col]; + if (bits_changed == 0) + continue; + + for (row = 0; bits_changed; row++, bits_changed >>=1 ) { + if (!(bits_changed & BIT(0))) + continue; + + code = MATRIX_SCAN_CODE(row, col, gkb->row_shift); + input_event(input_dev, EV_MSC, MSC_SCAN, code); + input_report_key(input_dev, keycodes[code], + new_state[col] & (1 << row)); + } + } + input_sync(input_dev); + + memcpy(gkb->last_key_state, new_state, sizeof(gkb->last_key_state)); + kfree(new_state); + + /* Avoid missing key release events on quirky hardware */ + if (gkb->autorescan_ms && keymask) { + queue_delayed_work(system_highpri_wq, &gkb->work, + msecs_to_jiffies(gkb->autorescan_ms)); + return; + } +end: + /* Enable IRQs again */ + mutex_lock(&gkb->lock); + gkb->scan_pending = false; + enable_row_irqs(gkb); + mutex_unlock(&gkb->lock); +} + +static irqreturn_t gpio_keyboard_interrupt(int irq, void *id) +{ + struct gpio_keyboard *gkb = id; + + /* + * See if another IRQ beaten us to it and scheduled the + * scan already. In that case we should not try to + * disable IRQs again. + */ + if (unlikely(gkb->scan_pending || gkb->stopped)) + goto out; + + mutex_lock(&gkb->lock); + + disable_row_irqs(gkb); + gkb->scan_pending = true; + mod_delayed_work(system_highpri_wq, &gkb->work, + msecs_to_jiffies(gkb->debounce_ms)); + +out: + mutex_unlock(&gkb->lock); + return IRQ_HANDLED; +} + +static int gpio_keyboard_start(struct input_dev *dev) +{ + struct gpio_keyboard *gkb = input_get_drvdata(dev); + + gkb->stopped = false; + + /* + * Schedule an immediate key scan to capture current key state; + * columns will be activated and IRQs be enabled after the scan. + */ + schedule_delayed_work(&gkb->work, 0); + + return 0; +} + +static void gpio_keyboard_stop(struct input_dev *dev) +{ + struct gpio_keyboard *gkb = input_get_drvdata(dev); + + mutex_lock(&gkb->lock); + gkb->stopped = true; + mutex_unlock(&gkb->lock); + + flush_delayed_work(&gkb->work); + /* + * gpio_keyboard_scan() will leave IRQs enabled; + * we should disable them now. + */ + disable_row_irqs(gkb); +} + +static void __maybe_unused gpio_keyboard_wakeup_en(struct gpio_keyboard *gkb) +{ + int irq, i; + + if (gkb->clustered_irq > 0) { + if (enable_irq_wake(gkb->clustered_irq) == 0) + gkb->gpio_all_disabled = true; + } else { + + for (i = 0; i < gkb->row_gpios->ndescs; i++) { + if (!test_bit(i, gkb->disabled_gpios)) { + irq = gpiod_to_irq(gkb->row_gpios->desc[i]); + + if (enable_irq_wake(irq) == 0) + __set_bit(i, gkb->disabled_gpios); + } + } + } +} + +static void __maybe_unused gpio_keyboard_wakeup_dis(struct gpio_keyboard *gkb) +{ + int irq, i; + + if (gkb->clustered_irq > 0) { + if (gkb->gpio_all_disabled) { + disable_irq_wake(gkb->clustered_irq); + gkb->gpio_all_disabled = false; + } + } else { + for (i = 0; i < gkb->row_gpios->ndescs; i++) { + if (test_and_clear_bit(i, gkb->disabled_gpios)) { + irq = gpiod_to_irq(gkb->row_gpios->desc[i]); + disable_irq_wake(irq); + } + } + } +} + +static int __maybe_unused gpio_keyboard_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct gpio_keyboard *gkb = platform_get_drvdata(pdev); + + gpio_keyboard_stop(gkb->input_dev); + + if (device_may_wakeup(&pdev->dev)) + gpio_keyboard_wakeup_en(gkb); + + return 0; +} + +static int __maybe_unused gpio_keyboard_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct gpio_keyboard *gkb = platform_get_drvdata(pdev); + + if (device_may_wakeup(&pdev->dev)) + gpio_keyboard_wakeup_dis(gkb); + + gpio_keyboard_start(gkb->input_dev); + + return 0; +} +static SIMPLE_DEV_PM_OPS(gpio_keyboard_pm_ops, + gpio_keyboard_suspend, gpio_keyboard_resume); + +static int gpio_keyboard_init_gpio(struct platform_device *pdev, + struct gpio_keyboard *gkb) +{ + int i, ret; + + if (gkb->clustered_irq > 0) { + ret = devm_request_threaded_irq(gkb->dev, gkb->clustered_irq, + NULL, gpio_keyboard_interrupt, + IRQF_ONESHOT, "gpio-keyboard", gkb); + if (ret < 0) { + dev_err(&pdev->dev, + "Cannot get IRQ %d\n", gkb->clustered_irq); + return ret; + } + } else { + for (i = 0; i < gkb->row_gpios->ndescs; i++) { + ret = devm_request_threaded_irq( + gkb->dev, + gpiod_to_irq(gkb->row_gpios->desc[i]), + NULL, gpio_keyboard_interrupt, + IRQF_TRIGGER_HIGH | + IRQF_TRIGGER_LOW, + "gpio-keyboard", gkb); + if (ret < 0) { + dev_err(&pdev->dev, + "Cannot get IRQ for gpio%d\n", + desc_to_gpio(gkb->row_gpios->desc[i])); + return ret; + } + } + } + + /* initialized as disabled - enabled by input->open */ + disable_row_irqs(gkb); + return 0; +} + +static int gpio_keyboard_parse_dt(struct gpio_keyboard *gkb) +{ + struct device *dev = gkb->dev; + int rc; + + /* + * Get as GPIOD_ASIS to use the configuration that comes from + * device-tree. Anyway, in many cases the row is configured as + * input, while the column is configured as output-high. + * + * This may vary depending on the hardware. + */ + gkb->row_gpios = devm_gpiod_get_array(dev, "row", GPIOD_ASIS); + if (IS_ERR(gkb->row_gpios)) + return PTR_ERR(gkb->row_gpios); + + gkb->col_gpios = devm_gpiod_get_array(dev, "col", GPIOD_ASIS); + if (IS_ERR(gkb->col_gpios)) + return PTR_ERR(gkb->col_gpios); + + /* All of these additional properties are optional */ + device_property_read_string(dev, "label", &gkb->input_dev->name); + + if (device_property_read_bool(dev, "autorepeat")) + __set_bit(EV_REP, gkb->input_dev->evbit); + + gkb->drive_inactive_cols = + device_property_read_bool(dev, "drive-inactive-cols"); + + rc = device_property_read_u32(dev, "autorescan-ms", + &gkb->autorescan_ms); + if (rc < 0) + gkb->autorescan_ms = 0; + + rc = device_property_read_u32(dev, "debounce-delay-ms", + &gkb->debounce_ms); + if (rc < 0) + gkb->debounce_ms = 0; + + rc = device_property_read_u32(dev, "col-scan-delay-us", + &gkb->col_scan_us); + if (rc < 0) + gkb->col_scan_us = 0; + + return 0; +} + +static int gpio_keyboard_probe(struct platform_device *pdev) +{ + struct gpio_keyboard *gkb; + bool wake; + int irq, ret; + + gkb = devm_kmalloc(&pdev->dev, sizeof(*gkb), GFP_KERNEL); + if (!gkb) + return -ENOMEM; + + gkb->dev = &pdev->dev; + gkb->input_dev = devm_input_allocate_device(&pdev->dev); + if (!gkb->input_dev) + return -ENOMEM; + + ret = gpio_keyboard_parse_dt(gkb); + if (ret) + return ret; + + irq = platform_get_irq_optional(pdev, 0); + gkb->clustered_irq = (irq > 0) ? irq : 0; + gkb->row_shift = get_count_order(gkb->col_gpios->ndescs); + gkb->stopped = true; + INIT_DELAYED_WORK(&gkb->work, gpio_keyboard_scan); + memset(gkb->last_key_state, 0, sizeof(gkb->last_key_state)); + mutex_init(&gkb->lock); + + if (!gkb->input_dev->name) + gkb->input_dev->name = pdev->name; + gkb->input_dev->id.bustype = BUS_HOST; + gkb->input_dev->dev.parent = &pdev->dev; + gkb->input_dev->open = gpio_keyboard_start; + gkb->input_dev->close = gpio_keyboard_stop; + + ret = matrix_keypad_build_keymap(NULL, NULL, + gkb->row_gpios->ndescs, + gkb->col_gpios->ndescs, + NULL, gkb->input_dev); + if (ret) { + dev_err(&pdev->dev, "failed to build keymap\n"); + return ret; + } + + input_set_capability(gkb->input_dev, EV_MSC, MSC_SCAN); + input_set_drvdata(gkb->input_dev, gkb); + + ret = gpio_keyboard_init_gpio(pdev, gkb); + if (ret) + return ret; + + ret = input_register_device(gkb->input_dev); + if (ret) + return ret; + + wake = device_property_read_bool(&pdev->dev, "wakeup-source"); + device_init_wakeup(&pdev->dev, wake); + + platform_set_drvdata(pdev, gkb); + + return 0; +} + +static int gpio_keyboard_remove(struct platform_device *pdev) +{ + struct gpio_keyboard *gkb = platform_get_drvdata(pdev); + int i; + + if (gkb->clustered_irq > 0) { + free_irq(gkb->clustered_irq, gkb); + } else { + for (i = 0; i < gkb->row_gpios->ndescs; i++) + free_irq(gpiod_to_irq(gkb->row_gpios->desc[i]), gkb); + } + input_unregister_device(gkb->input_dev); + + return 0; +} + +static const struct of_device_id gpio_keyboard_dt_match[] = { + { .compatible = "gpio-fastmatrix-keyboard" }, + { } +}; +MODULE_DEVICE_TABLE(of, gpio_keyboard_dt_match); + +static struct platform_driver gpio_keyboard_driver = { + .probe = gpio_keyboard_probe, + .remove = gpio_keyboard_remove, + .driver = { + .name = "gpio-fastmatrix-keyboard", + .pm = &gpio_keyboard_pm_ops, + .of_match_table = gpio_keyboard_dt_match, + }, +}; +module_platform_driver(gpio_keyboard_driver); + +MODULE_AUTHOR("AngeloGioacchino Del Regno "); +MODULE_DESCRIPTION("Fast GPIO driven keyboard/keypad matrix driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:gpio-fastmatrix-keyboard"); From 59798e059012a8c6b175577a3d777fccb4bf8f1d Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 20 Jan 2021 22:33:36 +0100 Subject: [PATCH 0268/1823] clk: qcom: gcc-msm8998: Set MISC flags, mark hmss/gpu-ahb critical It is being evaluated whether this commit is really needed. DONOTUPSTREAM. (JAMI: fixed up for v5.16-rc1) --- drivers/clk/qcom/gcc-msm8998.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/clk/qcom/gcc-msm8998.c b/drivers/clk/qcom/gcc-msm8998.c index 33473c52eb90..42df6099699c 100644 --- a/drivers/clk/qcom/gcc-msm8998.c +++ b/drivers/clk/qcom/gcc-msm8998.c @@ -2080,6 +2080,7 @@ static struct clk_branch gcc_bimc_gfx_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "gcc_bimc_gfx_clk", + .flags = CLK_IS_CRITICAL, .ops = &clk_branch2_ops, }, }, @@ -2220,6 +2221,7 @@ static struct clk_rcg2 hmss_gpll0_clk_src = { .name = "hmss_gpll0_clk_src", .parent_data = gcc_parent_data_1, .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .flags = CLK_IS_CRITICAL, .ops = &clk_rcg2_ops, }, }; @@ -3247,6 +3249,14 @@ static int gcc_msm8998_probe(struct platform_device *pdev) if (ret) return ret; + /* + * GCC_MMSS_MISC - GCC_GPU_MISC: + * 1. Disable the GPLL0 active input to MMSS and GPU + * 2. Select clk division 1 (CLK/2) + */ + regmap_write(regmap, 0x0902C, 0x10003); /* MMSS*/ + regmap_write(regmap, 0x71028, 0x10003); /* GPU */ + return qcom_cc_really_probe(pdev, &gcc_msm8998_desc, regmap); } From 878adc31071b02c1511e8908974a78dcb8d3dff0 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 23 Dec 2020 12:35:07 +0100 Subject: [PATCH 0269/1823] clk: qcom: mmcc-msm8998: Set CLK_GET_RATE_NOCACHE to pixel/byte clks The pixel and byte clocks rate should not be cached, as a VCO shutdown may clear the frequency setup and this may not be set again due to the cached rate being present. This will also be useful when shadow clocks will be implemented in the DSI PLL for seamless timing/resolution switch. Signed-off-by: AngeloGioacchino Del Regno --- drivers/clk/qcom/mmcc-msm8998.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/clk/qcom/mmcc-msm8998.c b/drivers/clk/qcom/mmcc-msm8998.c index c421b1291651..07dfdb9e280d 100644 --- a/drivers/clk/qcom/mmcc-msm8998.c +++ b/drivers/clk/qcom/mmcc-msm8998.c @@ -511,7 +511,7 @@ static struct clk_rcg2 byte0_clk_src = { .parent_data = mmss_xo_dsibyte, .num_parents = ARRAY_SIZE(mmss_xo_dsibyte), .ops = &clk_byte2_ops, - .flags = CLK_SET_RATE_PARENT, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, }, }; @@ -524,7 +524,7 @@ static struct clk_rcg2 byte1_clk_src = { .parent_data = mmss_xo_dsibyte, .num_parents = ARRAY_SIZE(mmss_xo_dsibyte), .ops = &clk_byte2_ops, - .flags = CLK_SET_RATE_PARENT, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, }, }; @@ -1075,7 +1075,7 @@ static struct clk_rcg2 pclk0_clk_src = { .parent_data = mmss_xo_dsi0pll_dsi1pll, .num_parents = ARRAY_SIZE(mmss_xo_dsi0pll_dsi1pll), .ops = &clk_pixel_ops, - .flags = CLK_SET_RATE_PARENT, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, }, }; @@ -1089,7 +1089,7 @@ static struct clk_rcg2 pclk1_clk_src = { .parent_data = mmss_xo_dsi0pll_dsi1pll, .num_parents = ARRAY_SIZE(mmss_xo_dsi0pll_dsi1pll), .ops = &clk_pixel_ops, - .flags = CLK_SET_RATE_PARENT, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, }, }; From 707f3fc86f6a24e9f710887eb028bd8d0df82580 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 27 Jul 2021 12:35:49 +0300 Subject: [PATCH 0270/1823] drm/msm/dsi_phy_10nm: Fix bad VCO rate calculation --- drivers/gpu/drm/msm/dsi/phy/dsi_phy_10nm.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_10nm.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_10nm.c index 27b592c776a3..9340bbf9d03c 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_10nm.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_10nm.c @@ -122,7 +122,7 @@ static void dsi_pll_calc_dec_frac(struct dsi_pll_10nm *pll, struct dsi_pll_confi pll_freq = pll->vco_current_rate; - divider = fref * 2; + divider = fref; multiplier = 1 << FRAC_BITS; dec_multiple = div_u64(pll_freq * multiplier, divider); @@ -443,12 +443,11 @@ static unsigned long dsi_pll_10nm_vco_recalc_rate(struct clk_hw *hw, * 1. Assumes prescaler is disabled */ multiplier = 1 << FRAC_BITS; - pll_freq = dec * (ref_clk * 2); - tmp64 = (ref_clk * 2 * frac); + pll_freq = dec * ref_clk; + tmp64 = ref_clk * frac; pll_freq += div_u64(tmp64, multiplier); vco_rate = pll_freq; - pll_10nm->vco_current_rate = vco_rate; DBG("DSI PLL%d returning vco rate = %lu, dec = %x, frac = %x", pll_10nm->phy->id, (unsigned long)vco_rate, dec, frac); From 2633780796bfa7db3d0cd1c74922fb1c94ff98ba Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 9 Aug 2021 15:57:17 +0200 Subject: [PATCH 0271/1823] yoshino: Fix too high overheating backlight, enable all wled strings maple --- .../boot/dts/qcom/msm8998-sony-xperia-yoshino-maple.dts | 5 +++-- arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-maple.dts b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-maple.dts index 4c2cbae89193..1db80a529527 100644 --- a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-maple.dts +++ b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-maple.dts @@ -106,7 +106,8 @@ &pmi8998_wled { status = "okay"; - qcom,num-strings = <3>; - qcom,enabled-strings = <0 1 2>; + //qcom,auto-string-detection; + qcom,num-strings = <4>; + qcom,enabled-strings = <0 1 2 3>; }; diff --git a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi index 65f007429d5e..efbcaf7827dc 100644 --- a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi @@ -491,7 +491,7 @@ &pmi8998_wled { status = "okay"; - default-brightness = <3000>; + default-brightness = <800>; qcom,switching-freq = <800>; qcom,ovp-millivolt = <29600>; qcom,current-boost-limit = <970>; From 4dbc2501ff3ccaa5712581fb0ac1905043abed3e Mon Sep 17 00:00:00 2001 From: Jami Kettunen Date: Fri, 28 Jan 2022 15:42:37 +0200 Subject: [PATCH 0272/1823] regulator: qcom-labibb: Always disable interrupts during OCP & SC Originally from: cd95ecae --- drivers/regulator/qcom-labibb-regulator.c | 32 +++++++++++++---------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/drivers/regulator/qcom-labibb-regulator.c b/drivers/regulator/qcom-labibb-regulator.c index 639b71eb41ff..3143e9765029 100644 --- a/drivers/regulator/qcom-labibb-regulator.c +++ b/drivers/regulator/qcom-labibb-regulator.c @@ -279,12 +279,6 @@ static irqreturn_t qcom_labibb_ocp_isr(int irq, void *chip) } vreg->ocp_irq_count++; - /* - * Disable the interrupt temporarily, or it will fire continuously; - * we will re-enable it in the recovery worker function. - */ - disable_irq_nosync(irq); - /* Warn the user for overcurrent */ dev_warn(vreg->dev, "Over-Current interrupt fired!\n"); @@ -304,6 +298,12 @@ end: if (ret) return IRQ_NONE; + /* + * Disable the interrupt temporarily, or it will fire continuously; + * we will re-enable it in the recovery worker function. + */ + disable_irq_nosync(irq); + return IRQ_HANDLED; } @@ -316,8 +316,11 @@ static int qcom_labibb_set_ocp(struct regulator_dev *rdev, int lim, int irq_trig_low, ret; /* - * labibb supports only protection - and does not support setting - * limit. Furthermore, we don't support disabling protection. + * labibb does not support specifying a current limit that is + * special to over-current protection, but only a global one + * that will be used for both current limiting and protection; + * for this reason, we only support enabling the OCP here. + * Furthermore, we don't support disabling protection. */ if (lim || severity != REGULATOR_SEVERITY_PROT || !enable) return -EINVAL; @@ -540,12 +543,6 @@ static irqreturn_t qcom_labibb_sc_isr(int irq, void *chip) /* Warn the user for short circuit */ dev_warn(vreg->dev, "Short-Circuit interrupt fired!\n"); - /* - * Disable the interrupt temporarily, or it will fire continuously; - * we will re-enable it in the recovery worker function. - */ - disable_irq_nosync(irq); - /* Signal out of regulation event to drivers */ regulator_notifier_call_chain(vreg->rdev, REGULATOR_EVENT_REGULATION_OUT, NULL); @@ -553,6 +550,13 @@ static irqreturn_t qcom_labibb_sc_isr(int irq, void *chip) /* Schedule the short-circuit handling as high-priority work */ mod_delayed_work(system_highpri_wq, &vreg->sc_recovery_work, msecs_to_jiffies(SC_RECOVERY_INTERVAL_MS)); + + /* + * Disable the interrupt temporarily, or it will fire continuously; + * we will re-enable it in the recovery worker function. + */ + disable_irq_nosync(irq); + return IRQ_HANDLED; } From 0eec71b3b38c620942a6956c1e2bccbe7751f323 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 9 Aug 2021 16:35:27 +0200 Subject: [PATCH 0273/1823] arm64: dts: qcom: msm8998: Add qcom,adreno-smmu compatible --- arch/arm64/boot/dts/qcom/msm8998.dtsi | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi index 762cd4497126..2b000c531585 100644 --- a/arch/arm64/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi @@ -2107,7 +2107,8 @@ }; adreno_smmu: iommu@5040000 { - compatible = "qcom,msm8998-smmu-v2", "qcom,smmu-v2"; + compatible = "qcom,msm8998-smmu-v2", "qcom,smmu-v2", + "qcom,adreno-smmu"; reg = <0x05040000 0x10000>; clocks = <&gcc GCC_GPU_CFG_AHB_CLK>, <&gcc GCC_BIMC_GFX_CLK>, From 695a65ac50f39397e53d14a2fa054523637b05c2 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 9 Aug 2021 16:48:22 +0200 Subject: [PATCH 0274/1823] arm64: dts: qcom: msm8998: Add imem pil-reloc-info for firmware loading Firmware loading (mainly the modem) may require IMEM PIL relocation informations: specify this imem region in dt for qcom_pil_info to use it. Signed-off-by: AngeloGioacchino Del Regno --- arch/arm64/boot/dts/qcom/msm8998.dtsi | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi index 2b000c531585..2d0d27de82ec 100644 --- a/arch/arm64/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi @@ -3401,6 +3401,21 @@ ; }; + imem@146bf000 { + compatible = "simple-mfd"; + reg = <0x146bf000 0x1000>; + + #address-cells = <1>; + #size-cells = <1>; + + ranges = <0 0x146bf000 0x1000>; + + pil-reloc@94c { + compatible = "qcom,pil-reloc-info"; + reg = <0x94c 0xc8>; + }; + }; + remoteproc_adsp: remoteproc@17300000 { compatible = "qcom,msm8998-adsp-pas"; reg = <0x17300000 0x4040>; From 051c715c7535f6214e45b7a68f4c32a94e89c708 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 9 Aug 2021 16:56:45 +0200 Subject: [PATCH 0275/1823] dt-bindings: clock: gcc-msm8998: Add Q6 and LPASS clocks definitions Add definitions for the Q6 BIMC, LPASS core and adsp smmu clocks, required to enable audio functionality on MSM8998. Signed-off-by: AngeloGioacchino Del Regno (JAMI: merge with e122a6a4 for 5.17 & fixup for 6.0-rc1) --- include/dt-bindings/clock/qcom,gcc-msm8998.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/dt-bindings/clock/qcom,gcc-msm8998.h b/include/dt-bindings/clock/qcom,gcc-msm8998.h index 1badb4f9c58f..9b124ee63be9 100644 --- a/include/dt-bindings/clock/qcom,gcc-msm8998.h +++ b/include/dt-bindings/clock/qcom,gcc-msm8998.h @@ -190,6 +190,8 @@ #define AGGRE2_SNOC_NORTH_AXI 181 #define SSC_XO 182 #define SSC_CNOC_AHBS_CLK 183 +#define HLOS1_VOTE_LPASS_CORE_SMMU_CLK 184 +#define HLOS1_VOTE_LPASS_ADSP_SMMU_CLK 185 #define PCIE_0_GDSC 0 #define UFS_GDSC 1 @@ -304,5 +306,6 @@ #define GCC_QUSB2PHY_PRIM_BCR 106 #define GCC_QUSB2PHY_SEC_BCR 107 #define GCC_MSS_RESTART 108 +#define GCC_MSS_Q6_BIMC_AXI_CLK 109 #endif From 190a5aaae00ee2eae7cac9e3aa91fa477e40037b Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 9 Aug 2021 16:58:20 +0200 Subject: [PATCH 0276/1823] clk: qcom: gcc-msm8998: Add q6 bimc and lpass core, adsp SMMU clocks Add the Q6 BIMC, LPASS core/adsp SMMU clocks to support audio related functionality on MSM8998 and APQ variants. Please note that the Q6 and the lpass iommu also need GDSCs to be enabled in order to initialize, which are going to be added in a later commit. Signed-off-by: AngeloGioacchino Del Regno (JAMI: fixup for 6.0-rc2) --- drivers/clk/qcom/gcc-msm8998.c | 40 ++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/drivers/clk/qcom/gcc-msm8998.c b/drivers/clk/qcom/gcc-msm8998.c index 42df6099699c..8c86de5c347e 100644 --- a/drivers/clk/qcom/gcc-msm8998.c +++ b/drivers/clk/qcom/gcc-msm8998.c @@ -2887,6 +2887,43 @@ static struct clk_branch ssc_cnoc_ahbs_clk = { }, }; +static struct clk_branch hlos1_vote_lpass_core_smmu_clk = { + .halt_reg = 0x7D010, + .clkr = { + .enable_reg = 0x7D010, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) { + .name = "hlos1_vote_lpass_core_smmu_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch hlos1_vote_lpass_adsp_smmu_clk = { + .halt_reg = 0x7D014, + .clkr = { + .enable_reg = 0x7D014, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) { + .name = "hlos1_vote_lpass_adsp_smmu_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_mss_q6_bimc_axi_clk = { + .halt_reg = 0x8A040, + .clkr = { + .enable_reg = 0x8A040, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data) { + .name = "gcc_mss_q6_bimc_axi_clk", + .flags = CLK_IS_CRITICAL, + .ops = &clk_branch2_ops, + }, + }, +}; + static struct gdsc pcie_0_gdsc = { .gdscr = 0x6b004, .gds_hw_ctrl = 0x0, @@ -3094,6 +3131,9 @@ static struct clk_regmap *gcc_msm8998_clocks[] = { [AGGRE2_SNOC_NORTH_AXI] = &aggre2_snoc_north_axi_clk.clkr, [SSC_XO] = &ssc_xo_clk.clkr, [SSC_CNOC_AHBS_CLK] = &ssc_cnoc_ahbs_clk.clkr, + [GCC_MSS_Q6_BIMC_AXI_CLK] = &gcc_mss_q6_bimc_axi_clk.clkr, + [HLOS1_VOTE_LPASS_CORE_SMMU_CLK] = &hlos1_vote_lpass_core_smmu_clk.clkr, + [HLOS1_VOTE_LPASS_ADSP_SMMU_CLK] = &hlos1_vote_lpass_adsp_smmu_clk.clkr, }; static struct gdsc *gcc_msm8998_gdscs[] = { From 03cf81d3a0e042ce65a2571e6bd974ee0a579ba1 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 9 Aug 2021 17:01:49 +0200 Subject: [PATCH 0277/1823] dt-bindings: clock: gcc-msm8998: Add LPASS adsp/core GDSCs definitions Add the GDSC definitions for the LPASS_ADSP_GDSC and LPASS_CORE_GDSC as a final step to enable the required clock tree for the lpass iommu and for the audio dsp itself. Signed-off-by: AngeloGioacchino Del Regno --- include/dt-bindings/clock/qcom,gcc-msm8998.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/dt-bindings/clock/qcom,gcc-msm8998.h b/include/dt-bindings/clock/qcom,gcc-msm8998.h index 9b124ee63be9..f4cba2343abe 100644 --- a/include/dt-bindings/clock/qcom,gcc-msm8998.h +++ b/include/dt-bindings/clock/qcom,gcc-msm8998.h @@ -196,6 +196,8 @@ #define PCIE_0_GDSC 0 #define UFS_GDSC 1 #define USB_30_GDSC 2 +#define LPASS_ADSP_GDSC 3 +#define LPASS_CORE_GDSC 4 #define GCC_BLSP1_QUP1_BCR 0 #define GCC_BLSP1_QUP2_BCR 1 From f2386073b9ce6eedcf0a025bed99d98b81853878 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 9 Aug 2021 17:03:36 +0200 Subject: [PATCH 0278/1823] clk: qcom: gcc-msm8998: Add LPASS adsp and core GDSCs As a final step to entirely enable the required clock tree for the lpass iommu and audio dsp, add the lpass core/adsp GDSCs. As a side note, it was found out that disabling the lpass core GDSC at any time would cause a system lockup (and reboot): disabling this GDSC will leave the lpass iommu completely unclocked, losing its state entirely - including the secure contexts that have been previously set-up from the bootloader/TrustZone. Losing this IOMMU configuration will trigger a hypervisor fault, which will reboot the system; the only workaround for this issue is to declare the lpass core gdsc as always-on. It should also not be forgotten that this is all about firmware and there may be a version of it that doesn't enable this GDSC at all before booting Linux, which is the reason why this specific declaration wasn't simply omitted. Signed-off-by: AngeloGioacchino Del Regno --- drivers/clk/qcom/gcc-msm8998.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/clk/qcom/gcc-msm8998.c b/drivers/clk/qcom/gcc-msm8998.c index 8c86de5c347e..dc2fd84e23f3 100644 --- a/drivers/clk/qcom/gcc-msm8998.c +++ b/drivers/clk/qcom/gcc-msm8998.c @@ -2954,6 +2954,26 @@ static struct gdsc usb_30_gdsc = { .flags = VOTABLE, }; +static struct gdsc hlos1_vote_lpass_adsp = { + .gdscr = 0x7d034, + .gds_hw_ctrl = 0x0, + .pd = { + .name = "lpass_adsp_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE, +}; + +static struct gdsc hlos1_vote_lpass_core = { + .gdscr = 0x7d038, + .gds_hw_ctrl = 0x0, + .pd = { + .name = "lpass_core_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = ALWAYS_ON, +}; + static struct clk_regmap *gcc_msm8998_clocks[] = { [BLSP1_QUP1_I2C_APPS_CLK_SRC] = &blsp1_qup1_i2c_apps_clk_src.clkr, [BLSP1_QUP1_SPI_APPS_CLK_SRC] = &blsp1_qup1_spi_apps_clk_src.clkr, @@ -3140,6 +3160,8 @@ static struct gdsc *gcc_msm8998_gdscs[] = { [PCIE_0_GDSC] = &pcie_0_gdsc, [UFS_GDSC] = &ufs_gdsc, [USB_30_GDSC] = &usb_30_gdsc, + [LPASS_ADSP_GDSC] = &hlos1_vote_lpass_adsp, + [LPASS_CORE_GDSC] = &hlos1_vote_lpass_core, }; static const struct qcom_reset_map gcc_msm8998_resets[] = { From d5b12139e66985f4707789b9b7e23a5412a79f1a Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 9 Aug 2021 17:11:02 +0200 Subject: [PATCH 0279/1823] arm64: dts: qcom: msm8998: Add disabled support for lpass iommu for q6 Add support for the LPASS (Q6) SMMU and keep it disabled as this is used only when the audio DSP is present and used, which is not mandatory to have. It is expected for board-specific device-trees to enable this node if supported. Signed-off-by: AngeloGioacchino Del Regno --- arch/arm64/boot/dts/qcom/msm8998.dtsi | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi index 2d0d27de82ec..6abb33bcb17a 100644 --- a/arch/arm64/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi @@ -2146,6 +2146,33 @@ "gpll0"; }; + lpass_q6_smmu: iommu@5100000 { + compatible = "qcom,msm8998-smmu-v2", "qcom,smmu-v2"; + reg = <0x05100000 0x40000>; + clocks = <&gcc HLOS1_VOTE_LPASS_ADSP_SMMU_CLK>; + clock-names = "iface"; + + #global-interrupts = <0>; + #iommu-cells = <1>; + interrupts = + , + , + , + , + , + , + , + , + , + , + , + , + ; + + power-domains = <&gcc LPASS_ADSP_GDSC>; + status = "disabled"; + }; + remoteproc_slpi: remoteproc@5800000 { compatible = "qcom,msm8998-slpi-pas"; reg = <0x05800000 0x4040>; From 327e8d148364af581a180dded7741c4d95c4e8ee Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 9 Aug 2021 17:14:35 +0200 Subject: [PATCH 0280/1823] arm64: dts: qcom: msm8998: Define fastrpc ADSP compute context banks If the target board supports ADSP, it is essential to have fastrpc compute context banks defined. This commit adds the always usable unsecured context banks only. Signed-off-by: AngeloGioacchino Del Regno --- arch/arm64/boot/dts/qcom/msm8998.dtsi | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi index 6abb33bcb17a..2d04062bcb93 100644 --- a/arch/arm64/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi @@ -3473,6 +3473,33 @@ label = "lpass"; qcom,remote-pid = <2>; mboxes = <&apcs_glb 9>; + + fastrpc { + compatible = "qcom,fastrpc"; + qcom,glink-channels = "fastrpcglink-apps-dsp"; + label = "adsp"; + #address-cells = <1>; + #size-cells = <0>; + + cb@2 { + compatible = "qcom,fastrpc-compute-cb"; + dma-ranges = <0 0x60000000 0 0x18000000>; + reg = <2>; + iommus = <&lpass_q6_smmu 2>; + }; + + cb@5 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <5>; + iommus = <&lpass_q6_smmu 5>; + }; + + cb@6 { + compatible = "qcom,fastrpc-compute-cb"; + reg = <6>; + iommus = <&lpass_q6_smmu 6>; + }; + }; }; }; From b32ea4a02e9215cf1216cda027ada5e19dadc5df Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 9 Aug 2021 17:18:05 +0200 Subject: [PATCH 0281/1823] arm64: dts: qcom: msm8998: Add APR services configuration Add the standard APR Q6 services configuration for MSM8998, used by boards supporting the audio dsp. Signed-off-by: AngeloGioacchino Del Regno --- arch/arm64/boot/dts/qcom/msm8998.dtsi | 55 +++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi index 2d04062bcb93..48fe76bc8c45 100644 --- a/arch/arm64/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi @@ -8,6 +8,7 @@ #include #include #include +#include #include /* Macro for CPR-Hardened OPP entries - Example phandle: cprh_opp0 */ @@ -3474,6 +3475,60 @@ qcom,remote-pid = <2>; mboxes = <&apcs_glb 9>; + apr { + compatible = "qcom,apr-v2"; + power-domains = <&gcc LPASS_ADSP_GDSC>; + qcom,glink-channels = "apr_audio_svc"; + qcom,apr-domain = ; + #address-cells = <1>; + #size-cells = <0>; + qcom,intents = <512 20>; + + apr-service@3 { + reg = ; + compatible = "qcom,q6core"; + qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd"; + }; + + q6afe: apr-service@4 { + compatible = "qcom,q6afe"; + reg = ; + qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd"; + q6afedai: dais { + compatible = "qcom,q6afe-dais"; + #address-cells = <1>; + #size-cells = <0>; + #sound-dai-cells = <1>; + hdmi@1 { + reg = <1>; + }; + }; + }; + + q6asm: apr-service@7 { + compatible = "qcom,q6asm"; + reg = ; + qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd"; + q6asmdai: dais { + compatible = "qcom,q6asm-dais"; + #address-cells = <1>; + #size-cells = <0>; + #sound-dai-cells = <1>; + iommus = <&lpass_q6_smmu 1>; + }; + }; + + q6adm: apr-service@8 { + compatible = "qcom,q6adm"; + reg = ; + qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd"; + q6routing: routing { + compatible = "qcom,q6adm-routing"; + #sound-dai-cells = <0>; + }; + }; + }; + fastrpc { compatible = "qcom,fastrpc"; qcom,glink-channels = "fastrpcglink-apps-dsp"; From 08f802d98205984c64793c4f9b218b380f16c084 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 9 Aug 2021 19:09:29 +0200 Subject: [PATCH 0282/1823] arm64: dts: qcom: msm8998-xperia: Enable lpass smmu To support audio dsp functionality, enable the lpass smmu. The firmware distributed with the MSM8998 Sony Xperia smartphones requires us to skip resetting CB12 of this SMMU and to use CB11 as a context bank to emulate bypass streams. Signed-off-by: AngeloGioacchino Del Regno (JAMI: fixed up for v5.16-rc1) --- arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi index efbcaf7827dc..6043b720106c 100644 --- a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi @@ -381,6 +381,13 @@ regulator-soft-start; }; +&lpass_q6_smmu { + qcom,bypass-cbndx = /bits/ 8 <11>; + qcom,reset-nodisable-cbs = /bits/ 8 <12>; + + status = "ok"; +}; + &mmcc { status = "ok"; }; From 5be5c146c2cffdfb32ebaeb0d16c3c61212f7bbd Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 9 Aug 2021 19:21:02 +0200 Subject: [PATCH 0283/1823] ASoC: qcom: Add MSM8998 sound card support Add MSM8998 sound support: this driver supports basic functionality and headphone jack. Signed-off-by: AngeloGioacchino Del Regno (JAMI: fixup for 5.17 & 6.0-rc2) --- sound/soc/qcom/Kconfig | 11 + sound/soc/qcom/Makefile | 2 + sound/soc/qcom/msm8998.c | 501 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 514 insertions(+) create mode 100644 sound/soc/qcom/msm8998.c diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 8c7398bc1ca8..8f4d6efa8968 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -148,6 +148,17 @@ config SND_SOC_MSM8996 APQ8096 SoC-based systems. Say Y if you want to use audio device on this SoCs +config SND_SOC_MSM8998 + tristate "SoC Machine driver for MSM8998 and APQ8098 boards" + depends on QCOM_APR && I2C && SOUNDWIRE + depends on COMMON_CLK + select SND_SOC_QDSP6 + select SND_SOC_QCOM_COMMON + help + To add support for audio on Qualcomm Technologies Inc. + MSM8998 SoC-based systems. + Say Y if you want to use audio device on this SoCs. + config SND_SOC_SDM845 tristate "SoC Machine driver for SDM845 boards" depends on QCOM_APR && I2C && SOUNDWIRE diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile index 8b97172cf990..583a49a3102e 100644 --- a/sound/soc/qcom/Makefile +++ b/sound/soc/qcom/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_SND_SOC_LPASS_SC7280) += snd-soc-lpass-sc7280.o snd-soc-storm-objs := storm.o snd-soc-apq8016-sbc-objs := apq8016_sbc.o snd-soc-apq8096-objs := apq8096.o +snd-soc-msm8998-objs := msm8998.o snd-soc-sc7180-objs := sc7180.o snd-soc-sc7280-objs := sc7280.o snd-soc-sdm845-objs := sdm845.o @@ -32,6 +33,7 @@ snd-soc-qcom-common-objs := common.o obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o obj-$(CONFIG_SND_SOC_APQ8016_SBC) += snd-soc-apq8016-sbc.o obj-$(CONFIG_SND_SOC_MSM8996) += snd-soc-apq8096.o +obj-$(CONFIG_SND_SOC_MSM8998) += snd-soc-msm8998.o obj-$(CONFIG_SND_SOC_SC7180) += snd-soc-sc7180.o obj-$(CONFIG_SND_SOC_SC7280) += snd-soc-sc7280.o obj-$(CONFIG_SND_SOC_SC8280XP) += snd-soc-sc8280xp.o diff --git a/sound/soc/qcom/msm8998.c b/sound/soc/qcom/msm8998.c new file mode 100644 index 000000000000..e40bade35131 --- /dev/null +++ b/sound/soc/qcom/msm8998.c @@ -0,0 +1,501 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Based on sdm845.c + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * MSM8998 ASoC driver + * Copyright (c) 2021, AngeloGioacchino Del Regno + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "common.h" +#include "qdsp6/q6afe.h" + +#define DRIVER_NAME "msm8998" +#define DEFAULT_SAMPLE_RATE_48K 48000 +#define DEFAULT_MCLK_RATE 24576000 +#define TDM_BCLK_RATE 6144000 +#define MI2S_BCLK_RATE 1536000 +#define LEFT_SPK_TDM_TX_MASK 0x30 +#define RIGHT_SPK_TDM_TX_MASK 0xC0 +#define SPK_TDM_RX_MASK 0x03 +#define NUM_TDM_SLOTS 8 +#define SLIM_MAX_TX_PORTS 17 +#define SLIM_MAX_RX_PORTS 14 +#define WCD934X_DEFAULT_MCLK_RATE 9600000 + +struct msm8998_snd_data { + struct snd_soc_jack jack; + bool jack_setup; + bool stream_prepared[SLIM_MAX_RX_PORTS]; + struct snd_soc_card *card; + uint32_t quat_tdm_clk_count; + struct sdw_stream_runtime *sruntime[SLIM_MAX_RX_PORTS]; +}; + +static unsigned int tdm_slot_offset[8] = {0, 4, 8, 12, 16, 20, 24, 28}; + +static int msm8998_slim_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct snd_soc_dai *codec_dai; + struct msm8998_snd_data *pdata = snd_soc_card_get_drvdata(rtd->card); + u32 rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS]; + struct sdw_stream_runtime *sruntime; + u32 rx_ch_cnt = 0, tx_ch_cnt = 0; + int ret = 0, i; + + for_each_rtd_codec_dais(rtd, i, codec_dai) { + sruntime = snd_soc_dai_get_stream(codec_dai, + substream->stream); + if (sruntime != ERR_PTR(-ENOTSUPP)) + pdata->sruntime[cpu_dai->id] = sruntime; + + ret = snd_soc_dai_get_channel_map(codec_dai, + &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch); + + if (ret != 0 && ret != -ENOTSUPP) { + pr_err("failed to get codec chan map, err:%d\n", ret); + return ret; + } else if (ret == -ENOTSUPP) { + /* Ignore unsupported */ + continue; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL, + rx_ch_cnt, rx_ch); + else + ret = snd_soc_dai_set_channel_map(cpu_dai, tx_ch_cnt, + tx_ch, 0, NULL); + } + + return 0; +} + +static int msm8998_tdm_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct snd_soc_dai *codec_dai; + int ret = 0, j; + int channels, slot_width; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + slot_width = 16; + break; + default: + dev_err(rtd->dev, "%s: invalid param format 0x%x\n", + __func__, params_format(params)); + return -EINVAL; + } + + channels = params_channels(params); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, 0x3, + 8, slot_width); + if (ret < 0) { + dev_err(rtd->dev, "%s: failed to set tdm slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL, + channels, tdm_slot_offset); + if (ret < 0) { + dev_err(rtd->dev, "%s: failed to set channel map, err:%d\n", + __func__, ret); + goto end; + } + } else { + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0xf, 0, + 8, slot_width); + if (ret < 0) { + dev_err(rtd->dev, "%s: failed to set tdm slot, err:%d\n", + __func__, ret); + goto end; + } + + ret = snd_soc_dai_set_channel_map(cpu_dai, channels, + tdm_slot_offset, 0, NULL); + if (ret < 0) { + dev_err(rtd->dev, "%s: failed to set channel map, err:%d\n", + __func__, ret); + goto end; + } + } + + for_each_rtd_codec_dais(rtd, j, codec_dai) { + + if (!strcmp(codec_dai->component->name_prefix, "Left")) { + ret = snd_soc_dai_set_tdm_slot( + codec_dai, LEFT_SPK_TDM_TX_MASK, + SPK_TDM_RX_MASK, NUM_TDM_SLOTS, + slot_width); + if (ret < 0) { + dev_err(rtd->dev, + "DEV0 TDM slot err:%d\n", ret); + return ret; + } + } + + if (!strcmp(codec_dai->component->name_prefix, "Right")) { + ret = snd_soc_dai_set_tdm_slot( + codec_dai, RIGHT_SPK_TDM_TX_MASK, + SPK_TDM_RX_MASK, NUM_TDM_SLOTS, + slot_width); + if (ret < 0) { + dev_err(rtd->dev, + "DEV1 TDM slot err:%d\n", ret); + return ret; + } + } + } + +end: + return ret; +} + +static int msm8998_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + int ret = 0; + + switch (cpu_dai->id) { + case QUATERNARY_TDM_RX_0: + case QUATERNARY_TDM_TX_0: + ret = msm8998_tdm_snd_hw_params(substream, params); + break; + case SLIMBUS_0_RX...SLIMBUS_6_TX: + ret = msm8998_slim_snd_hw_params(substream, params); + break; + case QUATERNARY_MI2S_RX: + break; + default: + pr_err("%s: invalid dai id 0x%x\n", __func__, cpu_dai->id); + break; + } + return ret; +} + +static int msm8998_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct msm8998_snd_data *pdata = snd_soc_card_get_drvdata(card); + struct snd_jack *jack; + /* + * Codec SLIMBUS configuration + * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX13, RX14 + * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13, TX14, + * TX15, TX16 + */ + unsigned int rx_ch[SLIM_MAX_RX_PORTS] = {144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, + 156, 157 }; + unsigned int tx_ch[SLIM_MAX_TX_PORTS] = {128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143}; + int ret, i; + + if (!pdata->jack_setup) { + ret = snd_soc_card_jack_new(card, "Headset Jack", + SND_JACK_HEADSET | + SND_JACK_HEADPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &pdata->jack); + + if (ret < 0) { + dev_err(card->dev, "Unable to add Headphone Jack\n"); + return ret; + } + + jack = pdata->jack.jack; + + snd_jack_set_key(jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); + snd_jack_set_key(jack, SND_JACK_BTN_2, KEY_VOLUMEUP); + snd_jack_set_key(jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); + pdata->jack_setup = true; + } + + switch (cpu_dai->id) { + case SLIMBUS_0_RX...SLIMBUS_6_TX: + for_each_rtd_codec_dais(rtd, i, codec_dai) { + ret = snd_soc_dai_set_channel_map(codec_dai, + ARRAY_SIZE(tx_ch), tx_ch, + ARRAY_SIZE(rx_ch), rx_ch); + if (ret != 0 && ret != -ENOTSUPP) + return ret; + + snd_soc_dai_set_sysclk(codec_dai, 0, + WCD934X_DEFAULT_MCLK_RATE, + SNDRV_PCM_STREAM_PLAYBACK); + + ret = snd_soc_component_set_jack(codec_dai->component, + &pdata->jack, NULL); + if (ret != 0 && ret != -ENOTSUPP) { + dev_warn(card->dev, "Failed to set jack: %d\n", ret); + return ret; + } + } + break; + default: + break; + } + + return 0; +} + +static int msm8998_snd_startup(struct snd_pcm_substream *substream) +{ + unsigned int codec_dai_fmt = SND_SOC_DAIFMT_CBS_CFS; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_card *card = rtd->card; + struct msm8998_snd_data *data = snd_soc_card_get_drvdata(card); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + int j; + int ret; + + switch (cpu_dai->id) { + case QUATERNARY_TDM_RX_0: + case QUATERNARY_TDM_TX_0: + if (++(data->quat_tdm_clk_count) == 1) { + snd_soc_dai_set_sysclk(cpu_dai, + Q6AFE_LPASS_CLK_ID_QUAD_TDM_IBIT, + TDM_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); + } + + codec_dai_fmt |= SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_DSP_B; + + for_each_rtd_codec_dais(rtd, j, codec_dai) { + + if (!strcmp(codec_dai->component->name_prefix, + "Left")) { + ret = snd_soc_dai_set_fmt( + codec_dai, codec_dai_fmt); + if (ret < 0) { + dev_err(rtd->dev, + "Left TDM fmt err:%d\n", ret); + return ret; + } + } + + if (!strcmp(codec_dai->component->name_prefix, + "Right")) { + ret = snd_soc_dai_set_fmt( + codec_dai, codec_dai_fmt); + if (ret < 0) { + dev_err(rtd->dev, + "Right TDM slot err:%d\n", ret); + return ret; + } + } + } + break; + case SLIMBUS_0_RX...SLIMBUS_6_TX: + break; + + default: + pr_err("%s: invalid dai id 0x%x\n", __func__, cpu_dai->id); + break; + } + return 0; +} + +static void msm8998_snd_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_card *card = rtd->card; + struct msm8998_snd_data *data = snd_soc_card_get_drvdata(card); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + + switch (cpu_dai->id) { + case QUATERNARY_TDM_RX_0: + case QUATERNARY_TDM_TX_0: + if (--(data->quat_tdm_clk_count) == 0) { + snd_soc_dai_set_sysclk(cpu_dai, + Q6AFE_LPASS_CLK_ID_QUAD_TDM_IBIT, + 0, SNDRV_PCM_STREAM_PLAYBACK); + } + break; + case SLIMBUS_0_RX...SLIMBUS_6_TX: + case QUATERNARY_MI2S_RX: + break; + + default: + pr_err("%s: invalid dai id 0x%x\n", __func__, cpu_dai->id); + break; + } +} + +static int msm8998_snd_prepare(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct msm8998_snd_data *data = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id]; + int ret; + + if (!sruntime) + return 0; + + if (data->stream_prepared[cpu_dai->id]) { + sdw_disable_stream(sruntime); + sdw_deprepare_stream(sruntime); + data->stream_prepared[cpu_dai->id] = false; + } + + ret = sdw_prepare_stream(sruntime); + if (ret) + return ret; + + /** + * NOTE: there is a strict hw requirement about the ordering of port + * enables and actual WSA881x PA enable. PA enable should only happen + * after soundwire ports are enabled if not DC on the line is + * accumulated resulting in Click/Pop Noise + * PA enable/mute are handled as part of codec DAPM and digital mute. + */ + + ret = sdw_enable_stream(sruntime); + if (ret) { + sdw_deprepare_stream(sruntime); + return ret; + } + data->stream_prepared[cpu_dai->id] = true; + + return ret; +} + +static int msm8998_snd_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct msm8998_snd_data *data = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id]; + + if (sruntime && data->stream_prepared[cpu_dai->id]) { + sdw_disable_stream(sruntime); + sdw_deprepare_stream(sruntime); + data->stream_prepared[cpu_dai->id] = false; + } + + return 0; +} + +static const struct snd_soc_ops msm8998_be_ops = { + .hw_params = msm8998_snd_hw_params, + .hw_free = msm8998_snd_hw_free, + .prepare = msm8998_snd_prepare, + .startup = msm8998_snd_startup, + .shutdown = msm8998_snd_shutdown, +}; + +static int msm8998_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + rate->min = rate->max = DEFAULT_SAMPLE_RATE_48K; + channels->min = channels->max = 2; + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); + + return 0; +} + +static const struct snd_soc_dapm_widget msm8998_snd_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_SPK("Left Spk", NULL), + SND_SOC_DAPM_SPK("Right Spk", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), +}; + +static void msm8998_add_ops(struct snd_soc_card *card) +{ + struct snd_soc_dai_link *link; + int i; + + for_each_card_prelinks(card, i, link) { + if (link->no_pcm == 1) { + link->ops = &msm8998_be_ops; + link->be_hw_params_fixup = msm8998_be_hw_params_fixup; + } + link->init = msm8998_dai_init; + } +} + +static int msm8998_snd_platform_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card; + struct msm8998_snd_data *data; + struct device *dev = &pdev->dev; + int ret; + + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); + if (!card) + return -ENOMEM; + + /* Allocate the private data */ + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + card->driver_name = DRIVER_NAME; + card->dapm_widgets = msm8998_snd_widgets; + card->num_dapm_widgets = ARRAY_SIZE(msm8998_snd_widgets); + card->dev = dev; + card->owner = THIS_MODULE; + dev_set_drvdata(dev, card); + ret = qcom_snd_parse_of(card); + if (ret) + return ret; + + data->card = card; + snd_soc_card_set_drvdata(card, data); + + msm8998_add_ops(card); + return devm_snd_soc_register_card(dev, card); +} + +static const struct of_device_id msm8998_snd_device_id[] = { + { .compatible = "qcom,msm8998-sndcard" }, + {}, +}; +MODULE_DEVICE_TABLE(of, msm8998_snd_device_id); + +static struct platform_driver msm8998_snd_driver = { + .probe = msm8998_snd_platform_probe, + .driver = { + .name = "msm-snd-msm8998", + .of_match_table = msm8998_snd_device_id, + }, +}; +module_platform_driver(msm8998_snd_driver); + +MODULE_DESCRIPTION("msm8998 ASoC Machine Driver"); +MODULE_LICENSE("GPL v2"); From ad7c17af906b81dfb1c4adeb56388b0f90fa13bb Mon Sep 17 00:00:00 2001 From: Jami Kettunen Date: Sat, 20 Nov 2021 22:48:10 +0200 Subject: [PATCH 0284/1823] ASoC: qcom: lpass-msm8998: Add platform driver for lpass audio Add platform driver for configuring msm8998 lpass core I2S and DMA configuration to support playback & capture to external codecs connected over primary & secondary MI2S interfaces. (driver originally split from 4d7e1ead3b08 ("fastmatrix yaml fix")) --- include/dt-bindings/sound/msm8998-lpass.h | 9 + sound/soc/qcom/Kconfig | 7 + sound/soc/qcom/Makefile | 2 + sound/soc/qcom/lpass-msm8998.c | 330 ++++++++++++++++++++++ 4 files changed, 348 insertions(+) create mode 100644 include/dt-bindings/sound/msm8998-lpass.h create mode 100644 sound/soc/qcom/lpass-msm8998.c diff --git a/include/dt-bindings/sound/msm8998-lpass.h b/include/dt-bindings/sound/msm8998-lpass.h new file mode 100644 index 000000000000..79fc5cb4882b --- /dev/null +++ b/include/dt-bindings/sound/msm8998-lpass.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __DT_MSM8998_LPASS_H +#define __DT_MSM8998_LPASS_H + +#include + +/* NOTE: Use qcom,lpass.h to define any AIF ID's for LPASS */ + +#endif /* __DT_MSM8998_LPASS_H */ diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 8f4d6efa8968..835fc34be698 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -34,6 +34,12 @@ config SND_SOC_LPASS_APQ8016 select SND_SOC_LPASS_CPU select SND_SOC_LPASS_PLATFORM +config SND_SOC_LPASS_MSM8998 + tristate + select SND_SOC_LPASS_CPU + select SND_SOC_LPASS_PLATFORM + select SND_SOC_LPASS_HDMI + config SND_SOC_LPASS_SC7180 tristate select SND_SOC_LPASS_CPU @@ -154,6 +160,7 @@ config SND_SOC_MSM8998 depends on COMMON_CLK select SND_SOC_QDSP6 select SND_SOC_QCOM_COMMON + select SND_SOC_LPASS_MSM8998 help To add support for audio on Qualcomm Technologies Inc. MSM8998 SoC-based systems. diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile index 583a49a3102e..dc2348bee31b 100644 --- a/sound/soc/qcom/Makefile +++ b/sound/soc/qcom/Makefile @@ -6,6 +6,7 @@ snd-soc-lpass-hdmi-objs := lpass-hdmi.o snd-soc-lpass-platform-objs := lpass-platform.o snd-soc-lpass-ipq806x-objs := lpass-ipq806x.o snd-soc-lpass-apq8016-objs := lpass-apq8016.o +snd-soc-lpass-msm8998-objs := lpass-msm8998.o snd-soc-lpass-sc7180-objs := lpass-sc7180.o snd-soc-lpass-sc7280-objs := lpass-sc7280.o @@ -15,6 +16,7 @@ obj-$(CONFIG_SND_SOC_LPASS_HDMI) += snd-soc-lpass-hdmi.o obj-$(CONFIG_SND_SOC_LPASS_PLATFORM) += snd-soc-lpass-platform.o obj-$(CONFIG_SND_SOC_LPASS_IPQ806X) += snd-soc-lpass-ipq806x.o obj-$(CONFIG_SND_SOC_LPASS_APQ8016) += snd-soc-lpass-apq8016.o +obj-$(CONFIG_SND_SOC_LPASS_MSM8998) += snd-soc-lpass-msm8998.o obj-$(CONFIG_SND_SOC_LPASS_SC7180) += snd-soc-lpass-sc7180.o obj-$(CONFIG_SND_SOC_LPASS_SC7280) += snd-soc-lpass-sc7280.o diff --git a/sound/soc/qcom/lpass-msm8998.c b/sound/soc/qcom/lpass-msm8998.c new file mode 100644 index 000000000000..56079396337f --- /dev/null +++ b/sound/soc/qcom/lpass-msm8998.c @@ -0,0 +1,330 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021 AngeloGioacchino Del Regno + * + * + * lpass-msm8998.c -- ALSA SoC platform-machine driver for QTi LPASS + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lpass-lpaif-reg.h" +#include "lpass.h" + +static struct snd_soc_dai_driver msm8998_lpass_cpu_dai_driver[] = { + { + .id = MI2S_PRIMARY, + .name = "Primary MI2S", + .playback = { + .stream_name = "Primary Playback", + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .capture = { + .stream_name = "Primary Capture", + .formats = SNDRV_PCM_FMTBIT_S16 | + SNDRV_PCM_FMTBIT_S32, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .probe = &asoc_qcom_lpass_cpu_dai_probe, + .ops = &asoc_qcom_lpass_cpu_dai_ops, + }, { + .id = MI2S_SECONDARY, + .name = "Secondary MI2S", + .playback = { + .stream_name = "Secondary Playback", + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .probe = &asoc_qcom_lpass_cpu_dai_probe, + .ops = &asoc_qcom_lpass_cpu_dai_ops, + }, { + .id = MI2S_TERTIARY, + .name = "Tertiary MI2S", + .playback = { + .stream_name = "Tertiary Playback", + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .probe = &asoc_qcom_lpass_cpu_dai_probe, + .ops = &asoc_qcom_lpass_cpu_dai_ops, + }, { + .id = MI2S_QUATERNARY, + .name = "Quaternary MI2S", + .playback = { + .stream_name = "Quaternary Playback", + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .probe = &asoc_qcom_lpass_cpu_dai_probe, + .ops = &asoc_qcom_lpass_cpu_dai_ops, + }, { + .id = LPASS_DP_RX, + .name = "Hdmi", + .playback = { + .stream_name = "Hdmi Playback", + .formats = SNDRV_PCM_FMTBIT_S24, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &asoc_qcom_lpass_hdmi_dai_ops, + }, +}; + +static int msm8998_lpass_alloc_dma_channel(struct lpass_data *drvdata, + int direction, unsigned int dai_id) +{ + struct lpass_variant *v = drvdata->variant; + int chan = 0; + + if (dai_id == LPASS_DP_RX) { + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + chan = find_first_zero_bit(&drvdata->hdmi_dma_ch_bit_map, + v->hdmi_rdma_channels); + + if (chan >= v->hdmi_rdma_channels) + return -EBUSY; + } + set_bit(chan, &drvdata->hdmi_dma_ch_bit_map); + } else { + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + chan = find_first_zero_bit(&drvdata->dma_ch_bit_map, + v->rdma_channels); + + if (chan >= v->rdma_channels) + return -EBUSY; + } else { + chan = find_next_zero_bit(&drvdata->dma_ch_bit_map, + v->wrdma_channel_start + + v->wrdma_channels, + v->wrdma_channel_start); + + if (chan >= v->wrdma_channel_start + v->wrdma_channels) + return -EBUSY; + } + + set_bit(chan, &drvdata->dma_ch_bit_map); + } + return chan; +} + +static int msm8998_lpass_free_dma_channel(struct lpass_data *drvdata, int chan, unsigned int dai_id) +{ + if (dai_id == LPASS_DP_RX) + clear_bit(chan, &drvdata->hdmi_dma_ch_bit_map); + else + clear_bit(chan, &drvdata->dma_ch_bit_map); + + return 0; +} + +static int msm8998_lpass_init(struct platform_device *pdev) +{ + struct lpass_data *drvdata = platform_get_drvdata(pdev); + struct lpass_variant *variant = drvdata->variant; + struct device *dev = &pdev->dev; + int ret, i; + + drvdata->clks = devm_kcalloc(dev, variant->num_clks, + sizeof(*drvdata->clks), GFP_KERNEL); + drvdata->num_clks = variant->num_clks; + + for (i = 0; i < drvdata->num_clks; i++) + drvdata->clks[i].id = variant->clk_name[i]; + + ret = devm_clk_bulk_get(dev, drvdata->num_clks, drvdata->clks); + if (ret) { + dev_err(dev, "Failed to get clocks %d\n", ret); + return ret; + } + + ret = clk_bulk_prepare_enable(drvdata->num_clks, drvdata->clks); + if (ret) { + dev_err(dev, "msm8998 clk_enable failed\n"); + return ret; + } + + return 0; +} + +static int msm8998_lpass_exit(struct platform_device *pdev) +{ + struct lpass_data *drvdata = platform_get_drvdata(pdev); + + clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks); + + return 0; +} + +static struct lpass_variant msm8998_data = { + .i2sctrl_reg_base = 0x1000, + .i2sctrl_reg_stride = 0x1000, + .i2s_ports = 3, + .irq_reg_base = 0xa000, + .irq_reg_stride = 0x1000, + .irq_ports = 3, + .rdma_reg_base = 0xD000, + .rdma_reg_stride = 0x1000, + .rdma_channels = 5, + .hdmi_rdma_reg_base = 0x3000, + .hdmi_rdma_reg_stride = 0x1000, + .hdmi_rdma_channels = 4, + .dmactl_audif_start = 1, /* what's that?? */ + .wrdma_reg_base = 0x13000, + .wrdma_reg_stride = 0x1000, + .wrdma_channel_start = 5, /* ?? what's that ?? */ + .wrdma_channels = 4, + + .loopback = REG_FIELD_ID(0x1000, 15, 15, 4, 0x1000), + .spken = REG_FIELD_ID(0x1000, 14, 14, 4, 0x1000), + .spkmode = REG_FIELD_ID(0x1000, 10, 13, 4, 0x1000), + .spkmono = REG_FIELD_ID(0x1000, 9, 9, 4, 0x1000), + .micen = REG_FIELD_ID(0x1000, 8, 8, 4, 0x1000), + .micmode = REG_FIELD_ID(0x1000, 4, 7, 4, 0x1000), + .micmono = REG_FIELD_ID(0x1000, 3, 3, 4, 0x1000), + .wssrc = REG_FIELD_ID(0x1000, 2, 2, 4, 0x1000), + .bitwidth = REG_FIELD_ID(0x1000, 0, 1, 4, 0x1000), + + .rdma_dyncclk = REG_FIELD_ID(0xD000, 14, 14, 4, 0x1000), + .rdma_bursten = REG_FIELD_ID(0xD000, 13, 13, 4, 0x1000), + .rdma_wpscnt = REG_FIELD_ID(0xD000, 10, 12, 4, 0x1000), + .rdma_intf = REG_FIELD_ID(0xD000, 6, 9, 4, 0x1000), + .rdma_fifowm = REG_FIELD_ID(0xD000, 1, 5, 4, 0x1000), + .rdma_enable = REG_FIELD_ID(0xD000, 0, 0, 4, 0x1000), + + .wrdma_dyncclk = REG_FIELD_ID(0x13000, 12, 12, 3, 0x1000), + .wrdma_bursten = REG_FIELD_ID(0x13000, 11, 11, 3, 0x1000), + .wrdma_wpscnt = REG_FIELD_ID(0x13000, 8, 10, 3, 0x1000), + .wrdma_intf = REG_FIELD_ID(0x13000, 4, 7, 3, 0x1000), + .wrdma_fifowm = REG_FIELD_ID(0x13000, 1, 3, 3, 0x1000), + .wrdma_enable = REG_FIELD_ID(0x13000, 0, 0, 3, 0x1000), + + .hdmi_tx_ctl_addr = 0x1000, + .hdmi_legacy_addr = 0x1008, + .hdmi_vbit_addr = 0xc0, + .hdmi_ch_lsb_addr = 0x48, + .hdmi_ch_msb_addr = 0x4c, + .ch_stride = 0x8, + .hdmi_parity_addr = 0x34, + .hdmi_dmactl_addr = 0x38, + .hdmi_dma_stride = 0x4, + .hdmi_DP_addr = 0xc8, + .hdmi_sstream_addr = 0x1c, + .hdmi_irq_reg_base = 0x63000, + .hdmi_irq_ports = 1, + + .hdmi_rdma_dyncclk = REG_FIELD_ID(0x3000, 14, 14, 3, 0x1000), + .hdmi_rdma_bursten = REG_FIELD_ID(0x3000, 13, 13, 3, 0x1000), + .hdmi_rdma_burst8 = REG_FIELD_ID(0x3000, 15, 15, 3, 0x1000), + .hdmi_rdma_burst16 = REG_FIELD_ID(0x3000, 16, 16, 3, 0x1000), + .hdmi_rdma_dynburst = REG_FIELD_ID(0x3000, 18, 18, 3, 0x1000), + .hdmi_rdma_wpscnt = REG_FIELD_ID(0x3000, 10, 12, 3, 0x1000), + .hdmi_rdma_fifowm = REG_FIELD_ID(0x3000, 1, 5, 3, 0x1000), + .hdmi_rdma_enable = REG_FIELD_ID(0x3000, 0, 0, 3, 0x1000), + + .sstream_en = REG_FIELD(0x1c, 0, 0), + .dma_sel = REG_FIELD(0x1c, 1, 2), + .auto_bbit_en = REG_FIELD(0x1c, 3, 3), + .layout = REG_FIELD(0x1c, 4, 4), + .layout_sp = REG_FIELD(0x1c, 5, 8), + .set_sp_on_en = REG_FIELD(0x1c, 10, 10), + .dp_audio = REG_FIELD(0x1c, 11, 11), + .dp_staffing_en = REG_FIELD(0x1c, 12, 12), + .dp_sp_b_hw_en = REG_FIELD(0x1c, 13, 13), + + .mute = REG_FIELD(0xc8, 0, 0), + .as_sdp_cc = REG_FIELD(0xc8, 1, 3), + .as_sdp_ct = REG_FIELD(0xc8, 4, 7), + .aif_db4 = REG_FIELD(0xc8, 8, 15), + + .soft_reset = REG_FIELD(0x1000, 31, 31), + .force_reset = REG_FIELD(0x1000, 30, 30), + + .use_hw_chs = REG_FIELD(0x38, 0, 0), + .use_hw_usr = REG_FIELD(0x38, 1, 1), + .hw_chs_sel = REG_FIELD(0x38, 2, 4), + .hw_usr_sel = REG_FIELD(0x38, 5, 6), + + .replace_vbit = REG_FIELD(0xc0, 0, 0), + .vbit_stream = REG_FIELD(0xc0, 1, 1), + + .legacy_en = REG_FIELD(0x1008, 0, 0), + .calc_en = REG_FIELD(0x34, 0, 0), + .lsb_bits = REG_FIELD(0x48, 0, 31), + .msb_bits = REG_FIELD(0x4c, 0, 16), + + + .clk_name = (const char*[]) { + "pcnoc-sway-clk", + "audio-core", + "pcnoc-mport-clk", + }, + .num_clks = 3, + .dai_driver = msm8998_lpass_cpu_dai_driver, + .num_dai = ARRAY_SIZE(msm8998_lpass_cpu_dai_driver), + .dai_osr_clk_names = (const char *[]) { + "mclk0", + "null", + }, + .dai_bit_clk_names = (const char *[]) { + "mi2s-bit-clk0", + "mi2s-bit-clk1", + }, + .init = msm8998_lpass_init, + .exit = msm8998_lpass_exit, + .alloc_dma_channel = msm8998_lpass_alloc_dma_channel, + .free_dma_channel = msm8998_lpass_free_dma_channel, +}; + +static const struct of_device_id msm8998_lpass_cpu_device_id[] __maybe_unused = { + {.compatible = "qcom,msm8998-lpass-cpu", .data = &msm8998_data}, + {} +}; +MODULE_DEVICE_TABLE(of, msm8998_lpass_cpu_device_id); + +static struct platform_driver msm8998_lpass_cpu_platform_driver = { + .driver = { + .name = "msm8998-lpass-cpu", + .of_match_table = of_match_ptr(msm8998_lpass_cpu_device_id), + }, + .probe = asoc_qcom_lpass_cpu_platform_probe, + .remove = asoc_qcom_lpass_cpu_platform_remove, + .shutdown = asoc_qcom_lpass_cpu_platform_shutdown, +}; + +module_platform_driver(msm8998_lpass_cpu_platform_driver); + +MODULE_DESCRIPTION("msm8998 LPASS CPU DRIVER"); +MODULE_LICENSE("GPL v2"); From a92cc30ae839b4bd639520a20a986c5370c95534 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 9 Aug 2021 19:47:04 +0200 Subject: [PATCH 0285/1823] arm64: dts: qcom: msm8998: Add disabled slimbus support Add generic slimbus configuration to MSM8998 and keep it disabled. It is expected that this will be enabled in board-specific device trees when supported, along with the right audio codec. Signed-off-by: AngeloGioacchino Del Regno --- arch/arm64/boot/dts/qcom/msm8998.dtsi | 34 +++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi index 48fe76bc8c45..932cf884aa0d 100644 --- a/arch/arm64/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi @@ -3444,6 +3444,40 @@ }; }; + slimbam: dma-controller@17184000 { + compatible = "qcom,bam-v1.7.0"; + qcom,controlled-remotely; + reg = <0x17184000 0x32000>; + num-channels = <31>; + interrupts = ; + #dma-cells = <1>; + qcom,ee = <1>; + qcom,num-ees = <2>; + }; + + slim: slim@171c0000 { + compatible = "qcom,slim-ngd-v2.1.0"; + reg = <0x171c0000 0x2c000>; + interrupts = ; + + qcom,apps-ch-pipes = <0x1f80>; + qcom,ea-pc = <0x210>; + status = "okay"; + dmas = <&slimbam 3>, <&slimbam 4>, + <&slimbam 5>, <&slimbam 6>; + dma-names = "rx", "tx", "tx2", "rx2"; + + #address-cells = <1>; + #size-cells = <0>; + + ngd@1 { + reg = <1>; + #address-cells = <2>; + #size-cells = <0>; + }; + }; + + remoteproc_adsp: remoteproc@17300000 { compatible = "qcom,msm8998-adsp-pas"; reg = <0x17300000 0x4040>; From 83b748720a993787e8846e811469670f15d3ee59 Mon Sep 17 00:00:00 2001 From: Jami Kettunen Date: Fri, 28 Jan 2022 17:46:39 +0200 Subject: [PATCH 0286/1823] mfd: wcd9335: Add support to wcd9335 codec Qualcomm WCD9335 Codec is a standalone Hi-Fi audio codec IC. This codec has integrated SoundWire controller, pin controller and interrupt controller. Originally from: d95b982a --- drivers/mfd/Kconfig | 12 ++ drivers/mfd/Makefile | 1 + drivers/mfd/wcd9335.c | 300 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 313 insertions(+) create mode 100644 drivers/mfd/wcd9335.c diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 8b93856de432..2afb69b45193 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -2099,6 +2099,18 @@ config MFD_WCD934X This driver provides common support WCD934x audio codec and its associated Pin Controller, Soundwire Controller and Audio codec. +config MFD_WCD9335 + tristate "Support for WCD9335 Codec" + depends on SLIMBUS + select REGMAP + select REGMAP_SLIMBUS + select REGMAP_IRQ + select MFD_CORE + help + Support for the Qualcomm WCD9335 Codec. + This driver provides common support WCD9335 audio codec and its + associated Pin Controller, Soundwire Controller and Audio codec. + config MFD_ATC260X tristate select MFD_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 7ed3ef4a698c..9107e4b203f3 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -61,6 +61,7 @@ ifeq ($(CONFIG_MFD_CS47L24),y) arizona-objs += cs47l24-tables.o endif obj-$(CONFIG_MFD_WCD934X) += wcd934x.o +obj-$(CONFIG_MFD_WCD9335) += wcd9335.o obj-$(CONFIG_MFD_WM8400) += wm8400-core.o wm831x-objs := wm831x-core.o wm831x-irq.o wm831x-otp.o wm831x-objs += wm831x-auxadc.o diff --git a/drivers/mfd/wcd9335.c b/drivers/mfd/wcd9335.c new file mode 100644 index 000000000000..fa8985f97359 --- /dev/null +++ b/drivers/mfd/wcd9335.c @@ -0,0 +1,300 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019, Linaro Limited + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WCD9335_REGMAP_IRQ_REG(_irq, _off, _mask) \ + [_irq] = { \ + .reg_offset = (_off), \ + .mask = (_mask), \ + .type = { \ + .type_reg_offset = (_off), \ + .types_supported = IRQ_TYPE_EDGE_BOTH, \ + .type_reg_mask = (_mask), \ + .type_level_low_val = (_mask), \ + .type_level_high_val = (_mask), \ + .type_falling_val = 0, \ + .type_rising_val = 0, \ + }, \ + } + +static const struct mfd_cell wcd9335_devices[] = { + { + .name = "wcd9335-codec", + }, { + .name = "wcd9335-gpio", + .of_compatible = "qcom,wcd9340-gpio", + }, { + .name = "wcd9335-soundwire", + .of_compatible = "qcom,soundwire-v1.3.0", + }, +}; + +static const struct regmap_irq wcd9335_irqs[] = { + WCD9335_REGMAP_IRQ_REG(WCD9335_IRQ_SLIMBUS, 0, BIT(0)), + WCD9335_REGMAP_IRQ_REG(WCD9335_IRQ_HPH_PA_OCPL_FAULT, 0, BIT(2)), + WCD9335_REGMAP_IRQ_REG(WCD9335_IRQ_HPH_PA_OCPR_FAULT, 0, BIT(3)), + WCD9335_REGMAP_IRQ_REG(WCD9335_IRQ_MBHC_SW_DET, 1, BIT(0)), + WCD9335_REGMAP_IRQ_REG(WCD9335_IRQ_MBHC_ELECT_INS_REM_DET, 1, BIT(1)), + WCD9335_REGMAP_IRQ_REG(WCD9335_IRQ_MBHC_BUTTON_PRESS_DET, 1, BIT(2)), + WCD9335_REGMAP_IRQ_REG(WCD9335_IRQ_MBHC_BUTTON_RELEASE_DET, 1, BIT(3)), + WCD9335_REGMAP_IRQ_REG(WCD9335_IRQ_MBHC_ELECT_INS_REM_LEG_DET, 1, BIT(4)), + WCD9335_REGMAP_IRQ_REG(WCD9335_IRQ_SOUNDWIRE, 2, BIT(4)), +}; + +static const struct regmap_irq_chip wcd9335_regmap_irq_chip = { + .name = "wcd9335_irq", + .status_base = WCD9335_INTR_PIN1_STATUS0, + .mask_base = WCD9335_INTR_PIN1_MASK0, + .ack_base = WCD9335_INTR_PIN1_CLEAR0, + .type_base = WCD9335_INTR_LEVEL0, + .num_type_reg = 4, + .type_in_mask = false, + .num_regs = 4, + .irqs = wcd9335_irqs, + .num_irqs = ARRAY_SIZE(wcd9335_irqs), +}; + +static bool wcd9335_is_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WCD9335_INTR_PIN1_STATUS0...WCD9335_INTR_PIN2_CLEAR3: + case WCD9335_SWR_AHB_BRIDGE_RD_DATA_0: + case WCD9335_SWR_AHB_BRIDGE_RD_DATA_1: + case WCD9335_SWR_AHB_BRIDGE_RD_DATA_2: + case WCD9335_SWR_AHB_BRIDGE_RD_DATA_3: + case WCD9335_SWR_AHB_BRIDGE_ACCESS_STATUS: + case WCD9335_ANA_MBHC_RESULT_3: + case WCD9335_ANA_MBHC_RESULT_2: + case WCD9335_ANA_MBHC_RESULT_1: + case WCD9335_ANA_MBHC_MECH: + case WCD9335_ANA_MBHC_ELECT: + case WCD9335_ANA_MBHC_ZDET: + case WCD9335_ANA_MICB2: + case WCD9335_ANA_RCO: + case WCD9335_ANA_BIAS: + return true; + default: + return false; + } +}; + +static const struct regmap_range_cfg wcd9335_ranges[] = { + { .name = "WCD9335", + .range_min = 0x0, + .range_max = WCD9335_MAX_REGISTER, + .selector_reg = WCD9335_SEL_REGISTER, + .selector_mask = WCD9335_SEL_MASK, + .selector_shift = WCD9335_SEL_SHIFT, + .window_start = WCD9335_WINDOW_START, + .window_len = WCD9335_WINDOW_LENGTH, + }, +}; + +static struct regmap_config wcd9335_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .max_register = 0xffff, + .can_multi_write = true, + .ranges = wcd9335_ranges, + .num_ranges = ARRAY_SIZE(wcd9335_ranges), + .volatile_reg = wcd9335_is_volatile_register, +}; + +static int wcd9335_bring_up(struct wcd9335_ddata *ddata) +{ + struct regmap *regmap = ddata->regmap; + u16 id_minor, id_major; + int ret; + + ret = regmap_bulk_read(regmap, WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0, + (u8 *)&id_minor, sizeof(u16)); + if (ret) + return ret; + + ret = regmap_bulk_read(regmap, WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE2, + (u8 *)&id_major, sizeof(u16)); + if (ret) + return ret; + + dev_info(ddata->dev, "WCD934x chip id major 0x%x, minor 0x%x\n", + id_major, id_minor); + + regmap_write(regmap, WCD9335_CODEC_RPM_RST_CTL, 0x01); + regmap_write(regmap, WCD9335_SIDO_NEW_VOUT_A_STARTUP, 0x19); + regmap_write(regmap, WCD9335_SIDO_NEW_VOUT_D_STARTUP, 0x15); + /* Add 1msec delay for VOUT to settle */ + usleep_range(1000, 1100); + regmap_write(regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x5); + regmap_write(regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x7); + regmap_write(regmap, WCD9335_CODEC_RPM_RST_CTL, 0x3); + regmap_write(regmap, WCD9335_CODEC_RPM_RST_CTL, 0x7); + regmap_write(regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x3); + + return 0; +} + +static int wcd9335_slim_status_up(struct slim_device *sdev) +{ + struct device *dev = &sdev->dev; + struct wcd9335_ddata *ddata; + int ret; + + ddata = dev_get_drvdata(dev); + + ddata->regmap = regmap_init_slimbus(sdev, &wcd9335_regmap_config); + if (IS_ERR(ddata->regmap)) { + dev_err(dev, "Error allocating slim regmap\n"); + return PTR_ERR(ddata->regmap); + } + + ret = wcd9335_bring_up(ddata); + if (ret) { + dev_err(dev, "Failed to bring up WCD9335: err = %d\n", ret); + return ret; + } + + ret = devm_regmap_add_irq_chip(dev, ddata->regmap, ddata->irq, + IRQF_TRIGGER_HIGH, 0, + &wcd9335_regmap_irq_chip, + &ddata->irq_data); + if (ret) { + dev_err(dev, "Failed to add IRQ chip: err = %d\n", ret); + return ret; + } + + ret = mfd_add_devices(dev, PLATFORM_DEVID_AUTO, wcd9335_devices, + ARRAY_SIZE(wcd9335_devices), NULL, 0, NULL); + if (ret) { + dev_err(dev, "Failed to add child devices: err = %d\n", + ret); + return ret; + } + + return ret; +} + +static int wcd9335_slim_status(struct slim_device *sdev, + enum slim_device_status status) +{ + switch (status) { + case SLIM_DEVICE_STATUS_UP: + return wcd9335_slim_status_up(sdev); + case SLIM_DEVICE_STATUS_DOWN: + mfd_remove_devices(&sdev->dev); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wcd9335_slim_probe(struct slim_device *sdev) +{ + struct device *dev = &sdev->dev; + struct device_node *np = dev->of_node; + struct wcd9335_ddata *ddata; + int reset_gpio, ret; + + ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + ddata->irq = of_irq_get(np, 0); + if (ddata->irq < 0) + return dev_err_probe(ddata->dev, ddata->irq, + "Failed to get IRQ\n"); + + reset_gpio = of_get_named_gpio(np, "reset-gpios", 0); + if (reset_gpio < 0) { + dev_err(dev, "Failed to get reset gpio: err = %d\n", + reset_gpio); + return reset_gpio; + } + + ddata->extclk = devm_clk_get(dev, "extclk"); + if (IS_ERR(ddata->extclk)) { + dev_err(dev, "Failed to get extclk"); + return PTR_ERR(ddata->extclk); + } + + ddata->supplies[0].supply = "vdd-buck"; + ddata->supplies[1].supply = "vdd-buck-sido"; + ddata->supplies[2].supply = "vdd-tx"; + ddata->supplies[3].supply = "vdd-rx"; + ddata->supplies[4].supply = "vdd-io"; + + ret = regulator_bulk_get(dev, WCD9335_MAX_SUPPLY, ddata->supplies); + if (ret) { + dev_err(dev, "Failed to get supplies: err = %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(WCD9335_MAX_SUPPLY, ddata->supplies); + if (ret) { + dev_err(dev, "Failed to enable supplies: err = %d\n", ret); + return ret; + } + + /* + * For WCD9335, it takes about 600us for the Vout_A and + * Vout_D to be ready after BUCK_SIDO is powered up. + * SYS_RST_N shouldn't be pulled high during this time + */ + usleep_range(600, 650); + gpio_direction_output(reset_gpio, 0); + msleep(20); + gpio_set_value(reset_gpio, 1); + msleep(20); + + ddata->dev = dev; + dev_set_drvdata(dev, ddata); + + return 0; +} + +static void wcd9335_slim_remove(struct slim_device *sdev) +{ + struct wcd9335_ddata *ddata = dev_get_drvdata(&sdev->dev); + + regulator_bulk_disable(WCD9335_MAX_SUPPLY, ddata->supplies); + mfd_remove_devices(&sdev->dev); +} + +static const struct slim_device_id wcd9335_slim_id[] = { + { SLIM_MANF_ID_QCOM, SLIM_PROD_CODE_WCD9340, + SLIM_DEV_IDX_WCD9340, SLIM_DEV_INSTANCE_ID_WCD9340 }, + {} +}; + +static struct slim_driver wcd9335_slim_driver = { + .driver = { + .name = "wcd9335-slim", + }, + .probe = wcd9335_slim_probe, + .remove = wcd9335_slim_remove, + .device_status = wcd9335_slim_status, + .id_table = wcd9335_slim_id, +}; + +module_slim_driver(wcd9335_slim_driver); +MODULE_DESCRIPTION("WCD9335 slim driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("slim:217:250:*"); +MODULE_AUTHOR("Srinivas Kandagatla "); From 88f9d846d6dc5442cb32d0d56d5db5bed21d293b Mon Sep 17 00:00:00 2001 From: Jami Kettunen Date: Fri, 28 Jan 2022 15:34:27 +0200 Subject: [PATCH 0287/1823] [DROPME?] fixup! arm64: dts: qcom: msm8998: Configure Adreno GPU and related IOMMU --- arch/arm64/boot/dts/qcom/msm8998.dtsi | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi index 932cf884aa0d..ac168a0e328f 100644 --- a/arch/arm64/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi @@ -2131,7 +2131,6 @@ * SoC VDDMX RPM Power Domain in the Adreno driver. */ power-domains = <&gpucc GPU_GX_GDSC>; - status = "disabled"; }; gpucc: clock-controller@5065000 { From df09ff004c8249530882b1e4c1c0340628c5adf9 Mon Sep 17 00:00:00 2001 From: Jami Kettunen Date: Fri, 28 Jan 2022 15:50:24 +0200 Subject: [PATCH 0288/1823] arm64: dts: qcom: msm8998: More audio related node changes Originally from: d95b982a --- arch/arm64/boot/dts/qcom/msm8998.dtsi | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi index ac168a0e328f..7e565517be10 100644 --- a/arch/arm64/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi @@ -874,6 +874,9 @@ }; }; + sound: sound { + }; + thermal-zones { cpu0-thermal { polling-delay-passive = <250>; @@ -3452,6 +3455,7 @@ #dma-cells = <1>; qcom,ee = <1>; qcom,num-ees = <2>; + status = "disabled"; }; slim: slim@171c0000 { @@ -3461,22 +3465,22 @@ qcom,apps-ch-pipes = <0x1f80>; qcom,ea-pc = <0x210>; - status = "okay"; + dmas = <&slimbam 3>, <&slimbam 4>, <&slimbam 5>, <&slimbam 6>; dma-names = "rx", "tx", "tx2", "rx2"; #address-cells = <1>; #size-cells = <0>; + status = "disabled"; - ngd@1 { + slim_ngd: ngd@1 { reg = <1>; - #address-cells = <2>; - #size-cells = <0>; + #address-cells = <1>; + #size-cells = <1>; }; }; - remoteproc_adsp: remoteproc@17300000 { compatible = "qcom,msm8998-adsp-pas"; reg = <0x17300000 0x4040>; @@ -3571,7 +3575,7 @@ cb@2 { compatible = "qcom,fastrpc-compute-cb"; - dma-ranges = <0 0x60000000 0 0x18000000>; + //dma-ranges = <0 0x60000000 0 0x18000000>; reg = <2>; iommus = <&lpass_q6_smmu 2>; }; From 6944fd8b9336875fa9694880763f24dfdbd0c073 Mon Sep 17 00:00:00 2001 From: Jami Kettunen Date: Fri, 28 Jan 2022 15:48:02 +0200 Subject: [PATCH 0289/1823] arm64: dts: qcom: Enable audio on MSM8998 Sony Yoshino platform Originally from: d95b982a (JAMI: fixup for 6.0-rc2) --- .../msm8998-sony-xperia-yoshino-lilac.dts | 5 + .../msm8998-sony-xperia-yoshino-maple.dts | 14 +- .../msm8998-sony-xperia-yoshino-poplar.dts | 13 +- .../dts/qcom/msm8998-sony-xperia-yoshino.dtsi | 231 ++++++++++++++++++ 4 files changed, 248 insertions(+), 15 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-lilac.dts b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-lilac.dts index fcaefc1b1e2f..db254df10958 100644 --- a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-lilac.dts +++ b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-lilac.dts @@ -25,6 +25,11 @@ qcom,soft-start-us = <800>; }; +&wcd9335 { + pinctrl-0 = <&cdc_reset_n &wcd_int_n>; + pinctrl-names = "default"; +}; + &vreg_l22a_2p85 { regulator-min-microvolt = <2800000>; regulator-max-microvolt = <2800000>; diff --git a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-maple.dts b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-maple.dts index 1db80a529527..3e0857b6a270 100644 --- a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-maple.dts +++ b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-maple.dts @@ -76,12 +76,12 @@ &pm8005_gpio { ear_en_default: ear-en-active { pins = "gpio1"; - function = PMIC_GPIO_FUNC_NORMAL; - output-low; - drive-push-pull; + function = "normal"; bias-disable; - qcom,drive-strength = ; - power-source = <1>; + drive-push-pull; + output-low; + power-source = <1>; /* 1.8V */ + qcom,drive-strength = <1>; }; }; @@ -111,3 +111,7 @@ qcom,enabled-strings = <0 1 2 3>; }; +&wcd9335 { + pinctrl-0 = <&cdc_reset_n &wcd_int_n &ear_en_default>; + pinctrl-names = "default"; +}; diff --git a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-poplar.dts b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-poplar.dts index 055018b4eacc..86df5e87516a 100644 --- a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-poplar.dts +++ b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-poplar.dts @@ -25,16 +25,9 @@ qcom,soft-start-us = <800>; }; -&pm8005_gpio { - ear_en_default: ear-en-active { - pins = "gpio1"; - function = PMIC_GPIO_FUNC_NORMAL; - output-low; - drive-push-pull; - bias-disable; - qcom,drive-strength = ; - power-source = <1>; - }; +&wcd9335 { + pinctrl-0 = <&cdc_reset_n &wcd_int_n>; + pinctrl-names = "default"; }; &vreg_l18a_2p85 { diff --git a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi index 6043b720106c..3e152318e012 100644 --- a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include "msm8998.dtsi" #include "pm8005.dtsi" #include "pm8998.dtsi" @@ -507,6 +509,27 @@ qcom,enabled-strings = <0 1>; }; +&q6asmdai { + dai@0 { + reg = <0>; + }; + + dai@1 { + reg = <1>; + }; + + dai@2 { + reg = <2>; + }; +/* + dai@3 { + reg = <3>; + direction = <2>; + is-compress-dai; + }; +*/ +}; + &qusb2phy { status = "okay"; @@ -516,6 +539,7 @@ &remoteproc_adsp { firmware-name = "adsp.mdt"; + status = "okay"; }; &remoteproc_mss { @@ -714,6 +738,181 @@ pinctrl-1 = <&sdc2_off &sdc2_cd>; }; +/* Downstream example +&snd_9335 { + qcom,msm-mbhc-hphl-swh = <1>; + /delete-property/ qcom,hph-en1-gpio; + /delete-property/ qcom,hph-en0-gpio; + /delete-property/ qcom,us-euro-gpios; + qcom,ear-en-gpios = <&pm8005_gpios 1 0>; + qcom,audio-routing = + "AIF4 VI", "MCLK", + "RX_BIAS", "MCLK", + "MADINPUT", "MCLK", + "AMIC2", "MIC BIAS2", + "MIC BIAS2", "Headset Mic", + "MIC BIAS2", "ANCRight Headset Mic", + "AMIC3", "MIC BIAS3", + "MIC BIAS3", "ANCLeft Headset Mic", + "DMIC0", "MIC BIAS1", + "MIC BIAS1", "Digital Mic0", + "DMIC3", "MIC BIAS4", + "MIC BIAS4", "Digital Mic3", + "SpkrLeft IN", "SPK1 OUT", + "SpkrRight IN", "SPK2 OUT"; +}; +*/ + +&slimbam { + status = "okay"; +}; + +&slim { + status = "okay"; +}; + +&slim_ngd { + tasha_ifd: tas-ifd { + compatible = "slim217,1a0"; + reg = <0 0>; + }; + + wcd9335: codec@1{ + compatible = "slim217,1a0"; + reg = <1 0>; + + clock-names = "mclk", "slimbus"; + clocks = <&div1_mclk>, + <&rpmcc RPM_SMD_LN_BB_CLK1>; + #clock-cells = <0>; + + vdd-buck-supply = <&vreg_s4a_1p8>; + vdd-buck-sido-supply = <&vreg_s4a_1p8>; + vdd-tx-supply = <&vreg_s4a_1p8>; + vdd-rx-supply = <&vreg_s4a_1p8>; + vdd-io-supply = <&vreg_s4a_1p8>; + + interrupt-parent = <&tlmm>; + interrupts = <54 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "intr1"; + interrupt-controller; + #interrupt-cells = <1>; + reset-gpios = <&tlmm 64 0>; + + slim-ifc-dev = <&tasha_ifd>; + + #address-cells = <1>; + #size-cells = <1>; + #sound-dai-cells = <1>; + + swm: swm@c85 { + compatible = "qcom,soundwire-v1.3.0"; + reg = <0xc85 0x40>; + interrupts-extended = <&wcd9335 13>; + + qcom,dout-ports = <6>; + qcom,din-ports = <2>; + qcom,ports-sinterval-low =/bits/ 8 <0x07 0x1F 0x3F 0x7 0x1F 0x3F 0x0F 0x0F>; + qcom,ports-offset1 = /bits/ 8 <0x01 0x02 0x0C 0x6 0x12 0x0D 0x07 0x0A >; + qcom,ports-offset2 = /bits/ 8 <0x00 0x00 0x1F 0x00 0x00 0x1F 0x00 0x00>; + /*downstream is <0xFF 0x00 0x1F 0xFF 0x00 0x1F 0x00 0x00>;*/ + qcom,ports-block-pack-mode = /bits/ 8 <0xFF 0xFF 0x01 0xFF 0xFF 0x01 0xFF 0xFF>; + clocks = <&xo>; + clock-names = "iface"; + #address-cells = <2>; + #size-cells = <0>; + #sound-dai-cells = <1>; + + left_spkr: wsa8810-left { + compatible = "sdw10217201000"; + reg = <0 1>; + powerdown-gpios = <&tlmm 65 GPIO_ACTIVE_HIGH>; + pinctrl-0 = <&wsa_leftspk_pwr_n>; + pinctrl-names = "default"; + #thermal-sensor-cells = <0>; + sound-name-prefix = "SpkrRight"; + #sound-dai-cells = <0>; + }; + + right_spkr: wsa8810-right { + compatible = "sdw10217201000"; + powerdown-gpios = <&tlmm 66 GPIO_ACTIVE_HIGH>; + reg = <0 2>; + pinctrl-0 = <&wsa_rightspk_pwr_n>; + pinctrl-names = "default"; + #thermal-sensor-cells = <0>; + sound-name-prefix = "SpkrLeft"; + #sound-dai-cells = <0>; + }; + }; + }; +}; + +&sound { + compatible = "qcom,msm8998-sndcard"; + model = "Sony-Xperia-Yoshino"; + + audio-routing = "RX_BIAS", "MCLK", + "AMIC2", "MIC BIAS2", + "AMIC3", "MIC BIAS3", + "DMIC0", "MIC BIAS1", + "DMIC4", "MIC BIAS4", + "SpkrLeft IN", "SPK1 OUT", + "SpkrRight IN", "SPK2 OUT", + "MM_DL1", "MultiMedia1 Playback"; + + mm1-dai-link { + link-name = "MultiMedia1"; + cpu { + sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA1>; + }; + }; + + mm2-dai-link { + link-name = "MultiMedia2"; + cpu { + sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA2>; + }; + }; + + mm3-dai-link { + link-name = "MultiMedia3"; + cpu { + sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA3>; + }; + }; + + slim-dai-link { + link-name = "SLIM Playback"; + cpu { + sound-dai = <&q6afedai SLIMBUS_0_RX>; + }; + + platform { + sound-dai = <&q6routing>; + }; + + codec { + sound-dai = <&left_spkr>, <&right_spkr>, <&swm 0>, <&wcd9335 0>; + }; + }; + + slimcap-dai-link { + link-name = "SLIM Capture"; + cpu { + sound-dai = <&q6afedai SLIMBUS_0_TX>; + }; + + platform { + sound-dai = <&q6routing>; + }; + + codec { + sound-dai = <&wcd9335 1>; + }; + }; +}; + &tlmm { gpio-reserved-ranges = <0 4>, <81 4>; @@ -811,6 +1010,38 @@ drive-strength = <2>; }; + wcd_int_n: wcd-int-n { + pins = "gpio54"; + function = "gpio"; + bias-pull-down; + drive-strength = <2>; + input-enable; + }; + + cdc_reset_n: cdc-reset-n { + pins = "gpio64"; + function = "gpio"; + bias-pull-down; + drive-strength = <16>; + output-high; + }; + + wsa_leftspk_pwr_n: wsa-leftspk-pwr-n { + pins = "gpio65"; + function = "gpio"; + bias-disable; + drive-strength = <2>; + output-low; + }; + + wsa_rightspk_pwr_n: wsa-rightspk-pwr-n { + pins = "gpio66"; + function = "gpio"; + bias-disable; + drive-strength = <2>; + output-low; + }; + ts_reset_n: ts-reset-n { pins = "gpio89"; function = "gpio"; From c97a7876232636ede430299ba4dfed31739d3fde Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 31 Aug 2018 15:46:15 +0100 Subject: [PATCH 0290/1823] ASoC: wcd9335: add mbhc support Signed-off-by: Srinivas Kandagatla --- sound/soc/codecs/wcd9335.c | 296 ++++++++++++++++++++++++++++++++++++- 1 file changed, 294 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index d2548fdf9ae5..f8e3873008c1 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -88,12 +89,18 @@ #define WCD9335_DEC_PWR_LVL_HP 0x04 #define WCD9335_DEC_PWR_LVL_DF 0x00 +#define WCD9335_MBHC_MAX_BUTTONS (8) + #define WCD9335_SLIM_RX_CH(p) \ {.port = p + WCD9335_RX_START, .shift = p,} #define WCD9335_SLIM_TX_CH(p) \ {.port = p, .shift = p,} +static int btn_mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_BTN_4; +static int hs_jack_mask = SND_JACK_HEADPHONE | SND_JACK_HEADSET; + /* vout step value */ #define WCD9335_CALCULATE_VOUT_D(req_mv) (((req_mv - 650) * 10) / 25) @@ -310,6 +317,16 @@ struct wcd9335_codec { u32 num_rx_port; u32 num_tx_port; + struct snd_soc_jack *jack; + bool hphl_jack_type_normally_open; + bool gnd_jack_type_normally_open; + bool mbhc_btn_enabled; + int mbhc_btn0_released; + bool detect_accessory_type; + int accessory_type; + /* Voltage threshold for button detection */ + u32 vref_btn[WCD9335_MBHC_MAX_BUTTONS]; + int sido_input_src; enum wcd9335_sido_voltage sido_voltage; @@ -2805,7 +2822,6 @@ static int wcd9335_codec_enable_dec(struct snd_soc_dapm_widget *w, break; case SND_SOC_DAPM_POST_PMU: snd_soc_component_update_bits(comp, hpf_gate_reg, 0x01, 0x00); - if (decimator == 0) { snd_soc_component_write(comp, WCD9335_MBHC_ZDET_RAMP_CTL, 0x83); @@ -2816,7 +2832,6 @@ static int wcd9335_codec_enable_dec(struct snd_soc_dapm_widget *w, snd_soc_component_write(comp, WCD9335_MBHC_ZDET_RAMP_CTL, 0x03); } - snd_soc_component_update_bits(comp, hpf_gate_reg, 0x01, 0x01); snd_soc_component_update_bits(comp, tx_vol_ctl_reg, @@ -3954,6 +3969,131 @@ static int wcd9335_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, return 0; } +static irqreturn_t wcd9335_mbhc_sw_irq(int irq, void *data) +{ + struct wcd9335_codec *wcd = data; + struct snd_soc_component *component = wcd->component; + bool ins = false; + + if (snd_soc_component_read32(component, WCD9335_ANA_MBHC_MECH) & + WCD9335_MBHC_MECH_DETECT_TYPE_MASK) + ins = true; + + /* Set the detection type appropriately */ + snd_soc_component_update_bits(component, WCD9335_ANA_MBHC_MECH, + WCD9335_MBHC_MECH_DETECT_TYPE_MASK, + (!ins << WCD9335_MBHC_MECH_DETECT_TYPE_SHIFT)); + + if (ins) { /* hs insertion */ + u32 btndet_curr_src; + + /* + * If no micbias is enabled, then enable 100uA internal + * current source for Button detection + */ + if (snd_soc_component_read32(component, WCD9335_ANA_MICB2) & + WCD9335_ANA_MICB2_ENABLE) + btndet_curr_src = WCD9335_ANA_MBHC_BD_ISRC_OFF; + else + btndet_curr_src = WCD9335_ANA_MBHC_BD_ISRC_100UA; + + snd_soc_component_update_bits(component, + WCD9335_ANA_MBHC_ELECT, + WCD9335_ANA_MBHC_BD_ISRC_CTL_MASK, + btndet_curr_src); + + /* + * if only a btn0 press event is receive just before + * insert event then its a 3 pole headphone else if + * both press and release event received then its + * a headset. + */ + if (wcd->mbhc_btn0_released) { + snd_soc_jack_report(wcd->jack, + SND_JACK_HEADSET, hs_jack_mask); + wcd->accessory_type = SND_JACK_HEADSET; + } else { + snd_soc_jack_report(wcd->jack, + SND_JACK_HEADPHONE, hs_jack_mask); + wcd->accessory_type = SND_JACK_HEADPHONE; + } + + wcd->detect_accessory_type = false; + + } else { /* removal */ + snd_soc_jack_report(wcd->jack, 0, hs_jack_mask); + wcd->detect_accessory_type = true; + wcd->mbhc_btn0_released = false; + } + + return IRQ_HANDLED; +} + +static irqreturn_t wcd9335_mbhc_btn_press_irq(int irq, void *data) +{ + struct wcd9335_codec *wcd = data; + struct snd_soc_component *comp = wcd->component; + u32 btn_result, result; + + /* do not handle any button events for headset without buttons */ + if (wcd->accessory_type == SND_JACK_HEADPHONE) + return IRQ_HANDLED; + + result = snd_soc_component_read32(comp, WCD9335_ANA_MBHC_RESULT_3); + btn_result = result & WCD9335_MBHC_BTN_RESULT_MASK; + + switch (btn_result) { + case 0xf: + snd_soc_jack_report(wcd->jack, SND_JACK_BTN_4, btn_mask); + break; + case 0x4: + snd_soc_jack_report(wcd->jack, SND_JACK_BTN_4, btn_mask); + break; + case 0x3: + snd_soc_jack_report(wcd->jack, SND_JACK_BTN_3, btn_mask); + break; + case 0x2: + snd_soc_jack_report(wcd->jack, SND_JACK_BTN_2, btn_mask); + break; + case 0x1: + snd_soc_jack_report(wcd->jack, SND_JACK_BTN_1, btn_mask); + break; + case 0x0: + /* handle BTN_0 specially for type detection */ + if (!wcd->detect_accessory_type) + snd_soc_jack_report(wcd->jack, + SND_JACK_BTN_0, btn_mask); + break; + default: + dev_err(comp->dev, + "Unexpected button press result (%x)", btn_result); + break; + } + + return IRQ_HANDLED; +} + +static irqreturn_t wcd9335_mbhc_bt_rel_irq(int irq, void *data) +{ + struct wcd9335_codec *wcd = data; + + + if (wcd->detect_accessory_type) { + u32 result = snd_soc_component_read32(wcd->component, + WCD9335_ANA_MBHC_RESULT_3); + + /* check if its BTN0 thats released */ + if (!(result & WCD9335_MBHC_BTN_RESULT_MASK)) + wcd->mbhc_btn0_released = true; + + } else { + if (wcd->accessory_type != SND_JACK_HEADPHONE) + snd_soc_jack_report(wcd->jack, 0, btn_mask); + } + + return IRQ_HANDLED; +} + static irqreturn_t wcd9335_slimbus_irq(int irq, void *data) { struct wcd9335_codec *wcd = data; @@ -4028,11 +4168,95 @@ static irqreturn_t wcd9335_slimbus_irq(int irq, void *data) return ret; } +static void wcd9335_program_btn_threshold(struct wcd9335_codec *wcd) +{ + int i, vth; + + for (i = 0; i < WCD9335_MBHC_MAX_BUTTONS; i++) { + vth = ((wcd->vref_btn[i] * 2) / 25) & 0x3F; + snd_soc_component_update_bits(wcd->component, + WCD9335_ANA_MBHC_BTN0 + i, + 0xFC, vth << 2); + } +} + +static void wcd9335_mbhc_initialise(struct wcd9335_codec *wcd) +{ + struct snd_soc_component *comp = wcd->component; + u32 plug_type = 0; + + snd_soc_component_update_bits(comp, WCD9335_MBHC_PLUG_DETECT_CTL, + WCD9335_MBHC_HSDET_PULLUP_CTL_MASK, + WCD9335_MBHC_HSDET_PULLUP_CTL_1_2P0_UA); + + if (wcd->hphl_jack_type_normally_open) + plug_type |= WCD9335_MBHC_HPHL_PLUG_TYPE_NO; + + if (wcd->gnd_jack_type_normally_open) + plug_type |= WCD9335_MBHC_GND_PLUG_TYPE_NO; + + snd_soc_component_write(wcd->component, WCD9335_ANA_MBHC_MECH, + plug_type | + WCD9335_MBHC_L_DET_EN | + WCD9335_MBHC_HSL_PULLUP_COMP_EN | + WCD9335_MBHC_HPHL_100K_TO_GND_EN); + + /* Insertion debounce set to 96ms */ + snd_soc_component_write(wcd->component, + WCD9335_MBHC_PLUG_DETECT_CTL, + WCD9335_MBHC_DBNC_TIMER_INSREM_DBNC_T_96_MS| + WCD9335_MBHC_HSDET_PULLUP_CTL_1_2P0_UA); + /* Button Debounce set to 16ms */ + snd_soc_component_update_bits(wcd->component, WCD9335_MBHC_CTL_1, + WCD9335_MBHC_BTN_DBNC_MASK, + WCD9335_MBHC_BTN_DBNC_T_16_MS); + + /* enable bias distribution control */ + snd_soc_component_update_bits(comp, WCD9335_ANA_MBHC_ELECT, + WCD9335_ANA_MBHC_BIAS_EN_MASK, + WCD9335_ANA_MBHC_BIAS_EN); + + snd_soc_component_update_bits(wcd->component, + WCD9335_ANA_MBHC_ELECT, + WCD9335_ANA_MBHC_BD_ISRC_CTL_MASK, + WCD9335_ANA_MBHC_BD_ISRC_100UA); + + /* enable MBHC clock */ + snd_soc_component_update_bits(wcd->component, WCD9335_MBHC_CTL_1, + WCD9335_MBHC_CTL_RCO_EN_MASK, + WCD9335_MBHC_CTL_RCO_EN); + + snd_soc_component_update_bits(wcd->component, WCD9335_MBHC_CTL_2, + WCD9335_MBHC_HS_VREF_CTL_MASK, + WCD9335_MBHC_HS_VREF_1P5_V); + + /* program HS_VREF value */ + wcd9335_program_btn_threshold(wcd); + /* Start FSM */ + snd_soc_component_update_bits(wcd->component, WCD9335_ANA_MBHC_ELECT, + BIT(7), BIT(7)); + + wcd->mbhc_btn0_released = false; + wcd->detect_accessory_type = true; +} + static struct wcd9335_irq wcd9335_irqs[] = { { .irq = WCD9335_IRQ_SLIMBUS, .handler = wcd9335_slimbus_irq, .name = "SLIM Slave", + }, { + .irq = WCD9335_IRQ_MBHC_SW_DET, + .handler = wcd9335_mbhc_sw_irq, + .name = "Headset Mech Insert Removal", + }, { + .irq = WCD9335_IRQ_MBHC_BUTTON_PRESS_DET, + .handler = wcd9335_mbhc_btn_press_irq, + .name = "Headset Button Press", + }, { + .irq = WCD9335_IRQ_MBHC_BUTTON_RELEASE_DET, + .handler = wcd9335_mbhc_bt_rel_irq, + .name = "Headset Button Release", }, }; @@ -4841,6 +5065,7 @@ static void wcd9335_codec_init(struct snd_soc_component *component) wcd9335_codec_reg_init[i].val); wcd9335_enable_efuse_sensing(component); + wcd9335_mbhc_initialise(wcd); } static int wcd9335_codec_probe(struct snd_soc_component *component) @@ -4905,10 +5130,21 @@ static int wcd9335_codec_set_sysclk(struct snd_soc_component *comp, return clk_set_rate(wcd->mclk, freq); } +static int wcd9335_codec_set_jack(struct snd_soc_component *comp, + struct snd_soc_jack *jack, void *data) +{ + struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev); + + wcd->jack = jack; + + return 0; +} + static const struct snd_soc_component_driver wcd9335_component_drv = { .probe = wcd9335_codec_probe, .remove = wcd9335_codec_remove, .set_sysclk = wcd9335_codec_set_sysclk, + .set_jack = wcd9335_codec_set_jack, .controls = wcd9335_snd_controls, .num_controls = ARRAY_SIZE(wcd9335_snd_controls), .dapm_widgets = wcd9335_dapm_widgets, @@ -4918,10 +5154,39 @@ static const struct snd_soc_component_driver wcd9335_component_drv = { .endianness = 1, }; +static void of_parse_mbhc_data(struct device *dev, struct wcd9335_codec *wcd) +{ + int rval; + + if (of_property_read_bool(dev->of_node, + "qcom,hphl-jack-type-normally-open")) + wcd->hphl_jack_type_normally_open = true; + else + wcd->hphl_jack_type_normally_open = false; + + if (of_property_read_bool(dev->of_node, + "qcom,gnd-jack-type-normally-open")) + wcd->gnd_jack_type_normally_open = true; + else + wcd->gnd_jack_type_normally_open = false; + + wcd->mbhc_btn_enabled = true; + rval = of_property_read_u32_array(dev->of_node, + "qcom,mbhc-vthreshold", + &wcd->vref_btn[0], + WCD9335_MBHC_MAX_BUTTONS); + if (rval < 0) { + wcd->mbhc_btn_enabled = false; + dev_err(dev, "MBHC btn detection disabled\n"); + } +} + static int wcd9335_probe(struct wcd9335_codec *wcd) { struct device *dev = wcd->dev; + of_parse_mbhc_data(dev, wcd); + memcpy(wcd->rx_chs, wcd9335_rx_chs, sizeof(wcd9335_rx_chs)); memcpy(wcd->tx_chs, wcd9335_tx_chs, sizeof(wcd9335_tx_chs)); @@ -5009,6 +5274,33 @@ static const struct regmap_irq wcd9335_codec_irqs[] = { .type_reg_mask = BIT(0), }, }, + [WCD9335_IRQ_MBHC_SW_DET] = { + .reg_offset = 1, + .mask = BIT(0), + .type = { + .type_reg_offset = 1, + .types_supported = IRQ_TYPE_EDGE_BOTH, + .type_reg_mask = BIT(0), + }, + }, + [WCD9335_IRQ_MBHC_BUTTON_PRESS_DET] = { + .reg_offset = 1, + .mask = BIT(2), + .type = { + .type_reg_offset = 1, + .types_supported = IRQ_TYPE_EDGE_BOTH, + .type_reg_mask = BIT(2), + }, + }, + [WCD9335_IRQ_MBHC_BUTTON_RELEASE_DET] = { + .reg_offset = 1, + .mask = BIT(3), + .type = { + .type_reg_offset = 1, + .types_supported = IRQ_TYPE_EDGE_BOTH, + .type_reg_mask = BIT(3), + }, + } }; static const unsigned int wcd9335_config_regs[] = { From e0bb18b78357d8f156704c0b1de991e7c138923a Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 10 Aug 2021 13:34:05 +0200 Subject: [PATCH 0291/1823] ASoC: codecs: wcd9335: Fix headphone jack commit for new upstream kernel --- sound/soc/codecs/wcd9335.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index f8e3873008c1..835e14abdc77 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -3975,7 +3975,7 @@ static irqreturn_t wcd9335_mbhc_sw_irq(int irq, void *data) struct snd_soc_component *component = wcd->component; bool ins = false; - if (snd_soc_component_read32(component, WCD9335_ANA_MBHC_MECH) & + if (snd_soc_component_read(component, WCD9335_ANA_MBHC_MECH) & WCD9335_MBHC_MECH_DETECT_TYPE_MASK) ins = true; @@ -3991,7 +3991,7 @@ static irqreturn_t wcd9335_mbhc_sw_irq(int irq, void *data) * If no micbias is enabled, then enable 100uA internal * current source for Button detection */ - if (snd_soc_component_read32(component, WCD9335_ANA_MICB2) & + if (snd_soc_component_read(component, WCD9335_ANA_MICB2) & WCD9335_ANA_MICB2_ENABLE) btndet_curr_src = WCD9335_ANA_MBHC_BD_ISRC_OFF; else @@ -4039,7 +4039,7 @@ static irqreturn_t wcd9335_mbhc_btn_press_irq(int irq, void *data) if (wcd->accessory_type == SND_JACK_HEADPHONE) return IRQ_HANDLED; - result = snd_soc_component_read32(comp, WCD9335_ANA_MBHC_RESULT_3); + result = snd_soc_component_read(comp, WCD9335_ANA_MBHC_RESULT_3); btn_result = result & WCD9335_MBHC_BTN_RESULT_MASK; switch (btn_result) { @@ -4079,7 +4079,7 @@ static irqreturn_t wcd9335_mbhc_bt_rel_irq(int irq, void *data) if (wcd->detect_accessory_type) { - u32 result = snd_soc_component_read32(wcd->component, + u32 result = snd_soc_component_read(wcd->component, WCD9335_ANA_MBHC_RESULT_3); /* check if its BTN0 thats released */ From f2e1fd09d42e98729f231b3422fbfa67db57cc81 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 10 Aug 2021 16:40:05 +0200 Subject: [PATCH 0292/1823] arm64: dts: qcom: AUDIO WORKS oon MSM8998 Sony Yoshino platform!!! --- .../msm8998-sony-xperia-yoshino-maple.dts | 2 +- .../dts/qcom/msm8998-sony-xperia-yoshino.dtsi | 215 ++++++++++++++++-- 2 files changed, 202 insertions(+), 15 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-maple.dts b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-maple.dts index 3e0857b6a270..78502477ab56 100644 --- a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-maple.dts +++ b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-maple.dts @@ -79,7 +79,7 @@ function = "normal"; bias-disable; drive-push-pull; - output-low; + output-high; power-source = <1>; /* 1.8V */ qcom,drive-strength = <1>; }; diff --git a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi index 3e152318e012..3c4362555fcc 100644 --- a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi @@ -521,13 +521,11 @@ dai@2 { reg = <2>; }; -/* dai@3 { reg = <3>; - direction = <2>; - is-compress-dai; +// direction = <2>; +// is-compress-dai; }; -*/ }; &qusb2phy { @@ -738,6 +736,8 @@ pinctrl-1 = <&sdc2_off &sdc2_cd>; }; + /* EAR-EN is NXP NX5L2750C */ + /* Downstream example &snd_9335 { qcom,msm-mbhc-hphl-swh = <1>; @@ -786,21 +786,27 @@ <&rpmcc RPM_SMD_LN_BB_CLK1>; #clock-cells = <0>; - vdd-buck-supply = <&vreg_s4a_1p8>; - vdd-buck-sido-supply = <&vreg_s4a_1p8>; - vdd-tx-supply = <&vreg_s4a_1p8>; - vdd-rx-supply = <&vreg_s4a_1p8>; - vdd-io-supply = <&vreg_s4a_1p8>; - interrupt-parent = <&tlmm>; interrupts = <54 IRQ_TYPE_LEVEL_HIGH>; interrupt-names = "intr1"; interrupt-controller; #interrupt-cells = <1>; - reset-gpios = <&tlmm 64 0>; + + reset-gpios = <&tlmm 64 GPIO_ACTIVE_LOW>; slim-ifc-dev = <&tasha_ifd>; + vdd-buck-supply = <&vreg_s4a_1p8>; + vdd-buck-sido-supply = <&vreg_s4a_1p8>; + vdd-tx-supply = <&vreg_s4a_1p8>; + vdd-rx-supply = <&vreg_s4a_1p8>; + vdd-io-supply = <&vreg_s4a_1p8>; + qcom,mbhc-vthreshold = <1700>; + + /* On SoMC Yoshino, HPHL is normally open, GND normally closed */ + qcom,hphl-jack-type-normally-open; + //qcom,gnd-jack-type-normally-open; + #address-cells = <1>; #size-cells = <1>; #sound-dai-cells = <1>; @@ -848,20 +854,197 @@ }; }; + +/* NOTES */ +/* + # This is Dragonboard 820C + EnableSequence [ + cset "name='SLIM RX0 MUX' ZERO" + cset "name='SLIM RX1 MUX' ZERO" + cset "name='SLIM RX2 MUX' ZERO" + cset "name='SLIM RX3 MUX' ZERO" + cset "name='SLIM RX4 MUX' ZERO" + cset "name='SLIM RX5 MUX' AIF4_PB" + cset "name='SLIM RX6 MUX' AIF4_PB" + cset "name='SLIM RX7 MUX' ZERO" + cset "name='RX INT1_2 MUX' RX5" + cset "name='RX INT2_2 MUX' RX6" + ## gain to 0dB + cset "name='RX5 Digital Volume' 68" + ## gain to 0dB + cset "name='RX6 Digital Volume' 68" + cset "name='SLIMBUS_6_RX Audio Mixer MultiMedia2' 1" + cset "name='RX INT1 DEM MUX' CLSH_DSM_OUT" + cset "name='RX INT2 DEM MUX' CLSH_DSM_OUT" + ] + + +######### WORKS!!!!!! SHE SPEEEEEAKS!!!!!!! ######### +tinymix set "SLIM RX2 MUX" ZERO +tinymix set "SLIM RX3 MUX" ZERO +tinymix set "SLIM RX4 MUX" ZERO +tinymix set "SLIM RX5 MUX" ZERO +tinymix set "SLIM RX6 MUX" ZERO +tinymix set "SLIM RX7 MUX" ZERO +tinymix set "SLIM RX0 MUX" AIF1_PB +tinymix set "SLIM RX1 MUX" AIF1_PB +tinymix set "RX INT1_2 MUX" RX0 +tinymix set "RX INT2_2 MUX" RX1 +tinymix set "RX INT1_1 MIX1 INP0" RX0 +tinymix set "RX INT2_1 MIX1 INP0" RX1 +tinymix set "SLIMBUS_0_RX Audio Mixer MultiMedia1" 1 +tinymix set "RX INT1 DEM MUX" CLSH_DSM_OUT +tinymix set "RX INT2 DEM MUX" CLSH_DSM_OUT +tinymix set "SLIM TX0 MUX" DEC0 +tinymix set "AIF1_CAP Mixer SLIM TX0" 1 + +tinymix set "RX INT2_1 MIX1 INP0" RX1 +tinymix set "RX INT1_1 MIX1 INP0" RX0 +tinymix set "RX INT1_1 MIX1 INP0" RX2 +tinymix set "RX INT1_1 MIX1 INP0" RX0 +tinymix set "RX INT1_1 MIX1 INP0" RX2 +tinymix set "RX INT2_1 MIX1 INP0" RX2 +tinymix set "RX INT2_1 MIX1 INP0" RX1 +tinymix set "RX INT1_1 MIX1 INP0" RX0 +tinymix set "RX INT0_1 MIX1 INP0" RX0 +tinymix set "RX INT0_1 MIX1 INP0" RX1 +tinymix set "RX INT0_1 MIX1 INP0" RX2 +tinymix set "RX INT0_1 MIX1 INP0" RX0 +tinymix set "RX INT3_1 MIX1 INP0" RX0 +tinymix set "RX INT4_1 MIX1 INP0" RX0 +tinymix set "RX INT5_1 MIX1 INP0" RX0 +tinymix set "RX INT6_1 MIX1 INP0" RX1 +tinymix set "RX INT7_1 MIX1 INP0" RX1 +tinymix set "RX INT7_1 MIX1 INP0" RX1 +tinymix set "RX INT8_1 MIX1 INP0" RX1 +tinymix set "RX INT0_1 MIX1 INP1" RX0 +tinymix set "RX INT0_1 MIX1 INP1" RX2 +tinymix set "RX INT0_1 MIX1 INP1" RX0 +tinymix set "RX INT1_1 MIX1 INP1" RX0 +tinymix set "RX INT2_1 MIX1 INP1" RX0 +tinymix set "RX INT3_1 MIX1 INP1" RX0 +tinymix set "RX INT4_1 MIX1 INP1" RX0 +tinymix set "RX INT5_1 MIX1 INP1" RX0 +tinymix set "RX INT6_1 MIX1 INP1" RX0 +tinymix set "RX INT7_1 MIX1 INP1" RX0 +tinymix set "RX INT8_1 MIX1 INP1" RX0 +tinymix set "RX INT0_1 MIX1 INP2" RX1 +tinymix set "RX INT1_1 MIX1 INP2" RX1 +tinymix set "RX INT2_1 MIX1 INP2" RX1 +tinymix set "RX INT3_1 MIX1 INP2" RX1 +tinymix set "RX INT4_1 MIX1 INP2" RX1 +tinymix set "RX INT5_1 MIX1 INP2" RX1 +tinymix set "RX INT6_1 MIX1 INP2" RX1 +tinymix set "RX INT7_1 MIX1 INP2" RX1 +tinymix set "RX INT8_1 MIX1 INP2" RX1 +tinymix set "RX INT8_1 MIX1 INP2" RX0 +tinymix set "RX INT7_1 MIX1 INP2" RX0 +tinymix set "RX INT6_1 MIX1 INP2" RX0 + +tinymix set "RX0 Digital Volume" 80 +tinymix set "RX1 Digital Volume" 80 +tinymix set "RX2 Digital Volume" 80 + +*/ + &sound { compatible = "qcom,msm8998-sndcard"; model = "Sony-Xperia-Yoshino"; - audio-routing = "RX_BIAS", "MCLK", + /* Audio routing including WSA amp speakers */ +/* audio-routing = "RX_BIAS", "MCLK", "AMIC2", "MIC BIAS2", "AMIC3", "MIC BIAS3", "DMIC0", "MIC BIAS1", "DMIC4", "MIC BIAS4", "SpkrLeft IN", "SPK1 OUT", "SpkrRight IN", "SPK2 OUT", - "MM_DL1", "MultiMedia1 Playback"; + "MM_DL1", "MultiMedia1 Playback", + "MM_DL2", "MultiMedia2 Playback", + "MultiMedia3 Capture", "MM_UL3"; +*/ + + /* Basic routing, 3.5mm jack only */ + audio-routing = "RX_BIAS", "MCLK", + "AMIC2", "MIC BIAS2", + "AMIC3", "MIC BIAS3", + "DMIC0", "MIC BIAS1", + "DMIC4", "MIC BIAS4", + "MM_DL1", "MultiMedia1 Playback", + "MM_DL2", "MultiMedia2 Playback", + "MultiMedia3 Capture", "MM_UL3"; + +/* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +*/ mm1-dai-link { + /* Deep Buffer playback for SLIM{0,7}, BT, USBAUDIO, AFE, DP, HDMI */ link-name = "MultiMedia1"; cpu { sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA1>; @@ -869,6 +1052,7 @@ }; mm2-dai-link { + /* Multichannel playback for HDMI and DP */ link-name = "MultiMedia2"; cpu { sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA2>; @@ -876,6 +1060,7 @@ }; mm3-dai-link { + /* Ultra Low Latency playback for SLIM0, HDMI, and DP */ link-name = "MultiMedia3"; cpu { sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA3>; @@ -893,7 +1078,9 @@ }; codec { - sound-dai = <&left_spkr>, <&right_spkr>, <&swm 0>, <&wcd9335 0>; + /* Support only sound through 3.5mm for now: soundwire is currently unavailable */ + //sound-dai = <&left_spkr>, <&right_spkr>, <&swm 0>, <&wcd9335 0>; + sound-dai = <&wcd9335 0>; }; }; From 1d7018b903c51911d1ebb0e659e60d28d7a5ec58 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 10 Aug 2021 20:38:28 +0200 Subject: [PATCH 0293/1823] iommu/arm-smmu-qcom: Skip the TTBR1 quirk for MSM8998 and SDM630 Similarly to MSM8996 DragonBoard 820c, MSM8998 and SDM630 are equipped with Adreno 5xx series, which doesn't have separate pagetables support at the moment of writing. Skip the TTBR1 quirk for these two SoCs as to get Adreno in a usable state. --- drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c index e71706e2964c..c743efb35372 100644 --- a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c +++ b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c @@ -199,7 +199,9 @@ static bool qcom_adreno_can_do_ttbr1(struct arm_smmu_device *smmu) { const struct device_node *np = smmu->dev->of_node; - if (of_device_is_compatible(np, "qcom,msm8996-smmu-v2")) + if (of_device_is_compatible(np, "qcom,msm8996-smmu-v2") || + of_device_is_compatible(np, "qcom,msm8998-smmu-v2") || + of_device_is_compatible(np, "qcom,sdm630-smmu-v2")) return false; return true; From 8720300aa39b837c037c55a617fde64404ad3c20 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 10 Aug 2021 20:42:48 +0200 Subject: [PATCH 0294/1823] iommu/arm-smmu-qcom: Add MSM8998 and SDM660 mss compatibles for identity Add MSM8998 and SDM660's mss-pil compatibles to switch the default iommu domain type to IDENTITY, as similarly required by SDM845 and others. Signed-off-by: AngeloGioacchino Del Regno (JAMI: fixup for 6.0-rc2) --- drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c index c743efb35372..03e016058ee5 100644 --- a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c +++ b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c @@ -247,12 +247,14 @@ static const struct of_device_id qcom_smmu_client_of_match[] __maybe_unused = { { .compatible = "qcom,adreno" }, { .compatible = "qcom,mdp4" }, { .compatible = "qcom,mdss" }, + { .compatible = "qcom,msm8998-mss-pil" }, { .compatible = "qcom,sc7180-mdss" }, { .compatible = "qcom,sc7180-mss-pil" }, { .compatible = "qcom,sc7280-mdss" }, { .compatible = "qcom,sc7280-mss-pil" }, { .compatible = "qcom,sc8180x-mdss" }, { .compatible = "qcom,sm8250-mdss" }, + { .compatible = "qcom,sdm660-mss-pil" }, { .compatible = "qcom,sdm845-mdss" }, { .compatible = "qcom,sdm845-mss-pil" }, { } From a3e55d753aa81333c39d3f7c8c476d5492e5f41b Mon Sep 17 00:00:00 2001 From: Jami Kettunen Date: Fri, 28 Jan 2022 15:58:16 +0200 Subject: [PATCH 0295/1823] ASoC: wcd9335: various tests Originally from: c215a022 --- sound/soc/codecs/wcd9335.c | 15 ++++++++++++--- sound/soc/codecs/wcd9335.h | 6 ++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index 835e14abdc77..43a11182e9ea 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -484,7 +484,7 @@ static const struct wcd9335_reg_mask_val wcd9335_codec_reg_init[] = { {WCD9335_RCO_CTRL_2, 0x0F, 0x08}, {WCD9335_RX_BIAS_FLYB_MID_RST, 0xF0, 0x10}, {WCD9335_FLYBACK_CTRL_1, 0x20, 0x20}, - {WCD9335_HPH_OCP_CTL, 0xFF, 0x5A}, + {WCD9335_HPH_OCP_CTL, 0xFF, 0x7A}, /*downstream is ff 7a */ {WCD9335_HPH_L_TEST, 0x01, 0x01}, {WCD9335_HPH_R_TEST, 0x01, 0x01}, {WCD9335_CDC_BOOST0_BOOST_CFG1, 0x3F, 0x12}, @@ -497,6 +497,13 @@ static const struct wcd9335_reg_mask_val wcd9335_codec_reg_init[] = { {WCD9335_CDC_RX0_RX_PATH_SEC0, 0xFC, 0xF4}, {WCD9335_HPH_REFBUFF_LP_CTL, 0x08, 0x08}, {WCD9335_HPH_REFBUFF_LP_CTL, 0x06, 0x02}, + + {WCD9335_DIFF_LO_CORE_OUT_PROG, 0xFC, 0xA0}, + {WCD9335_SE_LO_COM1, 0xFF, 0xC0}, + {WCD9335_CDC_RX3_RX_PATH_SEC0, 0xFC, 0xF4}, + {WCD9335_CDC_RX4_RX_PATH_SEC0, 0xFC, 0xF4}, + {WCD9335_CDC_RX5_RX_PATH_SEC0, 0xFC, 0xF8}, + {WCD9335_CDC_RX6_RX_PATH_SEC0, 0xFC, 0xF8}, }; /* Cutoff frequency for high pass filter */ @@ -3744,11 +3751,12 @@ static int wcd9335_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev); int hph_mode = wcd->hph_mode; - +pr_err("%s\n", __func__); switch (event) { case SND_SOC_DAPM_PRE_PMU: break; case SND_SOC_DAPM_POST_PMU: +pr_err("%s post pmu\n", __func__); /* * 7ms sleep is required after PA is enabled as per * HW requirement @@ -3772,6 +3780,7 @@ static int wcd9335_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, break; case SND_SOC_DAPM_PRE_PMD: +pr_err("%s pre pmd\n", __func__); wcd9335_codec_hph_post_pa_config(wcd, hph_mode, event); break; case SND_SOC_DAPM_POST_PMD: @@ -3893,7 +3902,7 @@ static int wcd9335_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev); int hph_mode = wcd->hph_mode; - +pr_err("%s\n", __func__); switch (event) { case SND_SOC_DAPM_PRE_PMU: break; diff --git a/sound/soc/codecs/wcd9335.h b/sound/soc/codecs/wcd9335.h index 490fc44144a2..d54d6b1aedea 100644 --- a/sound/soc/codecs/wcd9335.h +++ b/sound/soc/codecs/wcd9335.h @@ -368,8 +368,10 @@ #define WCD9335_EAR_CMBUFF WCD9335_REG(0x06, 0x0e2) #define WCD9335_DIFF_LO_LO2_COMPANDER WCD9335_REG(0x06, 0x0ea) #define WCD9335_DIFF_LO_LO1_COMPANDER WCD9335_REG(0x06, 0x0eb) +#define WCD9335_DIFF_LO_CORE_OUT_PROG WCD9335_REG(0x06, 0x0ef) #define WCD9335_DIFF_LO_COM_SWCAP_REFBUF_FREQ WCD9335_REG(0x06, 0x0f1) #define WCD9335_DIFF_LO_COM_PA_FREQ WCD9335_REG(0x06, 0x0f2) +#define WCD9335_SE_LO_COM1 WCD9335_REG(0x06, 0x0f6) #define WCD9335_SE_LO_LO3_GAIN WCD9335_REG(0x06, 0x0f8) #define WCD9335_SE_LO_LO3_CTRL WCD9335_REG(0x06, 0x0f9) #define WCD9335_SE_LO_LO4_GAIN WCD9335_REG(0x06, 0x0fa) @@ -495,6 +497,7 @@ #define WCD9335_CDC_RX3_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x082) #define WCD9335_CDC_RX3_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x083) #define WCD9335_CDC_RX3_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x084) +#define WCD9335_CDC_RX3_RX_PATH_SEC0 WCD9335_REG(0x0b, 0x085) #define WCD9335_CDC_RX4_RX_PATH_CTL WCD9335_REG(0x0b, 0x091) #define WCD9335_CDC_RX4_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x092) #define WCD9335_CDC_RX4_RX_PATH_CFG2 WCD9335_REG(0x0b, 0x094) @@ -502,6 +505,7 @@ #define WCD9335_CDC_RX4_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x096) #define WCD9335_CDC_RX4_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x097) #define WCD9335_CDC_RX4_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x098) +#define WCD9335_CDC_RX4_RX_PATH_SEC0 WCD9335_REG(0x0b, 0x099) #define WCD9335_CDC_RX5_RX_PATH_CTL WCD9335_REG(0x0b, 0x0a5) #define WCD9335_CDC_RX5_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x0a6) #define WCD9335_CDC_RX5_RX_PATH_CFG2 WCD9335_REG(0x0b, 0x0a8) @@ -509,6 +513,7 @@ #define WCD9335_CDC_RX5_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x0aa) #define WCD9335_CDC_RX5_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x0ab) #define WCD9335_CDC_RX5_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x0ac) +#define WCD9335_CDC_RX5_RX_PATH_SEC0 WCD9335_REG(0x0b, 0x0ad) #define WCD9335_CDC_RX6_RX_PATH_CTL WCD9335_REG(0x0b, 0x0b9) #define WCD9335_CDC_RX6_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x0ba) #define WCD9335_CDC_RX6_RX_PATH_CFG2 WCD9335_REG(0x0b, 0x0bc) @@ -516,6 +521,7 @@ #define WCD9335_CDC_RX6_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x0be) #define WCD9335_CDC_RX6_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x0bf) #define WCD9335_CDC_RX6_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x0c0) +#define WCD9335_CDC_RX6_RX_PATH_SEC0 WCD9335_REG(0x0b, 0x0c1) #define WCD9335_CDC_RX7_RX_PATH_CTL WCD9335_REG(0x0b, 0x0cd) #define WCD9335_CDC_RX7_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x0ce) #define WCD9335_CDC_RX7_RX_PATH_CFG1 WCD9335_REG(0x0b, 0x0cf) From 2c83fb4bc9f674a3f10b82dbae0fb5ee580e942f Mon Sep 17 00:00:00 2001 From: Jami Kettunen Date: Fri, 28 Jan 2022 16:00:32 +0200 Subject: [PATCH 0296/1823] arm64: dts: qcom: msm8998-xperia: various tests Originally from: c215a022 --- arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi index 3c4362555fcc..58b2f154e84e 100644 --- a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino.dtsi @@ -787,12 +787,13 @@ #clock-cells = <0>; interrupt-parent = <&tlmm>; - interrupts = <54 IRQ_TYPE_LEVEL_HIGH>; - interrupt-names = "intr1"; + interrupts = <54 IRQ_TYPE_LEVEL_HIGH>, + <53 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "intr1", "intr2"; interrupt-controller; #interrupt-cells = <1>; - reset-gpios = <&tlmm 64 GPIO_ACTIVE_LOW>; + reset-gpios = <&tlmm 64 GPIO_ACTIVE_HIGH>; slim-ifc-dev = <&tasha_ifd>; From b1953b7b81211f8ffee84dbc2f462578904703b5 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 11 Aug 2021 21:28:34 +0200 Subject: [PATCH 0297/1823] adreno 5xx: TTBR1 extravaganza on MSM8998 (JAMI: fixup for 5.16) --- .../msm8998-sony-xperia-yoshino-maple.dts | 4 +- drivers/gpu/drm/msm/adreno/a5xx_gpu.c | 66 ++++++++++++++++++- drivers/gpu/drm/msm/adreno/a5xx_gpu.h | 1 + drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c | 4 +- 4 files changed, 69 insertions(+), 6 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-maple.dts b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-maple.dts index 78502477ab56..92654b1e4f6b 100644 --- a/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-maple.dts +++ b/arch/arm64/boot/dts/qcom/msm8998-sony-xperia-yoshino-maple.dts @@ -107,8 +107,8 @@ status = "okay"; //qcom,auto-string-detection; - qcom,num-strings = <4>; - qcom,enabled-strings = <0 1 2 3>; + qcom,num-strings = <3>; + qcom,enabled-strings = <0 1 2>; }; &wcd9335 { diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c index 3dcec7acb384..7ba1afef3412 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c @@ -121,6 +121,48 @@ static void a5xx_submit_in_rb(struct msm_gpu *gpu, struct msm_gem_submit *submit msm_gpu_retire(gpu); } +static void a5xx_set_pagetable(struct a5xx_gpu *a5xx_gpu, + struct msm_ringbuffer *ring, struct msm_file_private *ctx) +{ + phys_addr_t ttbr; + u32 asid; + u64 memptr = rbmemptr(ring, ttbr0); + + if (ctx == a5xx_gpu->cur_ctx) + return; + + if (msm_iommu_pagetable_params(ctx->aspace->mmu, &ttbr, &asid)) + return; + + /* Execute the table update */ + OUT_PKT7(ring, CP_SMMU_TABLE_UPDATE, 3); + OUT_RING(ring, CP_SMMU_TABLE_UPDATE_0_TTBR0_LO(lower_32_bits(ttbr))); + + OUT_RING(ring, + CP_SMMU_TABLE_UPDATE_1_TTBR0_HI(upper_32_bits(ttbr)) | + CP_SMMU_TABLE_UPDATE_1_ASID(asid)); + OUT_RING(ring, CP_SMMU_TABLE_UPDATE_2_CONTEXTIDR(0)); + + /* + * Write the new TTBR0 to the memstore. This is good for debugging. + */ + OUT_PKT7(ring, CP_MEM_WRITE, 4); + OUT_RING(ring, CP_MEM_WRITE_0_ADDR_LO(lower_32_bits(memptr))); + OUT_RING(ring, CP_MEM_WRITE_1_ADDR_HI(upper_32_bits(memptr))); + OUT_RING(ring, lower_32_bits(ttbr)); + OUT_RING(ring, (asid << 16) | upper_32_bits(ttbr)); + + /* + * And finally, trigger a uche flush to be sure there isn't anything + * lingering in that part of the GPU + */ + + OUT_PKT7(ring, CP_EVENT_WRITE, 1); + OUT_RING(ring, 0x31); + + a5xx_gpu->cur_ctx = ctx; +} + static void a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) { struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); @@ -151,13 +193,17 @@ static void a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) OUT_RING(ring, 1); /* Enable local preemption for finegrain preemption */ - OUT_PKT7(ring, CP_PREEMPT_ENABLE_GLOBAL, 1); - OUT_RING(ring, 0x02); + //OUT_PKT7(ring, CP_PREEMPT_ENABLE_GLOBAL, 1); + //OUT_RING(ring, 0x02); + OUT_PKT7(ring, CP_PREEMPT_ENABLE_LOCAL, 1); + OUT_RING(ring, 0x01); /* Allow CP_CONTEXT_SWITCH_YIELD packets in the IB2 */ OUT_PKT7(ring, CP_YIELD_ENABLE, 1); OUT_RING(ring, 0x02); + a5xx_set_pagetable(a5xx_gpu, ring, submit->queue->ctx); + /* Submit the commands */ for (i = 0; i < submit->nr_cmds; i++) { switch (submit->cmd[i].type) { @@ -942,6 +988,7 @@ static int a5xx_hw_init(struct msm_gpu *gpu) a5xx_preempt_fini(gpu); gpu->nr_rings = 1; } + a5xx_gpu->cur_ctx = NULL; a5xx_preempt_hw_init(gpu); @@ -1684,6 +1731,20 @@ static uint32_t a5xx_get_rptr(struct msm_gpu *gpu, struct msm_ringbuffer *ring) return ring->memptrs->rptr = gpu_read(gpu, REG_A5XX_CP_RB_RPTR); } +static struct msm_gem_address_space * +a5xx_create_private_address_space(struct msm_gpu *gpu) +{ + struct msm_mmu *mmu; + + mmu = msm_iommu_pagetable_create(gpu->aspace->mmu); + + if (IS_ERR(mmu)) + return ERR_CAST(mmu); + + return msm_gem_address_space_create(mmu, + "gpu", 0x100000000ULL, 0x1ffffffffULL); +} + static const struct adreno_gpu_funcs funcs = { .base = { .get_param = adreno_get_param, @@ -1706,6 +1767,7 @@ static const struct adreno_gpu_funcs funcs = { .gpu_state_get = a5xx_gpu_state_get, .gpu_state_put = a5xx_gpu_state_put, .create_address_space = adreno_iommu_create_address_space, + .create_private_address_space = a5xx_create_private_address_space, .get_rptr = a5xx_get_rptr, }, .get_timestamp = a5xx_get_timestamp, diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.h b/drivers/gpu/drm/msm/adreno/a5xx_gpu.h index c7187bcc5e90..781b759918e9 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.h +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.h @@ -29,6 +29,7 @@ struct a5xx_gpu { struct msm_ringbuffer *cur_ring; struct msm_ringbuffer *next_ring; + struct msm_file_private *cur_ctx; struct drm_gem_object *preempt_bo[MSM_GPU_MAX_RINGS]; struct drm_gem_object *preempt_counters_bo[MSM_GPU_MAX_RINGS]; diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c index 03e016058ee5..e9e69672d189 100644 --- a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c +++ b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c @@ -197,13 +197,13 @@ static int qcom_adreno_smmu_alloc_context_bank(struct arm_smmu_domain *smmu_doma static bool qcom_adreno_can_do_ttbr1(struct arm_smmu_device *smmu) { +/* const struct device_node *np = smmu->dev->of_node; - if (of_device_is_compatible(np, "qcom,msm8996-smmu-v2") || of_device_is_compatible(np, "qcom,msm8998-smmu-v2") || of_device_is_compatible(np, "qcom,sdm630-smmu-v2")) return false; - +*/ return true; } From 6a627ac6b711fb18e38384bea316d971b6d613d7 Mon Sep 17 00:00:00 2001 From: Jami Kettunen Date: Fri, 28 Jan 2022 16:08:09 +0200 Subject: [PATCH 0298/1823] [ANNOTATION] Base @ angelo/5.14-msm8998-audio-working I also rebased this tree on the linus/v5.18-rc1 tag and while at it finally dropped changes irrelevant to msm8998. From cd56ff15676be75d98c089307be3cf69610075e4 Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Sat, 15 Oct 2022 17:30:35 +0000 Subject: [PATCH 0299/1823] dt-bindings: input: document Qualcomm PMI8998 haptics driver Add bindings for qcom PMIC PMI8998 haptics driver. Signed-off-by: Caleb Connolly Reviewed-by: Krzysztof Kozlowski --- .../bindings/input/qcom,pmi8998-haptics.yaml | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 Documentation/devicetree/bindings/input/qcom,pmi8998-haptics.yaml diff --git a/Documentation/devicetree/bindings/input/qcom,pmi8998-haptics.yaml b/Documentation/devicetree/bindings/input/qcom,pmi8998-haptics.yaml new file mode 100644 index 000000000000..d43324cceb89 --- /dev/null +++ b/Documentation/devicetree/bindings/input/qcom,pmi8998-haptics.yaml @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright 2020 Unisoc Inc. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/qcom,pmi8998-haptics.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm PMI8998/PM660 Haptics + +maintainers: + - Caleb Connolly + +description: | + Qualcomm SPMI haptics is a peripheral on some QTI PMICs. It supports linear resonant + actuators and eccentric rotating mass type haptics commonly found in mobile devices. + It supports multiple sources of wave data such as an internal buffer, direct play + (from kernel or userspace) as well as an audio output mode. + +properties: + compatible: + items: + - enum: + - qcom,pmi8998-haptics + - qcom,pm660-charger + - qcom,pmi8996-haptics + - qcom,pmi8941-haptics + + reg: + maxItems: 1 + + interrupts: + maxItems: 2 + + interrupt-names: + items: + - const: short + - const: play + + qcom,wave-play-duration-us: + description: | + Wave sample duration in microseconds, 1/f where f + is the resonant frequency of the actuator. + This property is named qcom,wave-play-rate-us in + downstream kernels. + minimum: 0 + maximum: 20475 + +required: + - compatible + - reg + - interrupts + - qcom,wave-play-rate-us + +additionalProperties: false + +examples: + - | + #include + + spmi { + #address-cells = <1>; + #size-cells = <0>; + pmi8998_haptics: haptics@c000 { + compatible = "qcom,pmi8998-haptics"; + reg = <0xc000>; + + interrupts = <0x3 0xc0 0x0 IRQ_TYPE_EDGE_BOTH>, + <0x3 0xc0 0x1 IRQ_TYPE_EDGE_BOTH>; + interrupt-names = "short", "play"; + + qcom,wave-play-rate-us = <4255>; + }; + }; From 80b7e7d1a0a29c37c1ae43d7d33ebe53f39d5fe0 Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Sat, 15 Oct 2022 17:30:56 +0000 Subject: [PATCH 0300/1823] input: add Qualcomm SPMI haptics driver Add support for the haptics found in pmi8998 and related PMICs. Based on the ff-memless interface. Currently this driver provides a partial implementation of hardware features. This driver only supports LRAs (Linear Resonant Actuators) in the "buffer" mode with a single wave pattern. Signed-off-by: Caleb Connolly --- drivers/input/misc/Kconfig | 15 + drivers/input/misc/Makefile | 1 + drivers/input/misc/qcom-pmi8998-haptics.c | 690 ++++++++++++++++++++++ 3 files changed, 706 insertions(+) create mode 100644 drivers/input/misc/qcom-pmi8998-haptics.c diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 9f088900f863..37a9005706a6 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -186,6 +186,21 @@ config INPUT_PMIC8XXX_PWRKEY To compile this driver as a module, choose M here: the module will be called pmic8xxx-pwrkey. +config INPUT_QCOM_PMI8998_HAPTICS + tristate "Qualcomm SPMI HAPTICS" + depends on ARCH_QCOM || COMPILE_TEST + depends on MFD_PM8XXX || MFD_SPMI_PMIC || COMPILE_TEST + select INPUT_FF_MEMLESS + help + This option enables support for the haptics found in pmi8998 and + related PMICs. Based on the ff-memless interface. + + This driver is for hardware too new for the INPUT_PM8XXX_VIBRATOR + driver. + + To compile this driver as module, choose M here: the + module will be called qcom_pmi8998_haptics. + config INPUT_SPARCSPKR tristate "SPARC Speaker support" depends on PCI && SPARC64 diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 6abefc41037b..b5fbf63165d9 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -66,6 +66,7 @@ obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o obj-$(CONFIG_INPUT_POWERMATE) += powermate.o obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.o obj-$(CONFIG_INPUT_PWM_VIBRA) += pwm-vibra.o +obj-$(CONFIG_INPUT_QCOM_PMI8998_HAPTICS)+= qcom-pmi8998-haptics.o obj-$(CONFIG_INPUT_RAVE_SP_PWRBUTTON) += rave-sp-pwrbutton.o obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o obj-$(CONFIG_INPUT_REGULATOR_HAPTIC) += regulator-haptic.o diff --git a/drivers/input/misc/qcom-pmi8998-haptics.c b/drivers/input/misc/qcom-pmi8998-haptics.c new file mode 100644 index 000000000000..e9eec2d9de2d --- /dev/null +++ b/drivers/input/misc/qcom-pmi8998-haptics.c @@ -0,0 +1,690 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022, Caleb Connolly + * Qualcomm QPMI haptics driver for pmi8998 and related PMICs. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// clang-format off +#define HAP_STATUS_1_REG 0x0A +#define HAP_BUSY_BIT BIT(1) +#define SC_FLAG_BIT BIT(3) +#define AUTO_RES_ERROR_BIT BIT(4) + +#define HAP_LRA_AUTO_RES_LO_REG 0x0B +#define HAP_LRA_AUTO_RES_HI_REG 0x0C + +#define HAP_EN_CTL_REG 0x46 +#define HAP_EN_BIT BIT(7) + +#define HAP_EN_CTL2_REG 0x48 +#define BRAKE_EN_BIT BIT(0) + +#define HAP_AUTO_RES_CTRL_REG 0x4B +#define AUTO_RES_EN_BIT BIT(7) +#define AUTO_RES_ERR_RECOVERY_BIT BIT(3) +#define AUTO_RES_EN_FLAG_BIT BIT(0) + +#define HAP_CFG1_REG 0x4C +#define HAP_ACT_TYPE_MASK BIT(0) + +#define HAP_CFG2_REG 0x4D +#define HAP_LRA_RES_TYPE_MASK BIT(0) + +#define HAP_SEL_REG 0x4E +#define HAP_WF_SOURCE_MASK GENMASK(5, 4) +#define HAP_WF_SOURCE_SHIFT 4 + +#define HAP_LRA_AUTO_RES_REG 0x4F +#define LRA_AUTO_RES_MODE_MASK GENMASK(6, 4) +#define LRA_AUTO_RES_MODE_SHIFT 4 +#define LRA_HIGH_Z_MASK GENMASK(3, 2) +#define LRA_HIGH_Z_SHIFT 2 +#define LRA_RES_CAL_MASK GENMASK(1, 0) +#define HAP_RES_CAL_PERIOD_MIN 4 +#define HAP_RES_CAL_PERIOD_MAX 32 + +#define HAP_VMAX_CFG_REG 0x51 +#define HAP_VMAX_OVD_BIT BIT(6) +#define HAP_VMAX_MASK GENMASK(5, 1) +#define HAP_VMAX_SHIFT 1 + +#define HAP_ILIM_CFG_REG 0x52 +#define HAP_ILIM_SEL_MASK BIT(0) +#define HAP_ILIM_400_MA 0 +#define HAP_ILIM_800_MA 1 + +#define HAP_SC_DEB_REG 0x53 +#define HAP_SC_DEB_MASK GENMASK(2, 0) +#define HAP_SC_DEB_CYCLES_MIN 0 +#define HAP_DEF_SC_DEB_CYCLES 8 +#define HAP_SC_DEB_CYCLES_MAX 32 + +#define HAP_RATE_CFG1_REG 0x54 +#define HAP_RATE_CFG1_MASK GENMASK(7, 0) +#define HAP_RATE_CFG2_SHIFT 8 // As CFG2 is the most significant byte + +#define HAP_RATE_CFG2_REG 0x55 +#define HAP_RATE_CFG2_MASK GENMASK(3, 0) + +#define HAP_SC_CLR_REG 0x59 +#define SC_CLR_BIT BIT(0) + +#define HAP_BRAKE_REG 0x5C +#define HAP_BRAKE_PAT_MASK 0x3 + +#define HAP_WF_REPEAT_REG 0x5E +#define WF_REPEAT_MASK GENMASK(6, 4) +#define WF_REPEAT_SHIFT 4 +#define WF_REPEAT_MIN 1 +#define WF_REPEAT_MAX 128 +#define WF_S_REPEAT_MASK GENMASK(1, 0) +#define WF_S_REPEAT_MIN 1 +#define WF_S_REPEAT_MAX 8 + +#define HAP_WF_S1_REG 0x60 +#define HAP_WF_SIGN_BIT BIT(7) +#define HAP_WF_OVD_BIT BIT(6) +#define HAP_WF_SAMP_MAX GENMASK(5, 1) +#define HAP_WF_SAMPLE_LEN 8 + +#define HAP_PLAY_REG 0x70 +#define HAP_PLAY_BIT BIT(7) +#define HAP_PAUSE_BIT BIT(0) + +#define HAP_SEC_ACCESS_REG 0xD0 +#define HAP_SEC_ACCESS_UNLOCK 0xA5 + +#define HAP_TEST2_REG 0xE3 + + +#define HAP_VMAX_MIN_MV 116 +#define HAP_VMAX_MAX_MV 3596 +#define HAP_VMAX_MAX_MV_STRONG 3596 + +#define HAP_WAVE_PLAY_RATE_MIN_US 0 +#define HAP_WAVE_PLAY_RATE_MAX_US 20475 +#define HAP_WAVE_PLAY_TIME_MAX_MS 15000 + +#define AUTO_RES_ERR_POLL_TIME_NS (20 * NSEC_PER_MSEC) +#define HAPTICS_BACK_EMF_DELAY_US 20000 + +#define HAP_BRAKE_PAT_LEN 4 +#define HAP_WAVE_SAMP_LEN 8 +#define NUM_WF_SET 4 +#define HAP_WAVE_SAMP_SET_LEN (HAP_WAVE_SAMP_LEN * NUM_WF_SET) +#define HAP_RATE_CFG_STEP_US 5 + +#define SC_MAX_COUNT 5 +#define SC_COUNT_RST_DELAY_US 1000000 + +// Actuator types +#define HAP_TYPE_LRA 0 +#define HAP_TYPE_ERM 1 + +// LRA Wave type +#define HAP_WAVE_SINE 0 +#define HAP_WAVE_SQUARE 1 + +// Play modes +#define HAP_PLAY_DIRECT 0 +#define HAP_PLAY_BUFFER 1 +#define HAP_PLAY_AUDIO 2 +#define HAP_PLAY_PWM 3 + +#define HAP_PLAY_MAX HAP_PLAY_PWM + +// Auto resonance type +#define HAP_AUTO_RES_NONE 0 +#define HAP_AUTO_RES_ZXD 1 +#define HAP_AUTO_RES_QWD 2 +#define HAP_AUTO_RES_MAX_QWD 3 +#define HAP_AUTO_RES_ZXD_EOP 4 +// clang-format on + +static const uint8_t default_brake_pattern[] = { + 0x3, 0x3, 0x3, 0x3, 0x3, +}; + +static const uint8_t wave_sample_pattern[] = { + 0x7e, 0x7e, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, +}; + +/** + * struct spmi_haptics - struct for spmi haptics data. + * + * @dev: Our device parent. + * @regmap: Register map for the hardware block. + * @input: The input device used to receive events. + * @work: Work struct to play effects. + * @base: Base address of the regmap. + * @play_irq: Fired to load the next wave pattern. + * @sc_irq: Short circuit irq. + * @last_sc_time: Time since the short circuit IRQ last fired. + * @sc_count: Number of times the short circuit IRQ has fired in this interval. + * @actuator_type: The type of actuator in use. + * @wave_shape: The shape of the waves to use (sine or square). + * @play_mode: The play mode to use (direct, buffer, pwm, audio). + * @vmax: Max voltage to use when playing. + * @current_limit: The current limit for this hardware (400mA or 800mA). + * @play_wave_rate: The wave rate to use for this hardware. + * @play_lock: Lock to be held when updating the hardware state. + */ +struct spmi_haptics { + struct device *dev; + struct regmap *regmap; + struct input_dev *input; + struct work_struct work; + uint32_t base; + + int play_irq; + int sc_irq; + ktime_t last_sc_time; + uint8_t sc_count; + + uint8_t actuator_type; + uint8_t wave_shape; + uint8_t play_mode; + uint32_t vmax; + uint32_t current_limit; + uint32_t play_wave_rate; + struct mutex play_lock; +}; + +static int haptics_write_vmax(struct spmi_haptics *haptics) +{ + uint8_t val = 0; + uint32_t vmax_mv = haptics->vmax; + + vmax_mv = clamp_t(uint32_t, vmax_mv, HAP_VMAX_MIN_MV, HAP_VMAX_MAX_MV); + vmax_mv = DIV_ROUND_CLOSEST(vmax_mv, HAP_VMAX_MIN_MV); + + val = FIELD_PREP(HAP_VMAX_MASK, vmax_mv); + + return regmap_update_bits(haptics->regmap, + haptics->base + HAP_VMAX_CFG_REG, + HAP_VMAX_MASK | HAP_WF_OVD_BIT, val); +} + +static int haptics_module_enable(struct spmi_haptics *haptics, bool enable) +{ + return regmap_update_bits(haptics->regmap, + haptics->base + HAP_EN_CTL_REG, HAP_EN_BIT, + enable ? HAP_EN_BIT : 0); +} + +static int haptics_play(struct spmi_haptics *haptics, bool play) +{ + return regmap_update_bits(haptics->regmap, haptics->base + HAP_PLAY_REG, + HAP_PLAY_BIT | HAP_PAUSE_BIT, + play ? HAP_PLAY_BIT : 0); +} + +static bool is_haptics_module_enabled(struct spmi_haptics *haptics) +{ + uint32_t val; + + regmap_read(haptics->regmap, haptics->base + HAP_EN_CTL_REG, &val); + return !!val; +} + +/* + * This IRQ is fired to tell us to load the next wave sample set. + * As we only currently support a single sample set, it's unused. + */ +static irqreturn_t haptics_play_irq(int irq, void *data) +{ + struct spmi_haptics *haptics = data; + + dev_dbg(haptics->dev, "play_irq triggered"); + + return IRQ_HANDLED; +} + +/* + * Fires every ~50ms whilst the haptics are active. + * If the SC_FLAG_BIT is set then that means there isn't a short circuit + * and we just need to clear the IRQ to indicate that the device should + * keep vibrating. + * + * Otherwise, it means a short circuit situation has occurred. + */ +static irqreturn_t haptics_sc_irq(int irq, void *data) +{ + struct spmi_haptics *haptics = data; + int ret; + uint32_t val; + long sc_delta_time_us; + ktime_t temp; + + mutex_lock(&haptics->play_lock); + + ret = regmap_read(haptics->regmap, haptics->base + HAP_STATUS_1_REG, + &val); + if (ret) + goto out; + + if (!(val & SC_FLAG_BIT)) { + haptics->sc_count = 0; + goto out; + } + + temp = ktime_get(); + sc_delta_time_us = ktime_us_delta(temp, haptics->last_sc_time); + haptics->last_sc_time = temp; + + if (sc_delta_time_us > SC_COUNT_RST_DELAY_US) + haptics->sc_count = 0; + else + haptics->sc_count++; + + ret = regmap_update_bits(haptics->regmap, + haptics->base + HAP_SC_CLR_REG, SC_CLR_BIT, + SC_CLR_BIT); + if (ret) + goto out; + + if (haptics->sc_count > SC_MAX_COUNT) { + cancel_work_sync(&haptics->work); + dev_err(haptics->dev, + "Short circuit persists, disabling haptics\n"); + ret = haptics_module_enable(haptics, false); + if (ret) + dev_err(haptics->dev, "Error disabling module, rc=%d\n", + ret); + } + +out: + mutex_unlock(&haptics->play_lock); + return IRQ_HANDLED; +} + +static int haptics_vibrate(struct spmi_haptics *haptics) +{ + int ret; + + if (haptics->sc_count > SC_MAX_COUNT) { + dev_err(haptics->dev, "Can't play while in short circuit"); + return -EINVAL; + } + + ret = haptics_write_vmax(haptics); + if (ret) + return ret; + + ret = haptics_module_enable(haptics, true); + if (ret) { + dev_err(haptics->dev, "Error enabling module, ret=%d\n", ret); + return ret; + } + + ret = haptics_play(haptics, true); + if (ret) { + dev_err(haptics->dev, "Error enabling play, ret=%d\n", ret); + return ret; + } + + return ret; +} + +static int haptics_stop_vibrate(struct spmi_haptics *haptics) +{ + int ret; + + ret = haptics_play(haptics, false); + if (ret) { + dev_err(haptics->dev, "Error disabling play, ret=%d\n", ret); + return ret; + } + + ret = haptics_module_enable(haptics, false); + if (ret) { + dev_err(haptics->dev, "Error disabling module, ret=%d\n", ret); + return ret; + } + + return ret; +} + +static void haptics_play_stop_work(struct work_struct *work) +{ + struct spmi_haptics *haptics = + container_of(work, struct spmi_haptics, work); + + int ret; + + mutex_lock(&haptics->play_lock); + + if (!is_haptics_module_enabled(haptics) && haptics->vmax) + ret = haptics_vibrate(haptics); + else + ret = haptics_stop_vibrate(haptics); + if (ret) + dev_err(haptics->dev, "Error setting haptics, ret=%d", ret); + + mutex_unlock(&haptics->play_lock); +} + +static int spmi_haptics_play_effect(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct spmi_haptics *haptics = input_get_drvdata(dev); + uint32_t magnitude; + + dev_dbg(haptics->dev, "%s: Rumbling with strong: %d and weak: %d", + __func__, effect->u.rumble.strong_magnitude, + effect->u.rumble.weak_magnitude); + + magnitude = effect->u.rumble.strong_magnitude >> 8; + if (!magnitude) + magnitude = effect->u.rumble.weak_magnitude >> 10; + + if (!magnitude) + haptics->vmax = 0; + else + haptics->vmax = + ((HAP_VMAX_MAX_MV - HAP_VMAX_MIN_MV) * magnitude) / + 100 + + HAP_VMAX_MIN_MV; + + schedule_work(&haptics->work); + + return 0; +} + +/** + * spmi_haptics_close - callback for input device close + * @dev: input device pointer + * + * Turns off the vibrator. + */ +static void spmi_haptics_close(struct input_dev *dev) +{ + struct spmi_haptics *haptics = input_get_drvdata(dev); + + cancel_work_sync(&haptics->work); + haptics->vmax = 0; + + if (is_haptics_module_enabled(haptics)) + haptics_stop_vibrate(haptics); +} + +static int haptics_write_brake_pattern(struct spmi_haptics *haptics, + const uint8_t *brake_pattern) +{ + int ret, i; + uint8_t val = 0; + + for (i = HAP_BRAKE_PAT_LEN - 1; i >= 0; i--) + val |= FIELD_PREP(HAP_BRAKE_PAT_MASK, brake_pattern[i]) + << (i * 2); + + ret = regmap_update_bits(haptics->regmap, haptics->base + HAP_BRAKE_REG, + 0xff, val); + + return ret ?: + regmap_update_bits(haptics->regmap, + haptics->base + HAP_EN_CTL2_REG, + BRAKE_EN_BIT, BRAKE_EN_BIT); +} + +static int haptics_init(struct spmi_haptics *haptics) +{ + int ret; + uint8_t val, mask; + uint16_t play_rate; + + ret = regmap_update_bits(haptics->regmap, haptics->base + HAP_CFG1_REG, + HAP_ACT_TYPE_MASK, haptics->actuator_type); + if (ret) + return ret; + + /* + * Configure auto resonance + * see qpnp_haptics_lra_auto_res_config downstream + * This is greatly simplified. + */ + val = FIELD_PREP(LRA_RES_CAL_MASK, ilog2(32 / HAP_RES_CAL_PERIOD_MIN)) | + FIELD_PREP(LRA_AUTO_RES_MODE_MASK, HAP_AUTO_RES_ZXD_EOP) | + FIELD_PREP(LRA_HIGH_Z_MASK, 1); + mask = LRA_AUTO_RES_MODE_MASK | LRA_HIGH_Z_MASK | LRA_RES_CAL_MASK; + + ret = regmap_update_bits(haptics->regmap, + haptics->base + HAP_LRA_AUTO_RES_REG, mask, + val); + if (ret) + return ret; + + val = FIELD_PREP(HAP_WF_SOURCE_MASK, haptics->play_mode); + ret = regmap_update_bits(haptics->regmap, haptics->base + HAP_SEL_REG, + HAP_WF_SOURCE_MASK, val); + if (ret) + return ret; + + ret = regmap_update_bits(haptics->regmap, + haptics->base + HAP_ILIM_CFG_REG, + HAP_ILIM_SEL_MASK, haptics->current_limit); + if (ret) + return ret; + + /* Configure the debounce for short-circuit detection. */ + ret = regmap_update_bits(haptics->regmap, + haptics->base + HAP_SC_DEB_REG, + HAP_SC_DEB_MASK, HAP_SC_DEB_CYCLES_MAX); + if (ret) + return ret; + + ret = regmap_update_bits(haptics->regmap, haptics->base + HAP_CFG2_REG, + HAP_LRA_RES_TYPE_MASK, haptics->wave_shape); + if (ret) + return ret; + + /* + * Configure RATE_CFG1 and RATE_CFG2 registers. + * Note: For ERM (unsupported) these registers act as play rate and + * for LRA these represent resonance period + */ + play_rate = haptics->play_wave_rate / HAP_RATE_CFG_STEP_US; + val = FIELD_PREP(HAP_RATE_CFG1_MASK, play_rate); + ret = regmap_update_bits(haptics->regmap, + haptics->base + HAP_RATE_CFG1_REG, + HAP_RATE_CFG1_MASK, val); + val = FIELD_PREP(HAP_RATE_CFG2_MASK, play_rate >> 8); + ret = ret ?: + regmap_update_bits(haptics->regmap, + haptics->base + HAP_RATE_CFG2_REG, + HAP_RATE_CFG1_MASK, val); + if (ret) + return ret; + + ret = haptics_write_brake_pattern(haptics, default_brake_pattern); + if (ret) + return ret; + + /* Currently this is the only supported play mode */ + if (haptics->play_mode == HAP_PLAY_BUFFER) { + /* zero repeats and zero sample repeats */ + val = FIELD_PREP(WF_REPEAT_MASK, 0) | + FIELD_PREP(WF_S_REPEAT_MASK, 0); + ret = regmap_update_bits(haptics->regmap, + haptics->base + HAP_WF_REPEAT_REG, + WF_REPEAT_MASK | WF_S_REPEAT_MASK, + val); + if (ret) + return ret; + + ret = regmap_bulk_write(haptics->regmap, + haptics->base + HAP_WF_S1_REG, + wave_sample_pattern, HAP_WAVE_SAMP_LEN); + if (ret) + return ret; + } + + return 0; +} + +static int spmi_haptics_probe(struct platform_device *pdev) +{ + struct spmi_haptics *haptics; + struct input_dev *input_dev; + int ret, irq; + + haptics = devm_kzalloc(&pdev->dev, sizeof(*haptics), GFP_KERNEL); + if (!haptics) + return -ENOMEM; + + haptics->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!haptics->regmap) + return -ENODEV; + + haptics->dev = &pdev->dev; + + platform_set_drvdata(pdev, haptics); + + ret = device_property_read_u32(haptics->dev, "reg", &haptics->base); + if (ret) + return dev_err_probe(haptics->dev, ret, + "Couldn't read base address"); + + /* This is the only currently supported configuration, these values + * are left to allow future additions + */ + haptics->actuator_type = HAP_TYPE_LRA; + haptics->play_mode = HAP_PLAY_BUFFER; + haptics->wave_shape = HAP_WAVE_SINE; + haptics->current_limit = HAP_ILIM_400_MA; + + ret = device_property_read_u32(haptics->dev, "qcom,wave-play-rate-us", + &haptics->play_wave_rate); + if (ret) + return dev_err_probe(haptics->dev, ret, + "qcom,wave-play-rate-us is required\n"); + + INIT_WORK(&haptics->work, haptics_play_stop_work); + + ret = haptics_init(haptics); + if (ret) + return ret; + + input_dev = devm_input_allocate_device(&pdev->dev); + if (!input_dev) + return -ENOMEM; + + input_dev->name = "spmi_haptics"; + input_dev->id.version = 1; + input_dev->close = spmi_haptics_close; + input_set_drvdata(input_dev, haptics); + + haptics->input = input_dev; + + /* In the future this should become FF_PERIODIC */ + input_set_capability(haptics->input, EV_FF, FF_RUMBLE); + + ret = input_ff_create_memless(input_dev, NULL, + spmi_haptics_play_effect); + if (ret) + return dev_err_probe( + &pdev->dev, ret, + "Couldn't register haptics as EV_FF device\n"); + + ret = input_register_device(input_dev); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Couldn't register input device\n"); + + /* NOTE: the play IRQ is only used for buffer mode */ + irq = platform_get_irq_byname(pdev, "play"); + if (irq < 0) { + return dev_err_probe(&pdev->dev, irq, + "Unable to get play irq\n"); + } + ret = devm_request_threaded_irq(haptics->dev, irq, NULL, + haptics_play_irq, IRQF_ONESHOT, + "haptics_play_irq", haptics); + if (ret) + return dev_err_probe(haptics->dev, ret, + "Couldn't request play irq\n"); + + irq = platform_get_irq_byname(pdev, "short"); + if (irq < 0) + return dev_err_probe(&pdev->dev, irq, + "Unable to get short circut irq\n"); + ret = devm_request_threaded_irq(haptics->dev, irq, NULL, haptics_sc_irq, + IRQF_ONESHOT, "haptics_short_irq", + haptics); + if (ret) + return dev_err_probe(haptics->dev, ret, + "Couldn't request short circuit irq\n"); + + mutex_init(&haptics->play_lock); + + return 0; +} + +static int __maybe_unused spmi_haptics_suspend(struct device *dev) +{ + struct spmi_haptics *haptics = dev_get_drvdata(dev); + + cancel_work_sync(&haptics->work); + haptics_stop_vibrate(haptics); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(spmi_haptics_pm_ops, spmi_haptics_suspend, NULL); + +static int spmi_haptics_remove(struct platform_device *pdev) +{ + struct spmi_haptics *haptics = dev_get_drvdata(&pdev->dev); + + cancel_work_sync(&haptics->work); + mutex_destroy(&haptics->play_lock); + input_unregister_device(haptics->input); + + return 0; +} + +static void spmi_haptics_shutdown(struct platform_device *pdev) +{ + struct spmi_haptics *haptics = dev_get_drvdata(&pdev->dev); + + cancel_work_sync(&haptics->work); + haptics_stop_vibrate(haptics); +} + +static const struct of_device_id spmi_haptics_match_table[] = { + { .compatible = "qcom,pmi8998-haptics" }, + {} +}; +MODULE_DEVICE_TABLE(of, spmi_haptics_match_table); + +static struct platform_driver spmi_haptics_driver = { + .probe = spmi_haptics_probe, + .remove = spmi_haptics_remove, + .shutdown = spmi_haptics_shutdown, + .driver = { + .name = "spmi-haptics", + .pm = &spmi_haptics_pm_ops, + .of_match_table = spmi_haptics_match_table, + }, +}; +module_platform_driver(spmi_haptics_driver); + +MODULE_DESCRIPTION("spmi haptics driver using ff-memless framework"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Caleb Connolly "); From e5788ea5dd71b6ebc6e61887bae473d29b50e733 Mon Sep 17 00:00:00 2001 From: Jami Kettunen Date: Fri, 28 Jan 2022 16:12:04 +0200 Subject: [PATCH 0301/1823] [ANNOTATION] Import SPMI haptics driver v7 (2022-10-18) Link: https://patchwork.kernel.org/project/linux-arm-msm/cover/20221015172915.1436236-1-caleb@connolly.tech/ From 15864e39c071839863947d7b5d438b4a4cc33312 Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Sat, 8 Oct 2022 19:37:51 +0100 Subject: [PATCH 0302/1823] power: supply: add Qualcomm PMI8998 SMB2 Charger driver Add a driver for the SMB2 charger block found in the Qualcomm PMI8998 and PM660. This driver adds initial support for detecting USB cables and managing basic battery charging. Signed-off-by: Caleb Connolly --- drivers/power/supply/Kconfig | 16 + drivers/power/supply/Makefile | 1 + drivers/power/supply/qcom_pmi8998_charger.c | 1082 +++++++++++++++++++ 3 files changed, 1099 insertions(+) create mode 100644 drivers/power/supply/qcom_pmi8998_charger.c diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 0bbfe6a7ce4d..b4c39d16bed9 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -918,4 +918,20 @@ config BATTERY_UG3105 device is off or suspended, the functionality of this driver is limited to reporting capacity only. +config CHARGER_QCOM_SMB2 + tristate "Qualcomm PMI8998 PMIC charger driver" + depends on MFD_SPMI_PMIC + depends on IIO + select QCOM_SPMI_RRADC + help + Driver for the "SMB2" charger block found on Qualcomm PMI8998 + and PM660 PMICs. + + The charger features automatic power source detection via BC1.2, + automatic input current limiting and reports the realtime voltage + and current via the Round Robin ADC. + + Say Y or M here to enable reporting the charger status and rate on + supported platforms such as Snapdragon 845 and 835 based phones. + endif # POWER_SUPPLY diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index 0ee8653e882e..edfaa4c08c20 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -83,6 +83,7 @@ obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o obj-$(CONFIG_CHARGER_MP2629) += mp2629_charger.o obj-$(CONFIG_CHARGER_MT6360) += mt6360_charger.o obj-$(CONFIG_CHARGER_MT6370) += mt6370-charger.o +obj-$(CONFIG_CHARGER_QCOM_SMB2) += qcom_pmi8998_charger.o obj-$(CONFIG_CHARGER_QCOM_SMBB) += qcom_smbb.o obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o obj-$(CONFIG_CHARGER_BQ24190) += bq24190_charger.o diff --git a/drivers/power/supply/qcom_pmi8998_charger.c b/drivers/power/supply/qcom_pmi8998_charger.c new file mode 100644 index 000000000000..7e127198ea44 --- /dev/null +++ b/drivers/power/supply/qcom_pmi8998_charger.c @@ -0,0 +1,1082 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2021, Linaro Ltd. + * Author: Caleb Connolly + * + * This driver is for the switch-mode battery charger and boost + * hardware found in pmi8998 and related PMICs. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +// clang-format off +#define BATTERY_CHARGER_STATUS_1 0x06 +#define BVR_INITIAL_RAMP_BIT BIT(7) +#define CC_SOFT_TERMINATE_BIT BIT(6) +#define STEP_CHARGING_STATUS_SHIFT 3 +#define STEP_CHARGING_STATUS_MASK GENMASK(5, 3) +#define BATTERY_CHARGER_STATUS_MASK GENMASK(2, 0) + +#define BATTERY_CHARGER_STATUS_2 0x07 +#define INPUT_CURRENT_LIMITED_BIT BIT(7) +#define CHARGER_ERROR_STATUS_SFT_EXPIRE_BIT BIT(6) +#define CHARGER_ERROR_STATUS_BAT_OV_BIT BIT(5) +#define CHARGER_ERROR_STATUS_BAT_TERM_MISSING_BIT BIT(4) +#define BAT_TEMP_STATUS_MASK GENMASK(3, 0) +#define BAT_TEMP_STATUS_SOFT_LIMIT_MASK GENMASK(3, 2) +#define BAT_TEMP_STATUS_HOT_SOFT_LIMIT_BIT BIT(3) +#define BAT_TEMP_STATUS_COLD_SOFT_LIMIT_BIT BIT(2) +#define BAT_TEMP_STATUS_TOO_HOT_BIT BIT(1) +#define BAT_TEMP_STATUS_TOO_COLD_BIT BIT(0) + +#define BATTERY_CHARGER_STATUS_4 0x0A +#define CHARGE_CURRENT_POST_JEITA_MASK GENMASK(7, 0) + +#define BATTERY_CHARGER_STATUS_7 0x0D +#define ENABLE_TRICKLE_BIT BIT(7) +#define ENABLE_PRE_CHARGING_BIT BIT(6) +#define ENABLE_FAST_CHARGING_BIT BIT(5) +#define ENABLE_FULLON_MODE_BIT BIT(4) +#define TOO_COLD_ADC_BIT BIT(3) +#define TOO_HOT_ADC_BIT BIT(2) +#define HOT_SL_ADC_BIT BIT(1) +#define COLD_SL_ADC_BIT BIT(0) + +#define CHARGING_ENABLE_CMD 0x42 +#define CHARGING_ENABLE_CMD_BIT BIT(0) + +#define CHGR_CFG2 0x51 +#define CHG_EN_SRC_BIT BIT(7) +#define CHG_EN_POLARITY_BIT BIT(6) +#define PRETOFAST_TRANSITION_CFG_BIT BIT(5) +#define BAT_OV_ECC_BIT BIT(4) +#define I_TERM_BIT BIT(3) +#define AUTO_RECHG_BIT BIT(2) +#define EN_ANALOG_DROP_IN_VBATT_BIT BIT(1) +#define CHARGER_INHIBIT_BIT BIT(0) + +#define PRE_CHARGE_CURRENT_CFG 0x60 +#define PRE_CHARGE_CURRENT_SETTING_MASK GENMASK(5, 0) + +#define FAST_CHARGE_CURRENT_CFG 0x61 +#define FAST_CHARGE_CURRENT_SETTING_MASK GENMASK(7, 0) + +#define FLOAT_VOLTAGE_CFG 0x70 +#define FLOAT_VOLTAGE_SETTING_MASK GENMASK(7, 0) + +#define FG_UPDATE_CFG_2_SEL 0x7D +#define SOC_LT_OTG_THRESH_SEL_BIT BIT(3) +#define SOC_LT_CHG_RECHARGE_THRESH_SEL_BIT BIT(2) +#define VBT_LT_CHG_RECHARGE_THRESH_SEL_BIT BIT(1) +#define IBT_LT_CHG_TERM_THRESH_SEL_BIT BIT(0) + +#define JEITA_EN_CFG 0x90 +#define JEITA_EN_HARDLIMIT_BIT BIT(4) +#define JEITA_EN_HOT_SL_FCV_BIT BIT(3) +#define JEITA_EN_COLD_SL_FCV_BIT BIT(2) +#define JEITA_EN_HOT_SL_CCC_BIT BIT(1) +#define JEITA_EN_COLD_SL_CCC_BIT BIT(0) + +#define INT_RT_STS 0x310 +#define TYPE_C_CHANGE_RT_STS_BIT BIT(7) +#define USBIN_ICL_CHANGE_RT_STS_BIT BIT(6) +#define USBIN_SOURCE_CHANGE_RT_STS_BIT BIT(5) +#define USBIN_PLUGIN_RT_STS_BIT BIT(4) +#define USBIN_OV_RT_STS_BIT BIT(3) +#define USBIN_UV_RT_STS_BIT BIT(2) +#define USBIN_LT_3P6V_RT_STS_BIT BIT(1) +#define USBIN_COLLAPSE_RT_STS_BIT BIT(0) + +#define OTG_CFG 0x153 +#define OTG_RESERVED_MASK GENMASK(7, 6) +#define DIS_OTG_ON_TLIM_BIT BIT(5) +#define QUICKSTART_OTG_FASTROLESWAP_BIT BIT(4) +#define INCREASE_DFP_TIME_BIT BIT(3) +#define ENABLE_OTG_IN_DEBUG_MODE_BIT BIT(2) +#define OTG_EN_SRC_CFG_BIT BIT(1) +#define CONCURRENT_MODE_CFG_BIT BIT(0) + +#define OTG_ENG_OTG_CFG 0x1C0 +#define ENG_BUCKBOOST_HALT1_8_MODE_BIT BIT(0) + +#define APSD_STATUS 0x307 +#define APSD_STATUS_7_BIT BIT(7) +#define HVDCP_CHECK_TIMEOUT_BIT BIT(6) +#define SLOW_PLUGIN_TIMEOUT_BIT BIT(5) +#define ENUMERATION_DONE_BIT BIT(4) +#define VADP_CHANGE_DONE_AFTER_AUTH_BIT BIT(3) +#define QC_AUTH_DONE_STATUS_BIT BIT(2) +#define QC_CHARGER_BIT BIT(1) +#define APSD_DTC_STATUS_DONE_BIT BIT(0) + +#define APSD_RESULT_STATUS 0x308 +#define ICL_OVERRIDE_LATCH_BIT BIT(7) +#define APSD_RESULT_STATUS_MASK GENMASK(6, 0) +#define QC_3P0_BIT BIT(6) +#define QC_2P0_BIT BIT(5) +#define FLOAT_CHARGER_BIT BIT(4) +#define DCP_CHARGER_BIT BIT(3) +#define CDP_CHARGER_BIT BIT(2) +#define OCP_CHARGER_BIT BIT(1) +#define SDP_CHARGER_BIT BIT(0) + +#define TYPE_C_STATUS_1 0x30B +#define UFP_TYPEC_MASK GENMASK(7, 5) +#define UFP_TYPEC_RDSTD_BIT BIT(7) +#define UFP_TYPEC_RD1P5_BIT BIT(6) +#define UFP_TYPEC_RD3P0_BIT BIT(5) +#define UFP_TYPEC_FMB_255K_BIT BIT(4) +#define UFP_TYPEC_FMB_301K_BIT BIT(3) +#define UFP_TYPEC_FMB_523K_BIT BIT(2) +#define UFP_TYPEC_FMB_619K_BIT BIT(1) +#define UFP_TYPEC_OPEN_OPEN_BIT BIT(0) + +#define TYPE_C_STATUS_2 0x30C +#define DFP_RA_OPEN_BIT BIT(7) +#define TIMER_STAGE_BIT BIT(6) +#define EXIT_UFP_MODE_BIT BIT(5) +#define EXIT_DFP_MODE_BIT BIT(4) +#define DFP_TYPEC_MASK GENMASK(3, 0) +#define DFP_RD_OPEN_BIT BIT(3) +#define DFP_RD_RA_VCONN_BIT BIT(2) +#define DFP_RD_RD_BIT BIT(1) +#define DFP_RA_RA_BIT BIT(0) + +#define TYPE_C_STATUS_3 0x30D +#define ENABLE_BANDGAP_BIT BIT(7) +#define U_USB_GND_NOVBUS_BIT BIT(6) +#define U_USB_FLOAT_NOVBUS_BIT BIT(5) +#define U_USB_GND_BIT BIT(4) +#define U_USB_FMB1_BIT BIT(3) +#define U_USB_FLOAT1_BIT BIT(2) +#define U_USB_FMB2_BIT BIT(1) +#define U_USB_FLOAT2_BIT BIT(0) + +#define TYPE_C_STATUS_4 0x30E +#define UFP_DFP_MODE_STATUS_BIT BIT(7) +#define TYPEC_VBUS_STATUS_BIT BIT(6) +#define TYPEC_VBUS_ERROR_STATUS_BIT BIT(5) +#define TYPEC_DEBOUNCE_DONE_STATUS_BIT BIT(4) +#define TYPEC_UFP_AUDIO_ADAPT_STATUS_BIT BIT(3) +#define TYPEC_VCONN_OVERCURR_STATUS_BIT BIT(2) +#define CC_ORIENTATION_BIT BIT(1) +#define CC_ATTACHED_BIT BIT(0) + +#define TYPE_C_STATUS_5 0x30F +#define TRY_SOURCE_FAILED_BIT BIT(6) +#define TRY_SINK_FAILED_BIT BIT(5) +#define TIMER_STAGE_2_BIT BIT(4) +#define TYPEC_LEGACY_CABLE_STATUS_BIT BIT(3) +#define TYPEC_NONCOMP_LEGACY_CABLE_STATUS_BIT BIT(2) +#define TYPEC_TRYSOURCE_DETECT_STATUS_BIT BIT(1) +#define TYPEC_TRYSINK_DETECT_STATUS_BIT BIT(0) + +#define CMD_APSD 0x341 +#define ICL_OVERRIDE_BIT BIT(1) +#define APSD_RERUN_BIT BIT(0) + +#define TYPE_C_CFG 0x358 +#define APSD_START_ON_CC_BIT BIT(7) +#define WAIT_FOR_APSD_BIT BIT(6) +#define FACTORY_MODE_DETECTION_EN_BIT BIT(5) +#define FACTORY_MODE_ICL_3A_4A_BIT BIT(4) +#define FACTORY_MODE_DIS_CHGING_CFG_BIT BIT(3) +#define SUSPEND_NON_COMPLIANT_CFG_BIT BIT(2) +#define VCONN_OC_CFG_BIT BIT(1) +#define TYPE_C_OR_U_USB_BIT BIT(0) + +#define TYPE_C_CFG_2 0x359 +#define TYPE_C_DFP_CURRSRC_MODE_BIT BIT(7) +#define DFP_CC_1P4V_OR_1P6V_BIT BIT(6) +#define VCONN_SOFTSTART_CFG_MASK GENMASK(5, 4) +#define EN_TRY_SOURCE_MODE_BIT BIT(3) +#define USB_FACTORY_MODE_ENABLE_BIT BIT(2) +#define TYPE_C_UFP_MODE_BIT BIT(1) +#define EN_80UA_180UA_CUR_SOURCE_BIT BIT(0) + +#define TYPE_C_CFG_3 0x35A +#define TVBUS_DEBOUNCE_BIT BIT(7) +#define TYPEC_LEGACY_CABLE_INT_EN_BIT BIT(6) +#define TYPEC_NONCOMPLIANT_LEGACY_CABLE_INT_EN_B BIT(5) +#define TYPEC_TRYSOURCE_DETECT_INT_EN_BIT BIT(4) +#define TYPEC_TRYSINK_DETECT_INT_EN_BIT BIT(3) +#define EN_TRYSINK_MODE_BIT BIT(2) +#define EN_LEGACY_CABLE_DETECTION_BIT BIT(1) +#define ALLOW_PD_DRING_UFP_TCCDB_BIT BIT(0) + +#define USBIN_OPTIONS_1_CFG 0x362 +#define CABLE_R_SEL_BIT BIT(7) +#define HVDCP_AUTH_ALG_EN_CFG_BIT BIT(6) +#define HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT BIT(5) +#define INPUT_PRIORITY_BIT BIT(4) +#define AUTO_SRC_DETECT_BIT BIT(3) +#define HVDCP_EN_BIT BIT(2) +#define VADP_INCREMENT_VOLTAGE_LIMIT_BIT BIT(1) +#define VADP_TAPER_TIMER_EN_BIT BIT(0) + +#define USBIN_OPTIONS_2_CFG 0x363 +#define WIPWR_RST_EUD_CFG_BIT BIT(7) +#define SWITCHER_START_CFG_BIT BIT(6) +#define DCD_TIMEOUT_SEL_BIT BIT(5) +#define OCD_CURRENT_SEL_BIT BIT(4) +#define SLOW_PLUGIN_TIMER_EN_CFG_BIT BIT(3) +#define FLOAT_OPTIONS_MASK GENMASK(2, 0) +#define FLOAT_DIS_CHGING_CFG_BIT BIT(2) +#define SUSPEND_FLOAT_CFG_BIT BIT(1) +#define FORCE_FLOAT_SDP_CFG_BIT BIT(0) + +#define TAPER_TIMER_SEL_CFG 0x364 +#define TYPEC_SPARE_CFG_BIT BIT(7) +#define TYPEC_DRP_DFP_TIME_CFG_BIT BIT(5) +#define TAPER_TIMER_SEL_MASK GENMASK(1, 0) + +#define USBIN_LOAD_CFG 0x365 +#define USBIN_OV_CH_LOAD_OPTION_BIT BIT(7) +#define ICL_OVERRIDE_AFTER_APSD_BIT BIT(4) + +#define USBIN_ICL_OPTIONS 0x366 +#define CFG_USB3P0_SEL_BIT BIT(2) +#define USB51_MODE_BIT BIT(1) +#define USBIN_MODE_CHG_BIT BIT(0) + +#define TYPE_C_INTRPT_ENB_SOFTWARE_CTRL 0x368 +#define EXIT_SNK_BASED_ON_CC_BIT BIT(7) +#define VCONN_EN_ORIENTATION_BIT BIT(6) +#define TYPEC_VCONN_OVERCURR_INT_EN_BIT BIT(5) +#define VCONN_EN_SRC_BIT BIT(4) +#define VCONN_EN_VALUE_BIT BIT(3) +#define TYPEC_POWER_ROLE_CMD_MASK GENMASK(2, 0) +#define UFP_EN_CMD_BIT BIT(2) +#define DFP_EN_CMD_BIT BIT(1) +#define TYPEC_DISABLE_CMD_BIT BIT(0) + +#define USBIN_CURRENT_LIMIT_CFG 0x370 +#define USBIN_CURRENT_LIMIT_MASK GENMASK(7, 0) + +#define USBIN_AICL_OPTIONS_CFG 0x380 +#define SUSPEND_ON_COLLAPSE_USBIN_BIT BIT(7) +#define USBIN_AICL_HDC_EN_BIT BIT(6) +#define USBIN_AICL_START_AT_MAX_BIT BIT(5) +#define USBIN_AICL_RERUN_EN_BIT BIT(4) +#define USBIN_AICL_ADC_EN_BIT BIT(3) +#define USBIN_AICL_EN_BIT BIT(2) +#define USBIN_HV_COLLAPSE_RESPONSE_BIT BIT(1) +#define USBIN_LV_COLLAPSE_RESPONSE_BIT BIT(0) + +#define USBIN_5V_AICL_THRESHOLD_CFG 0x381 +#define USBIN_5V_AICL_THRESHOLD_CFG_MASK GENMASK(2, 0) + +#define USBIN_CONT_AICL_THRESHOLD_CFG 0x384 +#define USBIN_CONT_AICL_THRESHOLD_CFG_MASK GENMASK(5, 0) + +#define DC_ENG_SSUPPLY_CFG2 0x4C1 +#define ENG_SSUPPLY_IVREF_OTG_SS_MASK GENMASK(2, 0) +#define OTG_SS_SLOW 0x3 + +#define DCIN_AICL_REF_SEL_CFG 0x481 +#define DCIN_CONT_AICL_THRESHOLD_CFG_MASK GENMASK(5, 0) + +#define WI_PWR_OPTIONS 0x495 +#define CHG_OK_BIT BIT(7) +#define WIPWR_UVLO_IRQ_OPT_BIT BIT(6) +#define BUCK_HOLDOFF_ENABLE_BIT BIT(5) +#define CHG_OK_HW_SW_SELECT_BIT BIT(4) +#define WIPWR_RST_ENABLE_BIT BIT(3) +#define DCIN_WIPWR_IRQ_SELECT_BIT BIT(2) +#define AICL_SWITCH_ENABLE_BIT BIT(1) +#define ZIN_ICL_ENABLE_BIT BIT(0) + +#define ICL_STATUS 0x607 +#define INPUT_CURRENT_LIMIT_MASK GENMASK(7, 0) + +#define POWER_PATH_STATUS 0x60B +#define P_PATH_INPUT_SS_DONE_BIT BIT(7) +#define P_PATH_USBIN_SUSPEND_STS_BIT BIT(6) +#define P_PATH_DCIN_SUSPEND_STS_BIT BIT(5) +#define P_PATH_USE_USBIN_BIT BIT(4) +#define P_PATH_USE_DCIN_BIT BIT(3) +#define P_PATH_POWER_PATH_MASK GENMASK(2, 1) +#define P_PATH_VALID_INPUT_POWER_SOURCE_STS_BIT BIT(0) + +#define BARK_BITE_WDOG_PET 0x643 +#define BARK_BITE_WDOG_PET_BIT BIT(0) + +#define WD_CFG 0x651 +#define WATCHDOG_TRIGGER_AFP_EN_BIT BIT(7) +#define BARK_WDOG_INT_EN_BIT BIT(6) +#define BITE_WDOG_INT_EN_BIT BIT(5) +#define SFT_AFTER_WDOG_IRQ_MASK GENMASK(4, 3) +#define WDOG_IRQ_SFT_BIT BIT(2) +#define WDOG_TIMER_EN_ON_PLUGIN_BIT BIT(1) +#define WDOG_TIMER_EN_BIT BIT(0) + +#define SNARL_BARK_BITE_WD_CFG 0x653 +#define BITE_WDOG_DISABLE_CHARGING_CFG_BIT BIT(7) +#define SNARL_WDOG_TIMEOUT_MASK GENMASK(6, 4) +#define BARK_WDOG_TIMEOUT_MASK GENMASK(3, 2) +#define BITE_WDOG_TIMEOUT_MASK GENMASK(1, 0) + +#define AICL_RERUN_TIME_CFG 0x661 +#define AICL_RERUN_TIME_MASK GENMASK(1, 0) + +#define STAT_CFG 0x690 +#define STAT_SW_OVERRIDE_VALUE_BIT BIT(7) +#define STAT_SW_OVERRIDE_CFG_BIT BIT(6) +#define STAT_PARALLEL_OFF_DG_CFG_MASK GENMASK(5, 4) +#define STAT_POLARITY_CFG_BIT BIT(3) +#define STAT_PARALLEL_CFG_BIT BIT(2) +#define STAT_FUNCTION_CFG_BIT BIT(1) +#define STAT_IRQ_PULSING_EN_BIT BIT(0) + +#define USBIN_CURRENT_25MA 25000 +#define USBIN_CURRENT_100MA 100000 +#define USBIN_CURRENT_150MA 150000 +#define USBIN_CURRENT_500MA 500000 +#define USBIN_CURRENT_900MA 900000 +#define SDP_CURRENT_UA 500000 +#define CDP_CURRENT_UA 1500000 +#define DCP_CURRENT_UA 1500000 +#define CURRENT_MAX_UA DCP_CURRENT_UA +// clang-format on + +enum charger_status { + TRICKLE_CHARGE = 0, + PRE_CHARGE, + FAST_CHARGE, + FULLON_CHARGE, + TAPER_CHARGE, + TERMINATE_CHARGE, + INHIBIT_CHARGE, + DISABLE_CHARGE, +}; + +struct smb2_register { + u16 addr; + u8 mask; + u8 val; +}; + +/** + * struct smb2_chip - smb2 chip structure + * @dev: Device reference for power_supply + * @name: The platform device name + * @base: Base address for smb2 registers + * @regmap: Register map + * @batt_info: Battery data from DT + * @status_change_work: Worker to handle plug/unplug events + * @cable_irq: USB plugin IRQ + * @wakeup_enabled: If the cable IRQ will cause a wakeup + * @usb_in_i_chan: USB_IN current measurement channel + * @usb_in_v_chan: USB_IN voltage measurement channel + * @chg_psy: Charger power supply instance + * @default_curr_limit: The default current limit from the APSD, used + * for CHARGE_CONTROL_LIMIT_MAX + */ +struct smb2_chip { + struct device *dev; + const char *name; + unsigned int base; + struct regmap *regmap; + struct power_supply_battery_info *batt_info; + + struct delayed_work status_change_work; + int cable_irq; + bool wakeup_enabled; + + struct iio_channel *usb_in_i_chan; + struct iio_channel *usb_in_v_chan; + + struct power_supply *chg_psy; + long default_curr_limit; +}; + +static enum power_supply_property smb2_properties[] = { + POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_CURRENT_MAX, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_USB_TYPE, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, +}; + +static enum power_supply_usb_type smb2_usb_types[] = { + POWER_SUPPLY_USB_TYPE_UNKNOWN, + POWER_SUPPLY_USB_TYPE_SDP, + POWER_SUPPLY_USB_TYPE_DCP, + POWER_SUPPLY_USB_TYPE_CDP, +}; + +static int smb2_get_prop_usb_online(struct smb2_chip *chip, int *val) +{ + unsigned int stat; + int rc; + + rc = regmap_read(chip->regmap, chip->base + POWER_PATH_STATUS, &stat); + if (rc < 0) { + dev_err(chip->dev, "Couldn't read POWER_PATH_STATUS! ret=%d\n", + rc); + return rc; + } + + *val = (stat & P_PATH_USE_USBIN_BIT) && + (stat & P_PATH_VALID_INPUT_POWER_SOURCE_STS_BIT); + return 0; +} + +/* + * Qualcomm "automatic power source detection" aka APSD + * tells us what type of charger we're connected to. + */ +static int smb2_apsd_get_charger_type(struct smb2_chip *chip, int *val) +{ + int rc; + unsigned int apsd_stat, stat; + int usb_online; + + rc = smb2_get_prop_usb_online(chip, &usb_online); + if (rc < 0 || !usb_online) { + *val = POWER_SUPPLY_USB_TYPE_UNKNOWN; + return 0; + } + + rc = regmap_read(chip->regmap, chip->base + APSD_STATUS, &apsd_stat); + if (rc < 0) { + dev_err(chip->dev, "Failed to read apsd status, rc = %d", rc); + return rc; + } + if (!(apsd_stat & APSD_DTC_STATUS_DONE_BIT)) { + dev_dbg(chip->dev, "Apsd not ready"); + return -EAGAIN; + } + + rc = regmap_read(chip->regmap, chip->base + APSD_RESULT_STATUS, &stat); + if (rc < 0) { + dev_err(chip->dev, "Failed to read apsd result, rc = %d", rc); + return rc; + } + + stat &= APSD_RESULT_STATUS_MASK; + + if (stat & CDP_CHARGER_BIT) + *val = POWER_SUPPLY_USB_TYPE_CDP; + else if (stat & (DCP_CHARGER_BIT | OCP_CHARGER_BIT | FLOAT_CHARGER_BIT)) + *val = POWER_SUPPLY_USB_TYPE_DCP; + else /* SDP_CHARGER_BIT (or others) */ + *val = POWER_SUPPLY_USB_TYPE_SDP; + + return 0; +} + +static int smb2_get_prop_status(struct smb2_chip *chip, int *val) +{ + int usb_online_val; + unsigned char stat[2]; + int rc; + + rc = smb2_get_prop_usb_online(chip, &usb_online_val); + if (rc < 0) { + dev_err(chip->dev, "Couldn't get usb online property rc = %d\n", + rc); + return rc; + } + + if (!usb_online_val) { + *val = POWER_SUPPLY_STATUS_DISCHARGING; + return rc; + } + + rc = regmap_bulk_read(chip->regmap, + chip->base + BATTERY_CHARGER_STATUS_1, &stat, 2); + if (rc < 0) { + dev_err(chip->dev, "Failed to read charging status ret=%d\n", + rc); + return rc; + } + + if (stat[1] & CHARGER_ERROR_STATUS_BAT_OV_BIT) { + *val = POWER_SUPPLY_STATUS_NOT_CHARGING; + return 0; + } + + stat[0] = stat[0] & BATTERY_CHARGER_STATUS_MASK; + + switch (stat[0]) { + case TRICKLE_CHARGE: + case PRE_CHARGE: + case FAST_CHARGE: + case FULLON_CHARGE: + case TAPER_CHARGE: + *val = POWER_SUPPLY_STATUS_CHARGING; + return rc; + case DISABLE_CHARGE: + *val = POWER_SUPPLY_STATUS_NOT_CHARGING; + return rc; + case TERMINATE_CHARGE: + *val = POWER_SUPPLY_STATUS_FULL; + return rc; + case INHIBIT_CHARGE: + default: + *val = POWER_SUPPLY_STATUS_UNKNOWN; + return rc; + } +} + +static inline int smb2_get_current_limit(struct smb2_chip *chip, + unsigned int *val) +{ + int rc = regmap_read(chip->regmap, chip->base + ICL_STATUS, val); + + if (rc >= 0) + *val *= 25000; + return rc; +} + +static int smb2_set_current_limit(struct smb2_chip *chip, unsigned int val) +{ + unsigned char val_raw; + + if (val > 4800000) { + dev_err(chip->dev, + "Can't set current limit higher than 4800000uA"); + return -EINVAL; + } + val_raw = val / 25000; + + return regmap_write(chip->regmap, chip->base + USBIN_CURRENT_LIMIT_CFG, + val_raw); +} + +static void smb2_status_change_work(struct work_struct *work) +{ + struct smb2_chip *chip = + container_of(work, struct smb2_chip, status_change_work.work); + unsigned int charger_type, current_ua; + int usb_online, count, rc; + + smb2_get_prop_usb_online(chip, &usb_online); + if (usb_online == 0) { + chip->default_curr_limit = 0; + return; + } + + for (count = 0; count < 3; count++) { + dev_dbg(chip->dev, "get charger type retry %d\n", count); + rc = smb2_apsd_get_charger_type(chip, &charger_type); + if (rc == 0) + break; + msleep(100); + } + + if (rc < 0) { + rc = regmap_update_bits(chip->regmap, chip->base + CMD_APSD, + APSD_RERUN_BIT, APSD_RERUN_BIT); + schedule_delayed_work(&chip->status_change_work, + msecs_to_jiffies(1000)); + dev_dbg(chip->dev, "get charger type failed, rerun apsd\n"); + return; + } + + switch (charger_type) { + case POWER_SUPPLY_USB_TYPE_CDP: + current_ua = CDP_CURRENT_UA; + break; + case POWER_SUPPLY_USB_TYPE_DCP: + current_ua = DCP_CURRENT_UA; + break; + case POWER_SUPPLY_USB_TYPE_SDP: + default: + current_ua = SDP_CURRENT_UA; + break; + } + + chip->default_curr_limit = current_ua; + + smb2_set_current_limit(chip, current_ua); + power_supply_changed(chip->chg_psy); +} + +static int smb2_get_iio_chan(struct smb2_chip *chip, struct iio_channel *chan, + int *val) +{ + int rc; + union power_supply_propval status; + + rc = power_supply_get_property(chip->chg_psy, POWER_SUPPLY_PROP_STATUS, + &status); + if (rc < 0 || status.intval != POWER_SUPPLY_STATUS_CHARGING) { + *val = 0; + return 0; + } + + if (IS_ERR(chan)) { + dev_err(chip->dev, "Failed to chan, err = %li", PTR_ERR(chan)); + return PTR_ERR(chan); + } + + return iio_read_channel_processed(chan, val); +} + +static int smb2_get_prop_health(struct smb2_chip *chip, int *val) +{ + int rc; + unsigned int stat; + + rc = regmap_read(chip->regmap, chip->base + BATTERY_CHARGER_STATUS_2, + &stat); + if (rc < 0) { + dev_err(chip->dev, "Couldn't read charger status rc=%d\n", rc); + return rc; + } + + switch (stat) { + case CHARGER_ERROR_STATUS_BAT_OV_BIT: + *val = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + return 0; + case BAT_TEMP_STATUS_TOO_COLD_BIT: + *val = POWER_SUPPLY_HEALTH_COLD; + return 0; + case BAT_TEMP_STATUS_TOO_HOT_BIT: + *val = POWER_SUPPLY_HEALTH_OVERHEAT; + return 0; + case BAT_TEMP_STATUS_COLD_SOFT_LIMIT_BIT: + *val = POWER_SUPPLY_HEALTH_COOL; + return 0; + case BAT_TEMP_STATUS_HOT_SOFT_LIMIT_BIT: + *val = POWER_SUPPLY_HEALTH_WARM; + return 0; + default: + *val = POWER_SUPPLY_HEALTH_GOOD; + return 0; + } +} + +static int smb2_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct smb2_chip *chip = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = "Qualcomm"; + return 0; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = chip->name; + return 0; + case POWER_SUPPLY_PROP_CURRENT_MAX: + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + return smb2_get_current_limit(chip, &val->intval); + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX: + val->intval = chip->default_curr_limit; + return 0; + case POWER_SUPPLY_PROP_CURRENT_NOW: + return smb2_get_iio_chan(chip, chip->usb_in_i_chan, + &val->intval); + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + return smb2_get_iio_chan(chip, chip->usb_in_v_chan, + &val->intval); + case POWER_SUPPLY_PROP_ONLINE: + return smb2_get_prop_usb_online(chip, &val->intval); + case POWER_SUPPLY_PROP_STATUS: + return smb2_get_prop_status(chip, &val->intval); + case POWER_SUPPLY_PROP_HEALTH: + return smb2_get_prop_health(chip, &val->intval); + case POWER_SUPPLY_PROP_USB_TYPE: + return smb2_apsd_get_charger_type(chip, &val->intval); + default: + dev_err(chip->dev, "invalid property: %d\n", psp); + return -EINVAL; + } +} + +static int smb2_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct smb2_chip *chip = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_CURRENT_MAX: + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + return smb2_set_current_limit(chip, val->intval); + default: + dev_err(chip->dev, "No setter for property: %d\n", psp); + return -EINVAL; + } +} + +static int smb2_property_is_writable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_CURRENT_MAX: + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + return 1; + default: + return 0; + } +} + +static irqreturn_t smb2_handle_batt_overvoltage(int irq, void *data) +{ + struct smb2_chip *chip = data; + unsigned int status; + + regmap_read(chip->regmap, chip->base + BATTERY_CHARGER_STATUS_2, + &status); + + if (status & CHARGER_ERROR_STATUS_BAT_OV_BIT) { + /* The hardware stops charging automatically */ + dev_err(chip->dev, + "battery overvoltage detected, charging stopped\n"); + power_supply_changed(chip->chg_psy); + } + + return IRQ_HANDLED; +} + +static irqreturn_t smb2_handle_usb_plugin(int irq, void *data) +{ + struct smb2_chip *chip = data; + + power_supply_changed(chip->chg_psy); + + schedule_delayed_work(&chip->status_change_work, + msecs_to_jiffies(1500)); + + return IRQ_HANDLED; +} + +static irqreturn_t smb2_handle_usb_icl_change(int irq, void *data) +{ + struct smb2_chip *chip = data; + + power_supply_changed(chip->chg_psy); + + return IRQ_HANDLED; +} + +static irqreturn_t smb2_handle_wdog_bark(int irq, void *data) +{ + struct smb2_chip *chip = data; + int rc; + + power_supply_changed(chip->chg_psy); + + rc = regmap_write(chip->regmap, BARK_BITE_WDOG_PET, + BARK_BITE_WDOG_PET_BIT); + if (rc < 0) + dev_err(chip->dev, "Couldn't pet the dog rc=%d\n", rc); + + return IRQ_HANDLED; +} + +static const struct power_supply_desc smb2_psy_desc = { + .name = "pmi8998_charger", + .type = POWER_SUPPLY_TYPE_USB, + .usb_types = smb2_usb_types, + .num_usb_types = ARRAY_SIZE(smb2_usb_types), + .properties = smb2_properties, + .num_properties = ARRAY_SIZE(smb2_properties), + .get_property = smb2_get_property, + .set_property = smb2_set_property, + .property_is_writeable = smb2_property_is_writable, +}; + +/* Init sequence derived from vendor downstream driver */ +static const struct smb2_register smb2_init_seq[] = { + { .addr = AICL_RERUN_TIME_CFG, .mask = AICL_RERUN_TIME_MASK, .val = 0 }, + /* + * By default configure us as an upstream facing port + * NOTE: for OTG we should set UFP_EN_CMD_BIT and DFP_EN_CMD_BIT both + * to 0 + */ + { .addr = TYPE_C_INTRPT_ENB_SOFTWARE_CTRL, + .mask = TYPEC_POWER_ROLE_CMD_MASK | VCONN_EN_SRC_BIT | + VCONN_EN_VALUE_BIT, + .val = VCONN_EN_SRC_BIT }, + /* + * disable Type-C factory mode and stay in Attached.SRC state when VCONN + * over-current happens + */ + { .addr = TYPE_C_CFG, + .mask = FACTORY_MODE_DETECTION_EN_BIT | VCONN_OC_CFG_BIT, + .val = 0 }, + /* Configure VBUS for software control */ + { .addr = OTG_CFG, .mask = OTG_EN_SRC_CFG_BIT, .val = 0 }, + { .addr = FG_UPDATE_CFG_2_SEL, + .mask = SOC_LT_CHG_RECHARGE_THRESH_SEL_BIT | + VBT_LT_CHG_RECHARGE_THRESH_SEL_BIT, + .val = VBT_LT_CHG_RECHARGE_THRESH_SEL_BIT }, + /* Enable charging */ + { .addr = USBIN_OPTIONS_1_CFG, .mask = HVDCP_EN_BIT, .val = 0 }, + { .addr = CHARGING_ENABLE_CMD, + .mask = CHARGING_ENABLE_CMD_BIT, + .val = CHARGING_ENABLE_CMD_BIT }, + { .addr = CHGR_CFG2, + .mask = CHG_EN_SRC_BIT | CHG_EN_POLARITY_BIT | + PRETOFAST_TRANSITION_CFG_BIT | BAT_OV_ECC_BIT | I_TERM_BIT | + AUTO_RECHG_BIT | EN_ANALOG_DROP_IN_VBATT_BIT | + CHARGER_INHIBIT_BIT, + .val = 0 }, + /* + * No clue what this does + */ + { .addr = STAT_CFG, + .mask = STAT_SW_OVERRIDE_CFG_BIT, + .val = STAT_SW_OVERRIDE_CFG_BIT }, + /* + * Set the default SDP charger type to a 500ma USB 2.0 port + */ + { .addr = USBIN_ICL_OPTIONS, + .mask = USB51_MODE_BIT | USBIN_MODE_CHG_BIT, + .val = USB51_MODE_BIT }, + /* + * Disable watchdog + */ + { .addr = SNARL_BARK_BITE_WD_CFG, .mask = 0xff, .val = 0 }, + { .addr = WD_CFG, + .mask = WATCHDOG_TRIGGER_AFP_EN_BIT | WDOG_TIMER_EN_ON_PLUGIN_BIT | + BARK_WDOG_INT_EN_BIT, + .val = 0 }, + /* These bits aren't documented anywhere */ + { .addr = USBIN_5V_AICL_THRESHOLD_CFG, + .mask = USBIN_5V_AICL_THRESHOLD_CFG_MASK, + .val = 0x3 }, + { .addr = USBIN_CONT_AICL_THRESHOLD_CFG, + .mask = USBIN_CONT_AICL_THRESHOLD_CFG_MASK, + .val = 0x3 }, + { .addr = USBIN_LOAD_CFG, .mask = BIT(0) | BIT(1), .val = 0x3 }, + /* Enable Automatic Input Current Limit, this will slowly ramp up the current + * When connected to a wall charger, and automatically stop when it detects + * the charger current limit (voltage drop?) or it reaches the programmed limit. + */ + { .addr = USBIN_AICL_OPTIONS_CFG, + .mask = USBIN_AICL_START_AT_MAX_BIT | USBIN_AICL_ADC_EN_BIT | + USBIN_AICL_EN_BIT | SUSPEND_ON_COLLAPSE_USBIN_BIT | + USBIN_HV_COLLAPSE_RESPONSE_BIT | + USBIN_LV_COLLAPSE_RESPONSE_BIT, + .val = USBIN_HV_COLLAPSE_RESPONSE_BIT | + USBIN_LV_COLLAPSE_RESPONSE_BIT | USBIN_AICL_EN_BIT }, + /* + * Set pre charge current to default, the OnePlus 6 bootloader + * sets this very conservatively. + * NOTE: seems to be reset to zero again anyway after boot + */ + { .addr = PRE_CHARGE_CURRENT_CFG, + .mask = PRE_CHARGE_CURRENT_SETTING_MASK, + .val = 500000 / 25000 }, + /* + * This overrides all of the current limit options exposed to userspace + * and prevents the device from pulling more than 1A. This is done + * to avoid potential fire hazard risks. + */ + { .addr = FAST_CHARGE_CURRENT_CFG, + .mask = FAST_CHARGE_CURRENT_SETTING_MASK, + .val = 1000000 / 25000 }, +}; + +static int smb2_init_hw(struct smb2_chip *chip) +{ + int rc, i; + + for (i = 0; i < ARRAY_SIZE(smb2_init_seq); i++) { + dev_dbg(chip->dev, "%d: Writing 0x%02x to 0x%02x\n", i, + smb2_init_seq[i].val, smb2_init_seq[i].addr); + rc = regmap_update_bits(chip->regmap, + chip->base + smb2_init_seq[i].addr, + smb2_init_seq[i].mask, + smb2_init_seq[i].val); + if (rc < 0) + return dev_err_probe( + chip->dev, rc, + "%s: Failed to write 0x%02x to 0x%02x\n", + __func__, smb2_init_seq[i].val, + smb2_init_seq[i].addr); + } + + return 0; +} + +static int smb2_init_irq(struct smb2_chip *chip, int *irq, const char *name, + irqreturn_t (*handler)(int irq, void *data)) +{ + int rc; + + *irq = of_irq_get_byname(chip->dev->of_node, name); + if (*irq < 0) + return dev_err_probe(chip->dev, *irq, + "Couldn't get irq %s byname\n", name); + + rc = devm_request_threaded_irq(chip->dev, *irq, NULL, handler, + IRQF_ONESHOT, name, chip); + if (rc < 0) + return dev_err_probe(chip->dev, rc, "Couldn't request irq %s\n", + name); + + return 0; +} + +static int smb2_probe(struct platform_device *pdev) +{ + struct power_supply_config supply_config = {}; + struct smb2_chip *chip; + int rc, irq; + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = &pdev->dev; + chip->name = pdev->name; + + chip->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!chip->regmap) { + return dev_err_probe(chip->dev, -ENODEV, + "failed to locate the regmap\n"); + } + + rc = device_property_read_u32(chip->dev, "reg", &chip->base); + if (rc < 0) { + return dev_err_probe(chip->dev, rc, + "Couldn't read base address\n"); + } + + chip->usb_in_v_chan = devm_iio_channel_get(chip->dev, "usbin_v"); + if (IS_ERR(chip->usb_in_v_chan)) + return dev_err_probe( + chip->dev, PTR_ERR(chip->usb_in_v_chan), + "Couldn't get usbin_v IIO channel from RRADC\n"); + + chip->usb_in_i_chan = devm_iio_channel_get(chip->dev, "usbin_i"); + if (IS_ERR(chip->usb_in_i_chan)) { + return dev_err_probe( + chip->dev, PTR_ERR(chip->usb_in_i_chan), + "Couldn't get usbin_i IIO channel from RRADC\n"); + } + + rc = smb2_init_hw(chip); + if (rc < 0) + return rc; + + supply_config.drv_data = chip; + supply_config.of_node = pdev->dev.of_node; + + chip->chg_psy = devm_power_supply_register(chip->dev, &smb2_psy_desc, + &supply_config); + if (IS_ERR(chip->chg_psy)) + return dev_err_probe(chip->dev, PTR_ERR(chip->chg_psy), + "failed to register power supply\n"); + + rc = power_supply_get_battery_info(chip->chg_psy, &chip->batt_info); + if (rc) + return dev_err_probe(chip->dev, rc, + "Failed to get battery info\n"); + + rc = devm_delayed_work_autocancel(chip->dev, &chip->status_change_work, + smb2_status_change_work); + if (rc) + return dev_err_probe( + chip->dev, rc, + "Failed to initialise status change work\n"); + + rc = (chip->batt_info->voltage_max_design_uv - 3487500) / 7500 + 1; + rc = regmap_update_bits(chip->regmap, chip->base + FLOAT_VOLTAGE_CFG, + FLOAT_VOLTAGE_SETTING_MASK, rc); + if (rc < 0) + return dev_err_probe(chip->dev, rc, "Couldn't set vbat max\n"); + + rc = smb2_init_irq(chip, &irq, "bat-ov", smb2_handle_batt_overvoltage); + if (rc < 0) + return rc; + + rc = smb2_init_irq(chip, &chip->cable_irq, "usb-plugin", + smb2_handle_usb_plugin); + if (rc < 0) + return rc; + + rc = smb2_init_irq(chip, &irq, "usbin-icl-change", + smb2_handle_usb_icl_change); + if (rc < 0) + return rc; + rc = smb2_init_irq(chip, &irq, "wdog-bark", smb2_handle_wdog_bark); + if (rc < 0) + return rc; + + platform_set_drvdata(pdev, chip); + + device_init_wakeup(chip->dev, 1); + + /* Initialise charger state */ + schedule_delayed_work(&chip->status_change_work, 0); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int smb2_suspend(struct device *dev) +{ + struct smb2_chip *chip = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + chip->wakeup_enabled = !enable_irq_wake(chip->cable_irq); + + return 0; +} + +static int smb2_resume(struct device *dev) +{ + struct smb2_chip *chip = dev_get_drvdata(dev); + + if (device_may_wakeup(dev) && chip->wakeup_enabled) + disable_irq_wake(chip->cable_irq); + + power_supply_changed(chip->chg_psy); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(smb2_pm_ops, smb2_suspend, smb2_resume); + +static const struct of_device_id smb2_match_id_table[] = { + { .compatible = "qcom,pmi8998-charger" }, + { .compatible = "qcom,pm660-charger" }, + { /* sentinal */ } +}; +MODULE_DEVICE_TABLE(of, smb2_match_id_table); + +static struct platform_driver qcom_spmi_smb2 = { + .probe = smb2_probe, + .driver = { + .name = "qcom-pmi8998-charger", + .pm = &smb2_pm_ops, + .of_match_table = smb2_match_id_table, + }, +}; + +module_platform_driver(qcom_spmi_smb2); + +MODULE_AUTHOR("Caleb Connolly "); +MODULE_DESCRIPTION("Qualcomm SMB2 Charger Driver"); +MODULE_LICENSE("GPL"); From ea0538d0309a5f5f0ba332fe5047d8390d0320e2 Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Sat, 8 Oct 2022 19:37:52 +0100 Subject: [PATCH 0303/1823] dt-bindings: power: supply: qcom,pmi8998-charger: add bindings for smb2 driver Add devicetree bindings for the Qualcomm PMI8998/PM660 SMB2 charger driver. Signed-off-by: Caleb Connolly Reviewed-by: Krzysztof Kozlowski --- .../power/supply/qcom,pmi8998-charger.yaml | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 Documentation/devicetree/bindings/power/supply/qcom,pmi8998-charger.yaml diff --git a/Documentation/devicetree/bindings/power/supply/qcom,pmi8998-charger.yaml b/Documentation/devicetree/bindings/power/supply/qcom,pmi8998-charger.yaml new file mode 100644 index 000000000000..277c47e048b6 --- /dev/null +++ b/Documentation/devicetree/bindings/power/supply/qcom,pmi8998-charger.yaml @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/power/supply/qcom,pmi8998-charger.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm PMI8998/PM660 Switch-Mode Battery Charger "2" + +maintainers: + - Caleb Connolly + +properties: + compatible: + enum: + - qcom,pmi8998-charger + - qcom,pm660-charger + + reg: + maxItems: 1 + + interrupts: + maxItems: 4 + + interrupt-names: + items: + - const: usb-plugin + - const: bat-ov + - const: wdog-bark + - const: usbin-icl-change + + io-channels: + items: + - description: USB in current in uA + - description: USB in voltage in uV + + io-channel-names: + items: + - const: usbin_i + - const: usbin_v + + monitored-battery: + description: phandle to the simple-battery node + $ref: /schemas/types.yaml#/definitions/phandle + +required: + - compatible + - reg + - interrupts + - interrupt-names + - io-channels + - io-channel-names + - monitored-battery + +additionalProperties: false + +examples: + - | + #include + + pmic { + #address-cells = <1>; + #size-cells = <0>; + #interrupt-cells = <4>; + + charger@1000 { + compatible = "qcom,pmi8998-charger"; + reg = <0x1000>; + + interrupts = <0x2 0x12 0x2 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x13 0x4 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x13 0x6 IRQ_TYPE_EDGE_RISING>, + <0x2 0x16 0x1 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "usb-plugin", "bat-ov", "wdog-bark", "usbin-icl-change"; + + io-channels = <&pmi8998_rradc 3>, + <&pmi8998_rradc 4>; + io-channel-names = "usbin_i", + "usbin_v"; + + monitored-battery = <&battery>; + }; + }; From aadef46ad86af46342d6a768262cf86e03809a54 Mon Sep 17 00:00:00 2001 From: Jami Kettunen Date: Wed, 14 Sep 2022 18:57:29 +0300 Subject: [PATCH 0304/1823] [ANNOTATION] Import Qualcomm smb2 charger driver v6 (2022-10-09) Link: https://patchwork.kernel.org/project/linux-arm-msm/cover/20221008183753.249514-1-caleb.connolly@linaro.org/ From f74131008a33cb412a7cb22f50fd13ecde3c42c1 Mon Sep 17 00:00:00 2001 From: Joel Selvaraj Date: Fri, 25 Dec 2020 12:55:46 +0530 Subject: [PATCH 0305/1823] power: supply: introduce pmi8998 fuel guage driver --- drivers/power/supply/Kconfig | 10 + drivers/power/supply/Makefile | 1 + drivers/power/supply/pmi8998_fg.c | 526 ++++++++++++++++++++++++++++++ drivers/power/supply/pmi8998_fg.h | 138 ++++++++ 4 files changed, 675 insertions(+) create mode 100644 drivers/power/supply/pmi8998_fg.c create mode 100644 drivers/power/supply/pmi8998_fg.h diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index b4c39d16bed9..8ad785d54a4a 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -934,4 +934,14 @@ config CHARGER_QCOM_SMB2 Say Y or M here to enable reporting the charger status and rate on supported platforms such as Snapdragon 845 and 835 based phones. +config BATTERY_PMI8998_FG + tristate "Qualcomm PMI8998 fuel gauge driver" + depends on MFD_SPMI_PMIC + help + Say Y here to enable the Qualcomm PMI8998 Fuel Gauge driver. This + adds support for battery fuel gauging and state of charge of + battery connected tothe fuel gauge. The state of charge is + reported through a BMS power supply property and also sends + uevents when the capacity is updated. + endif # POWER_SUPPLY diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index edfaa4c08c20..dfa2b0382a46 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -111,3 +111,4 @@ obj-$(CONFIG_BATTERY_ACER_A500) += acer_a500_battery.o obj-$(CONFIG_BATTERY_SURFACE) += surface_battery.o obj-$(CONFIG_CHARGER_SURFACE) += surface_charger.o obj-$(CONFIG_BATTERY_UG3105) += ug3105_battery.o +obj-$(CONFIG_BATTERY_PMI8998_FG) += pmi8998_fg.o diff --git a/drivers/power/supply/pmi8998_fg.c b/drivers/power/supply/pmi8998_fg.c new file mode 100644 index 000000000000..ba2cf70ecc15 --- /dev/null +++ b/drivers/power/supply/pmi8998_fg.c @@ -0,0 +1,526 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2020, The Linux Foundation. All rights reserved. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pmi8998_fg.h" + +/************************ + * IO FUNCTIONS + * **********************/ + +/** + * pmi8998_read() - Read multiple registers with regmap_bulk_read + * + * @param map The regmap to read + * @param val Pointer to read values into + * @param addr Address to read from + * @param len Number of registers (bytes) to read + * @return int 0 on success, negative errno on error + */ +static int pmi8998_read(struct regmap *map, u8 *val, u16 addr, int len) +{ + if ((addr & 0xff00) == 0) { + pr_err("base cannot be zero base=0x%02x\n", addr); + return -EINVAL; + } + + pr_info("%s: reading 0x%x bytes from 0x%x", __func__, len, addr); + + return regmap_bulk_read(map, addr, val, len); +} + +/** + * @brief pmi8998_write() - Write multiple registers with regmap_bulk_write + * + * @param map The regmap to write + * @param val Pointer to write values into + * @param addr Address to write from + * @param len Number of registers (bytes) to write + * @return int 0 on success, negative errno on error + */ +static int pmi8998_write(struct regmap *map, u8 *val, u16 addr, int len) +{ + int rc; + bool sec_access = (addr & 0xff) > 0xd0; + u8 sec_addr_val = 0xa5; + + if (sec_access) { + rc = regmap_bulk_write(map, + (addr & 0xff00) | 0xd0, + &sec_addr_val, 1); + } + + if ((addr & 0xff00) == 0) { + pr_err("addr cannot be zero base=0x%02x\n", addr); + return -EINVAL; + } + + return regmap_bulk_write(map, addr, val, len); +} + +/** + * @brief pmi8998_masked_write() - like pmi8998_write but applies + * a mask first. + * + * @param map The regmap to write + * @param val Pointer to write values into + * @param addr Address to write from + * @param len Number of registers (bytes) to write + * @return int 0 on success, negative errno on error + */ +static int pmi8998_masked_write(struct regmap *map, u16 addr, + u8 mask, u8 val) +{ + int error; + u8 reg; + error = pmi8998_read(map, ®, addr, 1); + if (error) + return error; + + reg &= ~mask; + reg |= val & mask; + + error = pmi8998_write(map, ®, addr, 1); + return error; +} + +static int64_t twos_compliment_extend(int64_t val, int nbytes) +{ + int i; + int64_t mask; + + mask = 0x80LL << ((nbytes - 1) * 8); + if (val & mask) { + for (i = 8; i > nbytes; i--) { + mask = 0xFFLL << ((i - 1) * 8); + val |= mask; + } + } + return val; +} + +/************************* + * Battery Status RW + * ***********************/ + +static int pmi8998_fg_get_capacity(struct pmi8998_fg_chip *chip, int *val) +{ + u8 cap[2]; + int error = pmi8998_read(chip->regmap, cap, REG_BASE(chip) + BATT_MONOTONIC_SOC, 2); + if (error) + return error; + if (cap[0] != cap[1]) { + cap[0] = cap[0] < cap[1] ? cap[0] : cap[1]; + } + *val = DIV_ROUND_CLOSEST((cap[0] - 1) * 98, 0xff - 2) + 1; + return 0; +} + +static bool pmi8998_battery_missing(struct pmi8998_fg_chip *chip) +{ + int rc; + u8 fg_batt_sts; + + rc = pmi8998_read(chip->regmap, &fg_batt_sts, + REG_BATT(chip) + INT_RT_STS, 1); + if (rc) { + pr_warn("read read failed: addr=%03X, rc=%d\n", + REG_BATT(chip) + INT_RT_STS, rc); + return false; + } + + // Bit 6 is set if the battery is missing + return (fg_batt_sts & BIT(6)) ? true : false; +} + +static int pmi8998_fg_get_temperature(struct pmi8998_fg_chip *chip, int *val) +{ + int rc, temp; + u8 readval[2]; + + rc = pmi8998_read(chip->regmap, readval, REG_BATT(chip) + PARAM_ADDR_BATT_TEMP, 2); + if (rc) { + pr_err("Failed to read temperature\n"); + return rc; + } + temp = ((readval[1] & BATT_TEMP_MSB_MASK) << 8) | + (readval[0] & BATT_TEMP_LSB_MASK); + temp = DIV_ROUND_CLOSEST(temp * 10, 4); + + *val = temp -2730; + return 0; +} + +static int pmi8998_fg_get_current(struct pmi8998_fg_chip *chip, int *val) +{ + int rc, temp; + u8 readval[2]; + + rc = pmi8998_read(chip->regmap, readval, REG_BATT(chip) + PARAM_ADDR_BATT_CURRENT, 2); + if (rc) { + pr_err("Failed to read current\n"); + return rc; + } + + temp = readval[1] << 8 | readval[0]; + temp = twos_compliment_extend(temp, 2); + *val = div_s64((s64)temp * 488281, + 1000); + return 0; +} + +static int pmi8998_fg_get_voltage(struct pmi8998_fg_chip *chip, int *val) +{ + int rc, temp; + u8 readval[2]; + + rc = pmi8998_read(chip->regmap, readval, REG_BATT(chip) + PARAM_ADDR_BATT_VOLTAGE, 2); + if (rc) { + pr_err("Failed to read voltage\n"); + return rc; + } + + temp = readval[1] << 8 | readval[0]; + temp = twos_compliment_extend(temp, 2); + *val = div_s64((s64)temp * 122070, + 1000); + return 0; +} + +static int pmi8998_fg_get_max_charge_design(struct pmi8998_fg_chip *chip, int *val) +{ + int rc, temp; + u8 readval[2]; + + rc = pmi8998_read(chip->regmap, readval, REG_BATT(chip) + BATT_INFO_CHARGE_MAX_DESIGN, 2); + if (rc) { + pr_err("Failed to read voltage\n"); + return rc; + } + + temp = readval[1] << 8 | readval[0]; + temp = twos_compliment_extend(temp, 2); + *val = div_s64((s64)temp * 122070, + 1000); + return 0; +} + +/******************** + * Init stuff + * ******************/ + +static int pmi8998_iacs_clear_sequence(struct pmi8998_fg_chip *chip) +{ + int rc = 0; + u8 temp; + + /* clear the error */ + rc = pmi8998_masked_write(chip->regmap, REG_MEM(chip) + MEM_INTF_IMA_CFG, + BIT(2), BIT(2)); + if (rc) { + pr_err("Error writing to IMA_CFG, rc=%d\n", rc); + return rc; + } + + temp = 0x4; + rc = pmi8998_write(chip->regmap, &temp, REG_MEM(chip) + MEM_INTF_ADDR_LSB + 1, 1); + if (rc) { + pr_err("Error writing to MEM_INTF_ADDR_MSB, rc=%d\n", rc); + return rc; + } + + temp = 0x0; + rc = pmi8998_write(chip->regmap, &temp, REG_MEM(chip) + MEM_INTF_WR_DATA0 + 3, 1); + if (rc) { + pr_err("Error writing to WR_DATA3, rc=%d\n", rc); + return rc; + } + + rc = pmi8998_read(chip->regmap, &temp, REG_MEM(chip) + MEM_INTF_RD_DATA0 + 3, 1); + if (rc) { + pr_err("Error writing to RD_DATA3, rc=%d\n", rc); + return rc; + } + + rc = pmi8998_masked_write(chip->regmap, REG_MEM(chip) + MEM_INTF_IMA_CFG, + BIT(2), 0); + if (rc) { + pr_err("Error writing to IMA_CFG, rc=%d\n", rc); + return rc; + } + return rc; +} + +static int pmi8998_clear_ima(struct pmi8998_fg_chip *chip, + bool check_hw_sts) +{ + int rc = 0, ret = 0; + u8 err_sts = 0, exp_sts = 0, hw_sts = 0; + bool run_err_clr_seq = false; + + rc = pmi8998_read(chip->regmap, &err_sts, + REG_MEM(chip) + MEM_INTF_IMA_ERR_STS, 1); + if (rc) { + dev_err(chip->dev, "failed to read IMA_ERR_STS, rc=%d\n", rc); + return rc; + } + + rc = pmi8998_read(chip->regmap, &exp_sts, + REG_MEM(chip) + MEM_INTF_IMA_EXP_STS, 1); + if (rc) { + dev_err(chip->dev, "Error in reading IMA_EXP_STS, rc=%d\n", rc); + return rc; + } + + if (check_hw_sts) { + rc = pmi8998_read(chip->regmap, &hw_sts, + REG_MEM(chip) + MEM_INTF_IMA_HW_STS, 1); + if (rc) { + dev_err(chip->dev, "Error in reading IMA_HW_STS, rc=%d\n", rc); + return rc; + } + /* + * Lower nibble should be equal to upper nibble before SRAM + * transactions begins from SW side. + */ + if ((hw_sts & 0x0f) != hw_sts >> 4) { + dev_err(chip->dev, "IMA HW not in correct state, hw_sts=%x\n", + hw_sts); + run_err_clr_seq = true; + } + } + + if (exp_sts & (BIT(0) | BIT(1) | BIT(3) | + BIT(4) | BIT(5) | BIT(6) | + BIT(7))) { + dev_warn(chip->dev, "IMA exception bit set, exp_sts=%x\n", exp_sts); + run_err_clr_seq = true; + } + + if (run_err_clr_seq) { + ret = pmi8998_iacs_clear_sequence(chip); + if (!ret) + return -EAGAIN; + else + dev_err(chip->dev, "Error clearing IMA exception ret=%d\n", ret); + } + + return rc; +} + +static int fg_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct pmi8998_fg_chip *chip = power_supply_get_drvdata(psy); + int error = 0; + + dev_info(chip->dev, "Getting property: %d", psp); + + switch (psp) { + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = "QCOM"; + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = "PMI8998 Battery"; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; + case POWER_SUPPLY_PROP_CAPACITY: + error = pmi8998_fg_get_capacity(chip, &val->intval); + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + error = pmi8998_fg_get_current(chip, &val->intval); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + error = pmi8998_fg_get_voltage(chip, &val->intval); + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + val->intval = chip->batt_info.batt_max_voltage_uv; + break; + case POWER_SUPPLY_PROP_TEMP: + error = pmi8998_fg_get_temperature(chip, &val->intval); + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN: + val->intval = 3370000; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + case POWER_SUPPLY_PROP_CHARGE_FULL: /* TODO: Implement learning */ + val->intval = chip->batt_info.nom_cap_uah; + break; + case POWER_SUPPLY_PROP_STATUS: + val->intval = POWER_SUPPLY_STATUS_CHARGING; + //error = smb2_chg_get_status(chip, &val->intval); + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = POWER_SUPPLY_HEALTH_GOOD; + break; + default: + pr_err("invalid property: %d\n", psp); + return -EINVAL; + } + return error; +} + +static const struct power_supply_desc bms_psy_desc = { + .name = "pmi8998-bms", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = fg_properties, + .num_properties = ARRAY_SIZE(fg_properties), + .get_property = fg_get_property, +}; + +static int pmi8998_fg_of_battery_init(struct pmi8998_fg_chip *chip){ + struct device_node *batt_node; + struct device_node *node = chip->dev->of_node; + int rc = 0; + + batt_node = of_find_node_by_name(node, "qcom,battery-data"); + if (!batt_node) { + pr_err("No available batterydata\n"); + return rc; + } + + of_property_read_u32(batt_node, "qcom,max-voltage-uv", + &chip->batt_info.batt_max_voltage_uv_design); + + // Can be read from SRAM, hardcode in DTS for now as reading SRAM is HARD! + of_property_read_u32(batt_node, "qcom,design-capacity", + &chip->batt_info.nom_cap_uah); + + return rc; +} + +static int pmi8998_fg_probe(struct platform_device *pdev) +{ + struct power_supply_config supply_config = {}; + struct pmi8998_fg_chip *chip; + const __be32 *prop_addr; + int rc = 0; + u8 dma_status; + bool error_present; + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) { + return -ENOMEM; + } + + chip->dev = &pdev->dev; + mutex_init(&chip->lock); + + chip->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!chip->regmap) { + dev_err(chip->dev, "failed to locate the regmap\n"); + return -ENODEV; + } + + // Get base address + prop_addr = of_get_address(pdev->dev.of_node, 0, NULL, NULL); + if (!prop_addr) { + dev_err(chip->dev, "Couldn't read SOC base address from dt\n"); + return -EINVAL; + } + chip->base = be32_to_cpu(*prop_addr); + + // Init memif fn inlined here (chip hardware info) + rc = pmi8998_read(chip->regmap, chip->revision, REG_MEM(chip) + DIG_MINOR, 4); + if (rc) { + dev_err(chip->dev, "Unable to read FG revision rc=%d\n", rc); + return rc; + } + + dev_dbg(chip->dev, "pmi8998 revision DIG:%d.%d ANA:%d.%d\n", + chip->revision[DIG_MAJOR], chip->revision[DIG_MINOR], + chip->revision[ANA_MAJOR], chip->revision[ANA_MINOR]); + + /* + * Change the FG_MEM_INT interrupt to track IACS_READY + * condition instead of end-of-transaction. This makes sure + * that the next transaction starts only after the hw is ready. + * IACS_INTR_SRC_SLCT is BIT(3) + */ + rc = pmi8998_masked_write(chip->regmap, + REG_MEM(chip) + MEM_INTF_IMA_CFG, BIT(3), BIT(3)); + if (rc) { + dev_err(chip->dev, + "failed to configure interrupt source %d\n", + rc); + return rc; + } + + rc = pmi8998_clear_ima(chip, true); + if (rc && rc != -EAGAIN) { + dev_err(chip->dev, "Error clearing IMA, exception rc=%d", rc); + return rc; + } + + // Check and clear DMA errors + rc = pmi8998_read(chip->regmap, &dma_status, REG_MEM(chip) + 0x70, 1); + if (rc < 0) { + pr_err("failed to read dma_status, rc=%d\n", rc); + return rc; + } + + error_present = dma_status & (BIT(1) | BIT(2)); + rc = pmi8998_masked_write(chip->regmap, REG_MEM(chip) + 0x71, BIT(0), + error_present ? BIT(0) : 0); + if (rc < 0) { + pr_err("failed to write dma_ctl, rc=%d\n", rc); + return rc; + } + + dev_dbg(chip->dev, "probed revision DIG:%d.%d ANA:%d.%d\n", + chip->revision[DIG_MAJOR], chip->revision[DIG_MINOR], + chip->revision[ANA_MAJOR], chip->revision[ANA_MINOR]); + + supply_config.drv_data = chip; + supply_config.of_node = pdev->dev.of_node; + + chip->bms_psy = devm_power_supply_register(chip->dev, + &bms_psy_desc, &supply_config); + if (IS_ERR(chip->bms_psy)) { + dev_err(&pdev->dev, "failed to register battery\n"); + return PTR_ERR(chip->bms_psy); + } + + platform_set_drvdata(pdev, chip); + return 0; +} + +static int pmi8998_fg_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id fg_match_id_table[] = { + { .compatible = "qcom,pmi8998-fg" }, + { /* sentinal */ } +}; +MODULE_DEVICE_TABLE(of, fg_match_id_table); + +static struct platform_driver qcom_fg_driver = { + .probe = pmi8998_fg_probe, + .remove = pmi8998_fg_remove, + .driver = { + .name = "pmi8998-fg", + .of_match_table = fg_match_id_table, + }, +}; + +module_platform_driver(qcom_fg_driver); + +MODULE_AUTHOR("Caleb Connolly "); +MODULE_AUTHOR("Joel Selvaraj "); +MODULE_DESCRIPTION("Qualcomm PMI8998 Fuel Guage Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/supply/pmi8998_fg.h b/drivers/power/supply/pmi8998_fg.h new file mode 100644 index 000000000000..20dd2d2fc6e8 --- /dev/null +++ b/drivers/power/supply/pmi8998_fg.h @@ -0,0 +1,138 @@ + +// #define FG_PARAM_MAX 49 + +/**** Registers *****/ + +// pmi8998 v2 specific +#define BATT_INFO_CHARGE_MAX_DESIGN 0x4a +#define MEM_INTF_CFG 0x50 +#define MEM_INTF_ADDR_LSB 0x61 +#define MEM_INTF_RD_DATA0 0x67 +#define MEM_INTF_WR_DATA0 0x63 + +#define SMB2_CABLE_CONNECTED 0x06 + +// pm8950 / pm89988 common +#define MEM_INTF_IMA_CFG 0x52 +#define MEM_INTF_IMA_OPR_STS 0x54 +#define MEM_INTF_IMA_EXP_STS 0x55 +#define MEM_INTF_IMA_HW_STS 0x56 +#define MEM_INTF_BEAT_COUNT 0x57 +#define MEM_INTF_IMA_ERR_STS 0x5f +#define MEM_INTF_IMA_BYTE_EN 0x60 + +#define BATT_INFO_THERM_C1 0x5c +#define BATT_INFO_VBATT_LSB 0xa0 +#define BATT_INFO_VBATT_MSB 0xa1 +#define BATT_INFO_IBATT_LSB 0xa2 +#define BATT_INFO_IBATT_MSB 0xa3 +#define BATT_INFO_BATT_TEMP_LSB 0x50 +#define BATT_INFO_BATT_TEMP_MSB 0x51 +#define BATT_MONOTONIC_SOC 0x09 + +#define BATT_TEMP_LSB_MASK GENMASK(7, 0) +#define BATT_TEMP_MSB_MASK GENMASK(2, 0) + +#define REG_BASE(chip) (chip->base) +#define REG_BATT(chip) (chip->base + 0x100) +#define REG_MEM(chip) (chip->base + 0x400) + +/* Interrupt offsets */ +#define INT_RT_STS 0x10 +#define INT_EN_CLR 0x16 + +// Param addresses +#define PARAM_ADDR_BATT_TEMP 0x50 +#define PARAM_ADDR_BATT_VOLTAGE 0xa0 +#define PARAM_ADDR_BATT_CURRENT 0xa2 + +enum wa_flags { + PMI8998_V1_REV_WA, + PMI8998_V2_REV_WA, +}; + +enum pmi8998_rev_offsets { + DIG_MINOR = 0x0, + DIG_MAJOR = 0x1, + ANA_MINOR = 0x2, + ANA_MAJOR = 0x3, +}; +enum pmi8998_rev { + DIG_REV_1 = 0x1, + DIG_REV_2 = 0x2, + DIG_REV_3 = 0x3, +}; + +enum charger_status{ + TRICKLE_CHARGE = 0, + PRE_CHARGE, + FAST_CHARGE, + FULLON_CHARGE, + TAPER_CHARGE, + TERMINATE_CHARGE, + INHIBIT_CHARGE, + DISABLE_CHARGE, +}; + +static enum power_supply_property fg_properties[] = { + POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_MIN, + POWER_SUPPLY_PROP_TEMP, + // POWER_SUPPLY_PROP_CHARGE_NOW, + // POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_STATUS, +}; + +struct fg_learning_data { + struct mutex learning_lock; + bool active; + int64_t cc_uah; + int learned_cc_uah; + int init_cc_pc_val; + int max_start_soc; + int max_increment; + int max_decrement; + int vbat_est_thr_uv; + int max_cap_limit; + int min_cap_limit; + int min_temp; + int max_temp; +}; + +struct battery_info { + const char *manufacturer; + const char *model; + const char *serial_num; + + int nom_cap_uah; + + int batt_max_voltage_uv_design; + int batt_max_voltage_uv; +}; + +struct pmi8998_fg_chip { + struct device *dev; + unsigned int base; + struct regmap *regmap; + struct mutex lock; + + struct power_supply *bms_psy; + + u8 revision[4]; + bool ima_supported; + + struct battery_info batt_info; + + struct fg_learning_data learning_data; + + int health; + int status; +}; From 7214b14a7b4c3aef00acbb197b65f90787273ebe Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Fri, 25 Dec 2020 12:56:42 +0530 Subject: [PATCH 0306/1823] arm64: dts: qcom: pmi8998: Add fuel gauge Add a node for qcom,pmi8998-fg. --- arch/arm64/boot/dts/qcom/pmi8998.dtsi | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/pmi8998.dtsi b/arch/arm64/boot/dts/qcom/pmi8998.dtsi index 3852a012bb0f..79d47540fa8b 100644 --- a/arch/arm64/boot/dts/qcom/pmi8998.dtsi +++ b/arch/arm64/boot/dts/qcom/pmi8998.dtsi @@ -18,6 +18,16 @@ interrupt-controller; #interrupt-cells = <2>; }; + + pmi8998_fg: fuel-gauge@4000 { + compatible = "qcom,pmi8998-fg"; + reg = <0x4000>; + + interrupts = <0x2 0x40 0x3 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "soc-delta"; + + status = "disabled"; + }; }; pmi8998_lsid1: pmic@3 { From e5f8763d748a01c700ec0dc55502f40bb62940f1 Mon Sep 17 00:00:00 2001 From: Joel Selvaraj Date: Thu, 31 Dec 2020 16:35:41 +0530 Subject: [PATCH 0307/1823] power: supply: add battery charging status feature --- drivers/power/supply/pmi8998_fg.c | 106 +++++++++++++++++++++++++++--- drivers/power/supply/pmi8998_fg.h | 7 ++ 2 files changed, 105 insertions(+), 8 deletions(-) diff --git a/drivers/power/supply/pmi8998_fg.c b/drivers/power/supply/pmi8998_fg.c index ba2cf70ecc15..f38f1984b59c 100644 --- a/drivers/power/supply/pmi8998_fg.c +++ b/drivers/power/supply/pmi8998_fg.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include "pmi8998_fg.h" @@ -318,6 +320,70 @@ static int pmi8998_clear_ima(struct pmi8998_fg_chip *chip, return rc; } +int pmi8998_get_prop_usb_online(struct pmi8998_fg_chip *chip, int *val){ + unsigned int stat; + int rc; + + rc = regmap_read(chip->regmap, POWER_PATH_STATUS_REG, &stat); + if (rc < 0){ + dev_err(chip->dev, "Couldn't read POWER_PATH_STATUS! ret=%d\n", rc); + return rc; + } + + dev_dbg(chip->dev, "USB POWER_PATH_STATUS : 0x%02x\n", stat); + *val = (stat & BIT(4)) && (stat & BIT(0)); + return rc; +} + +int pmi8998_get_prop_batt_status(struct pmi8998_fg_chip *chip, int *val){ + int usb_online_val; + unsigned int stat; + int rc; + bool usb_online; + + rc = pmi8998_get_prop_usb_online(chip, &usb_online_val); + if (rc < 0) { + dev_err(chip->dev, "Couldn't get usb online property rc=%d\n", rc); + return rc; + } + dev_dbg(chip->dev, "USB ONLINE val : %d\n", usb_online_val); + usb_online = (bool)usb_online_val; + + if (!usb_online) { + *val = POWER_SUPPLY_STATUS_DISCHARGING; + return rc; + } + + rc = regmap_read(chip->regmap, BATTERY_CHARGER_STATUS_REG(chip), &stat); + if (rc < 0){ + dev_err(chip->dev, "Charging status REGMAP read failed! ret=%d\n", rc); + return rc; + } + + stat = stat & BATTERY_CHARGER_STATUS_MASK; + dev_dbg(chip->dev, "Charging status : %d!\n", stat); + + switch (stat) { + case TRICKLE_CHARGE: + case PRE_CHARGE: + case FAST_CHARGE: + case FULLON_CHARGE: + case TAPER_CHARGE: + case TERMINATE_CHARGE: + case INHIBIT_CHARGE: + *val = POWER_SUPPLY_STATUS_CHARGING; + break; + case DISABLE_CHARGE: + *val = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + default: + *val = POWER_SUPPLY_STATUS_UNKNOWN; + break; + } + + return rc; +} + static int fg_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -325,7 +391,7 @@ static int fg_get_property(struct power_supply *psy, struct pmi8998_fg_chip *chip = power_supply_get_drvdata(psy); int error = 0; - dev_info(chip->dev, "Getting property: %d", psp); + dev_dbg(chip->dev, "Getting property: %d", psp); switch (psp) { case POWER_SUPPLY_PROP_MANUFACTURER: @@ -360,8 +426,7 @@ static int fg_get_property(struct power_supply *psy, val->intval = chip->batt_info.nom_cap_uah; break; case POWER_SUPPLY_PROP_STATUS: - val->intval = POWER_SUPPLY_STATUS_CHARGING; - //error = smb2_chg_get_status(chip, &val->intval); + error = pmi8998_get_prop_batt_status(chip, &val->intval); break; case POWER_SUPPLY_PROP_HEALTH: val->intval = POWER_SUPPLY_HEALTH_GOOD; @@ -402,12 +467,19 @@ static int pmi8998_fg_of_battery_init(struct pmi8998_fg_chip *chip){ return rc; } +irqreturn_t pmi8998_handle_usb_plugin(int irq, void *data){ + struct pmi8998_fg_chip *chip = data; + dev_dbg(chip->dev, "USB IRQ called!\n"); + power_supply_changed(chip->bms_psy); + return IRQ_HANDLED; +} + static int pmi8998_fg_probe(struct platform_device *pdev) { struct power_supply_config supply_config = {}; struct pmi8998_fg_chip *chip; const __be32 *prop_addr; - int rc = 0; + int rc = 0, irq; u8 dma_status; bool error_present; @@ -433,6 +505,13 @@ static int pmi8998_fg_probe(struct platform_device *pdev) } chip->base = be32_to_cpu(*prop_addr); + prop_addr = of_get_address(pdev->dev.of_node, 1, NULL, NULL); + if (!prop_addr) { + dev_err(chip->dev, "Couldn't read CHG base address from dt\n"); + return -EINVAL; + } + chip->chg_base = be32_to_cpu(*prop_addr); + // Init memif fn inlined here (chip hardware info) rc = pmi8998_read(chip->regmap, chip->revision, REG_MEM(chip) + DIG_MINOR, 4); if (rc) { @@ -479,10 +558,6 @@ static int pmi8998_fg_probe(struct platform_device *pdev) pr_err("failed to write dma_ctl, rc=%d\n", rc); return rc; } - - dev_dbg(chip->dev, "probed revision DIG:%d.%d ANA:%d.%d\n", - chip->revision[DIG_MAJOR], chip->revision[DIG_MINOR], - chip->revision[ANA_MAJOR], chip->revision[ANA_MINOR]); supply_config.drv_data = chip; supply_config.of_node = pdev->dev.of_node; @@ -495,6 +570,21 @@ static int pmi8998_fg_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, chip); + + irq = of_irq_get_byname(pdev->dev.of_node, "usb-plugin"); + if (irq < 0) { + dev_err(&pdev->dev, "Couldn't get irq usb-plugin byname\n"); + return irq; + } + + rc = devm_request_threaded_irq(chip->dev, irq, NULL, + pmi8998_handle_usb_plugin, + IRQF_ONESHOT, "usb-plugin", chip); + if (rc < 0) { + pr_err("Couldn't request irq %d\n", irq); + return rc; + } + return 0; } diff --git a/drivers/power/supply/pmi8998_fg.h b/drivers/power/supply/pmi8998_fg.h index 20dd2d2fc6e8..81a90945c0e6 100644 --- a/drivers/power/supply/pmi8998_fg.h +++ b/drivers/power/supply/pmi8998_fg.h @@ -46,6 +46,12 @@ #define PARAM_ADDR_BATT_VOLTAGE 0xa0 #define PARAM_ADDR_BATT_CURRENT 0xa2 +#define MISC_BASE 0x1600 + +#define BATTERY_CHARGER_STATUS_REG(chip) (chip->chg_base + 0x06) +#define BATTERY_CHARGER_STATUS_MASK GENMASK(2, 0) +#define POWER_PATH_STATUS_REG (MISC_BASE + 0x0B) + enum wa_flags { PMI8998_V1_REV_WA, PMI8998_V2_REV_WA, @@ -121,6 +127,7 @@ struct battery_info { struct pmi8998_fg_chip { struct device *dev; unsigned int base; + unsigned int chg_base; struct regmap *regmap; struct mutex lock; From 6ab2aa83583e2d45220013ab9b8be70b397e36f3 Mon Sep 17 00:00:00 2001 From: Joel Selvaraj Date: Fri, 1 Jan 2021 16:18:26 +0530 Subject: [PATCH 0308/1823] fg: clean and read charge full and max voltage from dts --- drivers/power/supply/pmi8998_fg.c | 274 +++++++++++++++++++----------- drivers/power/supply/pmi8998_fg.h | 69 ++++---- 2 files changed, 206 insertions(+), 137 deletions(-) diff --git a/drivers/power/supply/pmi8998_fg.c b/drivers/power/supply/pmi8998_fg.c index f38f1984b59c..1aea6eaf30f9 100644 --- a/drivers/power/supply/pmi8998_fg.c +++ b/drivers/power/supply/pmi8998_fg.c @@ -36,7 +36,7 @@ static int pmi8998_read(struct regmap *map, u8 *val, u16 addr, int len) return -EINVAL; } - pr_info("%s: reading 0x%x bytes from 0x%x", __func__, len, addr); + //pr_info("%s: reading 0x%x bytes from 0x%x", __func__, len, addr); return regmap_bulk_read(map, addr, val, len); } @@ -118,7 +118,7 @@ static int64_t twos_compliment_extend(int64_t val, int nbytes) static int pmi8998_fg_get_capacity(struct pmi8998_fg_chip *chip, int *val) { u8 cap[2]; - int error = pmi8998_read(chip->regmap, cap, REG_BASE(chip) + BATT_MONOTONIC_SOC, 2); + int error = pmi8998_read(chip->regmap, cap, REG_BASE + BATT_MONOTONIC_SOC, 2); if (error) return error; if (cap[0] != cap[1]) { @@ -128,29 +128,12 @@ static int pmi8998_fg_get_capacity(struct pmi8998_fg_chip *chip, int *val) return 0; } -static bool pmi8998_battery_missing(struct pmi8998_fg_chip *chip) -{ - int rc; - u8 fg_batt_sts; - - rc = pmi8998_read(chip->regmap, &fg_batt_sts, - REG_BATT(chip) + INT_RT_STS, 1); - if (rc) { - pr_warn("read read failed: addr=%03X, rc=%d\n", - REG_BATT(chip) + INT_RT_STS, rc); - return false; - } - - // Bit 6 is set if the battery is missing - return (fg_batt_sts & BIT(6)) ? true : false; -} - static int pmi8998_fg_get_temperature(struct pmi8998_fg_chip *chip, int *val) { int rc, temp; u8 readval[2]; - rc = pmi8998_read(chip->regmap, readval, REG_BATT(chip) + PARAM_ADDR_BATT_TEMP, 2); + rc = pmi8998_read(chip->regmap, readval, REG_BATT + PARAM_ADDR_BATT_TEMP, 2); if (rc) { pr_err("Failed to read temperature\n"); return rc; @@ -168,16 +151,15 @@ static int pmi8998_fg_get_current(struct pmi8998_fg_chip *chip, int *val) int rc, temp; u8 readval[2]; - rc = pmi8998_read(chip->regmap, readval, REG_BATT(chip) + PARAM_ADDR_BATT_CURRENT, 2); + rc = pmi8998_read(chip->regmap, readval, REG_BATT + PARAM_ADDR_BATT_CURRENT, 2); if (rc) { pr_err("Failed to read current\n"); return rc; } - + //handle rev 1 too temp = readval[1] << 8 | readval[0]; - temp = twos_compliment_extend(temp, 2); - *val = div_s64((s64)temp * 488281, - 1000); + temp = twos_compliment_extend(temp, 15); + *val = div_s64((s64)temp * 488281, 1000); return 0; } @@ -186,34 +168,14 @@ static int pmi8998_fg_get_voltage(struct pmi8998_fg_chip *chip, int *val) int rc, temp; u8 readval[2]; - rc = pmi8998_read(chip->regmap, readval, REG_BATT(chip) + PARAM_ADDR_BATT_VOLTAGE, 2); + rc = pmi8998_read(chip->regmap, readval, REG_BATT + PARAM_ADDR_BATT_VOLTAGE, 2); if (rc) { pr_err("Failed to read voltage\n"); return rc; } - + //handle rev 1 too temp = readval[1] << 8 | readval[0]; - temp = twos_compliment_extend(temp, 2); - *val = div_s64((s64)temp * 122070, - 1000); - return 0; -} - -static int pmi8998_fg_get_max_charge_design(struct pmi8998_fg_chip *chip, int *val) -{ - int rc, temp; - u8 readval[2]; - - rc = pmi8998_read(chip->regmap, readval, REG_BATT(chip) + BATT_INFO_CHARGE_MAX_DESIGN, 2); - if (rc) { - pr_err("Failed to read voltage\n"); - return rc; - } - - temp = readval[1] << 8 | readval[0]; - temp = twos_compliment_extend(temp, 2); - *val = div_s64((s64)temp * 122070, - 1000); + *val = div_u64((u64)temp * 122070, 1000); return 0; } @@ -227,7 +189,7 @@ static int pmi8998_iacs_clear_sequence(struct pmi8998_fg_chip *chip) u8 temp; /* clear the error */ - rc = pmi8998_masked_write(chip->regmap, REG_MEM(chip) + MEM_INTF_IMA_CFG, + rc = pmi8998_masked_write(chip->regmap, REG_MEM + MEM_INTF_IMA_CFG, BIT(2), BIT(2)); if (rc) { pr_err("Error writing to IMA_CFG, rc=%d\n", rc); @@ -235,26 +197,26 @@ static int pmi8998_iacs_clear_sequence(struct pmi8998_fg_chip *chip) } temp = 0x4; - rc = pmi8998_write(chip->regmap, &temp, REG_MEM(chip) + MEM_INTF_ADDR_LSB + 1, 1); + rc = pmi8998_write(chip->regmap, &temp, REG_MEM + MEM_INTF_ADDR_LSB + 1, 1); if (rc) { pr_err("Error writing to MEM_INTF_ADDR_MSB, rc=%d\n", rc); return rc; } temp = 0x0; - rc = pmi8998_write(chip->regmap, &temp, REG_MEM(chip) + MEM_INTF_WR_DATA0 + 3, 1); + rc = pmi8998_write(chip->regmap, &temp, REG_MEM + MEM_INTF_WR_DATA0 + 3, 1); if (rc) { pr_err("Error writing to WR_DATA3, rc=%d\n", rc); return rc; } - rc = pmi8998_read(chip->regmap, &temp, REG_MEM(chip) + MEM_INTF_RD_DATA0 + 3, 1); + rc = pmi8998_read(chip->regmap, &temp, REG_MEM + MEM_INTF_RD_DATA0 + 3, 1); if (rc) { pr_err("Error writing to RD_DATA3, rc=%d\n", rc); return rc; } - rc = pmi8998_masked_write(chip->regmap, REG_MEM(chip) + MEM_INTF_IMA_CFG, + rc = pmi8998_masked_write(chip->regmap, REG_MEM + MEM_INTF_IMA_CFG, BIT(2), 0); if (rc) { pr_err("Error writing to IMA_CFG, rc=%d\n", rc); @@ -271,14 +233,14 @@ static int pmi8998_clear_ima(struct pmi8998_fg_chip *chip, bool run_err_clr_seq = false; rc = pmi8998_read(chip->regmap, &err_sts, - REG_MEM(chip) + MEM_INTF_IMA_ERR_STS, 1); + REG_MEM + MEM_INTF_IMA_ERR_STS, 1); if (rc) { dev_err(chip->dev, "failed to read IMA_ERR_STS, rc=%d\n", rc); return rc; } rc = pmi8998_read(chip->regmap, &exp_sts, - REG_MEM(chip) + MEM_INTF_IMA_EXP_STS, 1); + REG_MEM + MEM_INTF_IMA_EXP_STS, 1); if (rc) { dev_err(chip->dev, "Error in reading IMA_EXP_STS, rc=%d\n", rc); return rc; @@ -286,7 +248,7 @@ static int pmi8998_clear_ima(struct pmi8998_fg_chip *chip, if (check_hw_sts) { rc = pmi8998_read(chip->regmap, &hw_sts, - REG_MEM(chip) + MEM_INTF_IMA_HW_STS, 1); + REG_MEM + MEM_INTF_IMA_HW_STS, 1); if (rc) { dev_err(chip->dev, "Error in reading IMA_HW_STS, rc=%d\n", rc); return rc; @@ -384,6 +346,81 @@ int pmi8998_get_prop_batt_status(struct pmi8998_fg_chip *chip, int *val){ return rc; } +int pmi8998_get_prop_health_status(struct pmi8998_fg_chip *chip, int *val){ + unsigned int stat; + int rc; + + rc = regmap_read(chip->regmap, BATTERY_HEALTH_STATUS_REG(chip), &stat); + if (rc < 0){ + dev_err(chip->dev, "Health status REGMAP read failed! ret=%d\n", rc); + return rc; + } + + if (stat & BIT(0)) + *val = POWER_SUPPLY_HEALTH_COLD; + else if (stat & BIT(1)) + *val = POWER_SUPPLY_HEALTH_OVERHEAT; + else if (stat & BIT(2)) + *val = POWER_SUPPLY_HEALTH_COOL; + else if (stat & BIT(3)) + *val = POWER_SUPPLY_HEALTH_WARM; + else + *val = POWER_SUPPLY_HEALTH_GOOD; + + return rc; +} + +static int pmi8998_get_temp_threshold(struct pmi8998_fg_chip *chip, + enum power_supply_property psp, int *val) +{ + int rc; + u8 temp; + u16 reg; + + switch (psp) { + case POWER_SUPPLY_PROP_TEMP_MIN: + reg = BATT_INFO_JEITA_COLD(chip); + break; + case POWER_SUPPLY_PROP_TEMP_MAX: + reg = BATT_INFO_JEITA_HOT(chip); + break; + case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: + reg = BATT_INFO_JEITA_COOL(chip); + break; + case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: + reg = BATT_INFO_JEITA_WARM(chip); + break; + default: + return -EINVAL; + } + + rc = pmi8998_read(chip->regmap, &temp, reg, 1); + if (rc < 0) { + dev_err(chip->dev, "Error in reading jeita level for psp:%d, rc=%d\n", psp, rc); + return rc; + } + + /* Resolution is 0.5C. Base is -30C. */ + *val = (((5 * temp) / 10) - 30) * 10; + return 0; +} + +static void fg_get_model_name(struct pmi8998_fg_chip *chip, union power_supply_propval *val) +{ + switch (chip->subtype) + { + case PMI8998_SUBTYPE: + val->strval = "PMI8998 Battery"; + break; + case PM8998_SUBTYPE: + val->strval = "PM8998 Battery"; + break; + //handle pm660 and other socs that use fg3 + default: + val->strval = "Unknown PMIC Battery"; + } +} + static int fg_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -395,10 +432,10 @@ static int fg_get_property(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_MANUFACTURER: - val->strval = "QCOM"; + val->strval = "Qualcomm"; break; case POWER_SUPPLY_PROP_MODEL_NAME: - val->strval = "PMI8998 Battery"; + fg_get_model_name(chip, val); break; case POWER_SUPPLY_PROP_TECHNOLOGY: val->intval = POWER_SUPPLY_TECHNOLOGY_LION; @@ -412,64 +449,80 @@ static int fg_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_NOW: error = pmi8998_fg_get_voltage(chip, &val->intval); break; - case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: - val->intval = chip->batt_info.batt_max_voltage_uv; - break; - case POWER_SUPPLY_PROP_TEMP: - error = pmi8998_fg_get_temperature(chip, &val->intval); - break; case POWER_SUPPLY_PROP_VOLTAGE_MIN: - val->intval = 3370000; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + val->intval = chip->batt_min_voltage_uv; break; - case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: - case POWER_SUPPLY_PROP_CHARGE_FULL: /* TODO: Implement learning */ - val->intval = chip->batt_info.nom_cap_uah; + case POWER_SUPPLY_PROP_VOLTAGE_MAX: + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + val->intval = chip->batt_max_voltage_uv; break; case POWER_SUPPLY_PROP_STATUS: error = pmi8998_get_prop_batt_status(chip, &val->intval); break; case POWER_SUPPLY_PROP_HEALTH: - val->intval = POWER_SUPPLY_HEALTH_GOOD; + error = pmi8998_get_prop_health_status(chip, &val->intval); break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + case POWER_SUPPLY_PROP_CHARGE_FULL: /* TODO: Implement capacity learning */ + val->intval = chip->batt_cap_uah; + break; + case POWER_SUPPLY_PROP_TEMP: + error = pmi8998_fg_get_temperature(chip, &val->intval); + break; + case POWER_SUPPLY_PROP_TEMP_MIN: + case POWER_SUPPLY_PROP_TEMP_MAX: + case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: + case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: + error = pmi8998_get_temp_threshold(chip, psp, &val->intval); + break; + //POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,POWER_SUPPLY_PROP_TIME_TO_FULL_AVG - calculate time remaining for full charge - implementable + //POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG - calculate time remaining when discharging - implementable + //POWER_SUPPLY_PROP_CHARGE_NOW - requires capacity learning + //POWER_SUPPLY_PROP_CHARGE_FULL - requires capacity learning + //POWER_SUPPLY_PROP_CHARGE_COUNTER - requires capacity learning + //POWER_SUPPLY_PROP_CYCLE_COUNT - needs votables - no idea how they work + //POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT - needs votables - no idea how they work + //POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX - needs votables - no idea how they work + //POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT - needs votables - no idea how they work default: - pr_err("invalid property: %d\n", psp); + dev_err(chip->dev, "invalid property: %d\n", psp); return -EINVAL; } return error; } static const struct power_supply_desc bms_psy_desc = { - .name = "pmi8998-bms", + .name = "bms", .type = POWER_SUPPLY_TYPE_BATTERY, .properties = fg_properties, .num_properties = ARRAY_SIZE(fg_properties), .get_property = fg_get_property, }; -static int pmi8998_fg_of_battery_init(struct pmi8998_fg_chip *chip){ - struct device_node *batt_node; - struct device_node *node = chip->dev->of_node; - int rc = 0; - - batt_node = of_find_node_by_name(node, "qcom,battery-data"); - if (!batt_node) { - pr_err("No available batterydata\n"); - return rc; - } - - of_property_read_u32(batt_node, "qcom,max-voltage-uv", - &chip->batt_info.batt_max_voltage_uv_design); - - // Can be read from SRAM, hardcode in DTS for now as reading SRAM is HARD! - of_property_read_u32(batt_node, "qcom,design-capacity", - &chip->batt_info.nom_cap_uah); - - return rc; -} - irqreturn_t pmi8998_handle_usb_plugin(int irq, void *data){ struct pmi8998_fg_chip *chip = data; - dev_dbg(chip->dev, "USB IRQ called!\n"); + int rc; + unsigned int stat; + bool vbus_rising; + union power_supply_propval val; + + rc = regmap_read(chip->regmap, USBIN_BASE + INT_RT_STS, &stat); + if (rc < 0){ + dev_err(chip->dev, "Couldn't read USB status from reg! ret=%d\n", rc); + return rc; + } + vbus_rising = (bool)(stat & BIT(4)); + + if (vbus_rising) { + val.intval = POWER_SUPPLY_STATUS_CHARGING; + power_supply_set_property(chip->bms_psy, POWER_SUPPLY_PROP_STATUS, &val); + } else { + val.intval = POWER_SUPPLY_STATUS_DISCHARGING; + power_supply_set_property(chip->bms_psy, POWER_SUPPLY_PROP_STATUS, &val); + } + + dev_dbg(chip->dev, "USB IRQ: %s\n", vbus_rising ? "attached" : "detached"); power_supply_changed(chip->bms_psy); return IRQ_HANDLED; } @@ -512,13 +565,40 @@ static int pmi8998_fg_probe(struct platform_device *pdev) } chip->chg_base = be32_to_cpu(*prop_addr); + rc = of_property_read_u32(pdev->dev.of_node, "qcom,min-voltage-uv", + &chip->batt_min_voltage_uv); + if (rc < 0) { + dev_err(chip->dev, "Error in reading qcom,min-voltage-uv, rc=%d\n", rc); + return rc; + } + + rc = of_property_read_u32(pdev->dev.of_node, "qcom,max-voltage-uv", + &chip->batt_max_voltage_uv); + if (rc < 0) { + dev_err(chip->dev, "Error in reading qcom,max-voltage-uv, rc=%d\n", rc); + return rc; + } + + rc = of_property_read_u32(pdev->dev.of_node, "qcom,battery-capacity-ua", + &chip->batt_cap_uah); + if (rc < 0) { + dev_err(chip->dev, "Error in reading qcom,battery-capacity-ua, rc=%d\n", rc); + return rc; + } + // Init memif fn inlined here (chip hardware info) - rc = pmi8998_read(chip->regmap, chip->revision, REG_MEM(chip) + DIG_MINOR, 4); + rc = pmi8998_read(chip->regmap, chip->revision, REG_MEM + DIG_MINOR, 4); if (rc) { dev_err(chip->dev, "Unable to read FG revision rc=%d\n", rc); return rc; } + rc = regmap_read(chip->regmap, PMIC_SUBTYPE, &chip->subtype); + if (rc < 0) { + dev_err(chip->dev, "Unable to read FG subtype rc=%d\n", rc); + return rc; + } + dev_dbg(chip->dev, "pmi8998 revision DIG:%d.%d ANA:%d.%d\n", chip->revision[DIG_MAJOR], chip->revision[DIG_MINOR], chip->revision[ANA_MAJOR], chip->revision[ANA_MINOR]); @@ -530,7 +610,7 @@ static int pmi8998_fg_probe(struct platform_device *pdev) * IACS_INTR_SRC_SLCT is BIT(3) */ rc = pmi8998_masked_write(chip->regmap, - REG_MEM(chip) + MEM_INTF_IMA_CFG, BIT(3), BIT(3)); + REG_MEM + MEM_INTF_IMA_CFG, BIT(3), BIT(3)); if (rc) { dev_err(chip->dev, "failed to configure interrupt source %d\n", @@ -545,14 +625,14 @@ static int pmi8998_fg_probe(struct platform_device *pdev) } // Check and clear DMA errors - rc = pmi8998_read(chip->regmap, &dma_status, REG_MEM(chip) + 0x70, 1); + rc = pmi8998_read(chip->regmap, &dma_status, REG_MEM + 0x70, 1); if (rc < 0) { pr_err("failed to read dma_status, rc=%d\n", rc); return rc; } error_present = dma_status & (BIT(1) | BIT(2)); - rc = pmi8998_masked_write(chip->regmap, REG_MEM(chip) + 0x71, BIT(0), + rc = pmi8998_masked_write(chip->regmap, REG_MEM + 0x71, BIT(0), error_present ? BIT(0) : 0); if (rc < 0) { pr_err("failed to write dma_ctl, rc=%d\n", rc); diff --git a/drivers/power/supply/pmi8998_fg.h b/drivers/power/supply/pmi8998_fg.h index 81a90945c0e6..b44f88bc9395 100644 --- a/drivers/power/supply/pmi8998_fg.h +++ b/drivers/power/supply/pmi8998_fg.h @@ -10,7 +10,10 @@ #define MEM_INTF_RD_DATA0 0x67 #define MEM_INTF_WR_DATA0 0x63 -#define SMB2_CABLE_CONNECTED 0x06 +#define PMIC_SUBTYPE 0x105 + +#define PM8998_SUBTYPE 0x14 +#define PMI8998_SUBTYPE 0x15 // pm8950 / pm89988 common #define MEM_INTF_IMA_CFG 0x52 @@ -33,9 +36,9 @@ #define BATT_TEMP_LSB_MASK GENMASK(7, 0) #define BATT_TEMP_MSB_MASK GENMASK(2, 0) -#define REG_BASE(chip) (chip->base) -#define REG_BATT(chip) (chip->base + 0x100) -#define REG_MEM(chip) (chip->base + 0x400) +#define REG_BASE 0x4000 +#define REG_BATT 0x4100 +#define REG_MEM 0x4400 /* Interrupt offsets */ #define INT_RT_STS 0x10 @@ -46,9 +49,17 @@ #define PARAM_ADDR_BATT_VOLTAGE 0xa0 #define PARAM_ADDR_BATT_CURRENT 0xa2 +#define BATT_INFO_JEITA_COLD(chip) (REG_BATT + 0x62) +#define BATT_INFO_JEITA_COOL(chip) (REG_BATT + 0x63) +#define BATT_INFO_JEITA_WARM(chip) (REG_BATT + 0x64) +#define BATT_INFO_JEITA_HOT(chip) (REG_BATT + 0x65) + #define MISC_BASE 0x1600 +#define USBIN_BASE 0x1300 #define BATTERY_CHARGER_STATUS_REG(chip) (chip->chg_base + 0x06) +#define BATTERY_HEALTH_STATUS_REG(chip) (chip->chg_base + 0x07) + #define BATTERY_CHARGER_STATUS_MASK GENMASK(2, 0) #define POWER_PATH_STATUS_REG (MISC_BASE + 0x0B) @@ -87,41 +98,18 @@ static enum power_supply_property fg_properties[] = { POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, POWER_SUPPLY_PROP_VOLTAGE_MIN, - POWER_SUPPLY_PROP_TEMP, - // POWER_SUPPLY_PROP_CHARGE_NOW, - // POWER_SUPPLY_PROP_CHARGE_FULL, - POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, - POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, POWER_SUPPLY_PROP_STATUS, -}; - -struct fg_learning_data { - struct mutex learning_lock; - bool active; - int64_t cc_uah; - int learned_cc_uah; - int init_cc_pc_val; - int max_start_soc; - int max_increment; - int max_decrement; - int vbat_est_thr_uv; - int max_cap_limit; - int min_cap_limit; - int min_temp; - int max_temp; -}; - -struct battery_info { - const char *manufacturer; - const char *model; - const char *serial_num; - - int nom_cap_uah; - - int batt_max_voltage_uv_design; - int batt_max_voltage_uv; + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TEMP_MIN, + POWER_SUPPLY_PROP_TEMP_MAX, + POWER_SUPPLY_PROP_TEMP_ALERT_MIN, + POWER_SUPPLY_PROP_TEMP_ALERT_MAX, }; struct pmi8998_fg_chip { @@ -130,15 +118,16 @@ struct pmi8998_fg_chip { unsigned int chg_base; struct regmap *regmap; struct mutex lock; + unsigned int subtype; struct power_supply *bms_psy; u8 revision[4]; bool ima_supported; - struct battery_info batt_info; - - struct fg_learning_data learning_data; + int batt_cap_uah; + int batt_max_voltage_uv; + int batt_min_voltage_uv; int health; int status; From cf062a5c352146e06b3b703fd2bf905fac92751c Mon Sep 17 00:00:00 2001 From: Yassine Oudjana Date: Sat, 6 Mar 2021 07:55:07 +0400 Subject: [PATCH 0309/1823] power: pmi8998_fg: Remove some trailing spaces and tabs This should be squashed. --- drivers/power/supply/pmi8998_fg.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/power/supply/pmi8998_fg.c b/drivers/power/supply/pmi8998_fg.c index 1aea6eaf30f9..aa848e5577fe 100644 --- a/drivers/power/supply/pmi8998_fg.c +++ b/drivers/power/supply/pmi8998_fg.c @@ -22,7 +22,7 @@ /** * pmi8998_read() - Read multiple registers with regmap_bulk_read - * + * * @param map The regmap to read * @param val Pointer to read values into * @param addr Address to read from @@ -43,7 +43,7 @@ static int pmi8998_read(struct regmap *map, u8 *val, u16 addr, int len) /** * @brief pmi8998_write() - Write multiple registers with regmap_bulk_write - * + * * @param map The regmap to write * @param val Pointer to write values into * @param addr Address to write from @@ -73,7 +73,7 @@ static int pmi8998_write(struct regmap *map, u8 *val, u16 addr, int len) /** * @brief pmi8998_masked_write() - like pmi8998_write but applies * a mask first. - * + * * @param map The regmap to write * @param val Pointer to write values into * @param addr Address to write from @@ -321,7 +321,7 @@ int pmi8998_get_prop_batt_status(struct pmi8998_fg_chip *chip, int *val){ dev_err(chip->dev, "Charging status REGMAP read failed! ret=%d\n", rc); return rc; } - + stat = stat & BATTERY_CHARGER_STATUS_MASK; dev_dbg(chip->dev, "Charging status : %d!\n", stat); @@ -338,7 +338,7 @@ int pmi8998_get_prop_batt_status(struct pmi8998_fg_chip *chip, int *val){ case DISABLE_CHARGE: *val = POWER_SUPPLY_STATUS_NOT_CHARGING; break; - default: + default: *val = POWER_SUPPLY_STATUS_UNKNOWN; break; } @@ -366,7 +366,7 @@ int pmi8998_get_prop_health_status(struct pmi8998_fg_chip *chip, int *val){ *val = POWER_SUPPLY_HEALTH_WARM; else *val = POWER_SUPPLY_HEALTH_GOOD; - + return rc; } @@ -520,7 +520,7 @@ irqreturn_t pmi8998_handle_usb_plugin(int irq, void *data){ } else { val.intval = POWER_SUPPLY_STATUS_DISCHARGING; power_supply_set_property(chip->bms_psy, POWER_SUPPLY_PROP_STATUS, &val); - } + } dev_dbg(chip->dev, "USB IRQ: %s\n", vbus_rising ? "attached" : "detached"); power_supply_changed(chip->bms_psy); @@ -578,7 +578,7 @@ static int pmi8998_fg_probe(struct platform_device *pdev) dev_err(chip->dev, "Error in reading qcom,max-voltage-uv, rc=%d\n", rc); return rc; } - + rc = of_property_read_u32(pdev->dev.of_node, "qcom,battery-capacity-ua", &chip->batt_cap_uah); if (rc < 0) { @@ -602,7 +602,7 @@ static int pmi8998_fg_probe(struct platform_device *pdev) dev_dbg(chip->dev, "pmi8998 revision DIG:%d.%d ANA:%d.%d\n", chip->revision[DIG_MAJOR], chip->revision[DIG_MINOR], chip->revision[ANA_MAJOR], chip->revision[ANA_MINOR]); - + /* * Change the FG_MEM_INT interrupt to track IACS_READY * condition instead of end-of-transaction. This makes sure From f96cfaff293c44369b4cb6375de9091e6ebff573 Mon Sep 17 00:00:00 2001 From: Yassine Oudjana Date: Fri, 5 Mar 2021 21:37:13 +0400 Subject: [PATCH 0310/1823] power: pmi8998_fg: Rename to qcom_fg and add support for PMI8994/6 This adds support for accessing SRAM. That includes requesting and releasing access to SRAM by setting some registers and handling the mem-avail IRQ, configuring access, and reading from/writing to it. Pre-gen3 fuel gauges require reading from SRAM to get voltage, current and temperature data. Getting capacity is identical to gen3. Tested on Xiaomi Mi Note 2 with PMI8996. --- drivers/power/supply/Kconfig | 8 +- drivers/power/supply/Makefile | 2 +- .../power/supply/{pmi8998_fg.c => qcom_fg.c} | 599 ++++++++++++++++-- .../power/supply/{pmi8998_fg.h => qcom_fg.h} | 90 ++- 4 files changed, 599 insertions(+), 100 deletions(-) rename drivers/power/supply/{pmi8998_fg.c => qcom_fg.c} (50%) rename drivers/power/supply/{pmi8998_fg.h => qcom_fg.h} (60%) diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 8ad785d54a4a..f2e743ff29f5 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -934,13 +934,13 @@ config CHARGER_QCOM_SMB2 Say Y or M here to enable reporting the charger status and rate on supported platforms such as Snapdragon 845 and 835 based phones. -config BATTERY_PMI8998_FG - tristate "Qualcomm PMI8998 fuel gauge driver" +config BATTERY_QCOM_FG + tristate "Qualcomm PMIC fuel gauge driver" depends on MFD_SPMI_PMIC help - Say Y here to enable the Qualcomm PMI8998 Fuel Gauge driver. This + Say Y here to enable the Qualcomm PMIC Fuel Gauge driver. This adds support for battery fuel gauging and state of charge of - battery connected tothe fuel gauge. The state of charge is + battery connected to the fuel gauge. The state of charge is reported through a BMS power supply property and also sends uevents when the capacity is updated. diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index dfa2b0382a46..c0e39673363f 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -111,4 +111,4 @@ obj-$(CONFIG_BATTERY_ACER_A500) += acer_a500_battery.o obj-$(CONFIG_BATTERY_SURFACE) += surface_battery.o obj-$(CONFIG_CHARGER_SURFACE) += surface_charger.o obj-$(CONFIG_BATTERY_UG3105) += ug3105_battery.o -obj-$(CONFIG_BATTERY_PMI8998_FG) += pmi8998_fg.o +obj-$(CONFIG_BATTERY_QCOM_FG) += qcom_fg.o diff --git a/drivers/power/supply/pmi8998_fg.c b/drivers/power/supply/qcom_fg.c similarity index 50% rename from drivers/power/supply/pmi8998_fg.c rename to drivers/power/supply/qcom_fg.c index aa848e5577fe..dc41f6072828 100644 --- a/drivers/power/supply/pmi8998_fg.c +++ b/drivers/power/supply/qcom_fg.c @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -14,14 +15,14 @@ #include #include -#include "pmi8998_fg.h" +#include "qcom_fg.h" /************************ * IO FUNCTIONS * **********************/ /** - * pmi8998_read() - Read multiple registers with regmap_bulk_read + * qcom_fg_read() - Read multiple registers with regmap_bulk_read * * @param map The regmap to read * @param val Pointer to read values into @@ -29,7 +30,7 @@ * @param len Number of registers (bytes) to read * @return int 0 on success, negative errno on error */ -static int pmi8998_read(struct regmap *map, u8 *val, u16 addr, int len) +static int qcom_fg_read(struct regmap *map, u8 *val, u16 addr, int len) { if ((addr & 0xff00) == 0) { pr_err("base cannot be zero base=0x%02x\n", addr); @@ -42,7 +43,7 @@ static int pmi8998_read(struct regmap *map, u8 *val, u16 addr, int len) } /** - * @brief pmi8998_write() - Write multiple registers with regmap_bulk_write + * @brief qcom_fg_write() - Write multiple registers with regmap_bulk_write * * @param map The regmap to write * @param val Pointer to write values into @@ -50,7 +51,7 @@ static int pmi8998_read(struct regmap *map, u8 *val, u16 addr, int len) * @param len Number of registers (bytes) to write * @return int 0 on success, negative errno on error */ -static int pmi8998_write(struct regmap *map, u8 *val, u16 addr, int len) +static int qcom_fg_write(struct regmap *map, u8 *val, u16 addr, int len) { int rc; bool sec_access = (addr & 0xff) > 0xd0; @@ -71,7 +72,7 @@ static int pmi8998_write(struct regmap *map, u8 *val, u16 addr, int len) } /** - * @brief pmi8998_masked_write() - like pmi8998_write but applies + * @brief qcom_fg_masked_write() - like qcom_fg_write but applies * a mask first. * * @param map The regmap to write @@ -80,19 +81,19 @@ static int pmi8998_write(struct regmap *map, u8 *val, u16 addr, int len) * @param len Number of registers (bytes) to write * @return int 0 on success, negative errno on error */ -static int pmi8998_masked_write(struct regmap *map, u16 addr, +static int qcom_fg_masked_write(struct regmap *map, u16 addr, u8 mask, u8 val) { int error; u8 reg; - error = pmi8998_read(map, ®, addr, 1); + error = qcom_fg_read(map, ®, addr, 1); if (error) return error; reg &= ~mask; reg |= val & mask; - error = pmi8998_write(map, ®, addr, 1); + error = qcom_fg_write(map, ®, addr, 1); return error; } @@ -111,14 +112,243 @@ static int64_t twos_compliment_extend(int64_t val, int nbytes) return val; } +/************************ + * SRAM FUNCTIONS + * **********************/ + +/** + * qcom_fg_sram_check_access() - Check if SRAM is accessible + * + * @param chip Pointer to chip + * @return bool true if accessible, false otherwise + */ +static bool qcom_fg_sram_check_access(struct qcom_fg_chip *chip) +{ + int rc; + u8 mem_if_status; + + rc = qcom_fg_read(chip->regmap, &mem_if_status, + REG_MEM + MEM_INTF_STS, 1); + + if(!(mem_if_status & MEM_INTF_AVAIL)) + return false; + + rc = qcom_fg_read(chip->regmap, &mem_if_status, + REG_MEM + MEM_INTF_CFG, 1); + + if(rc) + return false; + + return !!(mem_if_status & RIF_MEM_ACCESS_REQ); +} + +/** + * qcom_fg_sram_request_access() - Request access to SRAM and wait for it + * + * @param chip Pointer to chip + * @return int 0 on success, negative errno on error + */ +static int qcom_fg_sram_request_access(struct qcom_fg_chip *chip) +{ + int rc; + + if(!qcom_fg_sram_check_access(chip)) { + rc = qcom_fg_masked_write(chip->regmap, REG_MEM + MEM_INTF_CFG, + RIF_MEM_ACCESS_REQ, RIF_MEM_ACCESS_REQ); + if (rc) { + dev_err(chip->dev, + "Failed to set SRAM access request bit: %d\n", rc); + return rc; + } + } + + /* Wait to get access to SRAM, and try again if interrupted */ + do { + rc = wait_for_completion_interruptible_timeout( + &chip->sram_access_granted, + msecs_to_jiffies(MEM_IF_TIMEOUT_MS)); + } while(rc == -ERESTARTSYS); + + if(rc <= 0) { + rc = -ETIMEDOUT; + return rc; + } + + return 0; +} + +/** + * qcom_fg_sram_release_access() - Release access to SRAM + * + * @param chip Pointer to chip + * @return int 0 on success, negative errno on error + */ +static int qcom_fg_sram_release_access(struct qcom_fg_chip *chip) +{ + int rc; + + rc = qcom_fg_masked_write(chip->regmap, REG_MEM + MEM_INTF_CFG, + RIF_MEM_ACCESS_REQ, 0); + if (rc) { + dev_err(chip->dev, + "Failed to set SRAM access request bit: %d\n", rc); + return rc; + } + + reinit_completion(&chip->sram_access_granted); + + return rc; +} + +/** + * qcom_fg_sram_config_access() - Configure access to SRAM + * + * @param chip Pointer to chip + * @param write 0 for read access, 1 for write access + * @param burst 1 to access mutliple addresses successively + * @return int 0 on success, negative errno on error + */ +static int qcom_fg_sram_config_access(struct qcom_fg_chip *chip, + bool write, bool burst) +{ + int rc; + u8 intf_ctl = (write ? MEM_INTF_CTL_WR_EN : 0) + | (burst ? MEM_INTF_CTL_BURST : 0); + + rc = qcom_fg_write(chip->regmap, &intf_ctl, + REG_MEM + MEM_INTF_CTL, 1); + + if(rc) + dev_err(chip->dev, + "Failed to configure SRAM access: %d\n", rc); + + return rc; +} + +/** + * qcom_fg_sram_read() - Read data from SRAM + * + * @param chip Pointer to chip + * @param val Pointer to read values into + * @param addr Address to read from + * @param len Number of bytes to read + * @return int 0 on success, negative errno on error + */ +static int qcom_fg_sram_read(struct qcom_fg_chip *chip, + u8 *val, u16 addr, int len, int offset) +{ + int rc; + u8 *rd_data = val; + + rc = qcom_fg_sram_request_access(chip); + if(rc) { + dev_err(chip->dev, "Failed to request SRAM access: %d", rc); + return rc; + } + + dev_dbg(chip->dev, + "Reading address 0x%x with offset %d of length %d from SRAM", + addr, len, offset); + + rc = qcom_fg_sram_config_access(chip, 0, (len > 4)); + if(rc) { + dev_err(chip->dev, "Failed to configure SRAM access: %d", rc); + return rc; + } + + while(len > 0) { + /* Set SRAM address register */ + rc = qcom_fg_write(chip->regmap, (u8 *) &addr, + REG_MEM + MEM_INTF_ADDR_LSB, 2); + if(rc) { + dev_err(chip->dev, "Failed to set SRAM address: %d", rc); + return rc; + } + + rc = qcom_fg_read(chip->regmap, rd_data, + REG_MEM + MEM_INTF_RD_DATA0 + offset, len); + + addr += 4; + + if(rc) + return rc; + + rd_data += 4 - offset; + len -= 4 - offset; + offset = 0; + } + + rc = qcom_fg_sram_release_access(chip); + + return rc; +} + +/** + * qcom_fg_sram_write() - Write data to SRAM + * + * @param chip Pointer to chip + * @param val Pointer to write values into + * @param addr Address to write to + * @param len Number of bytes to write + * @return int 0 on success, negative errno on error + */ +static int qcom_fg_sram_write(struct qcom_fg_chip *chip, + u8 *val, u16 addr, int len, int offset) +{ + int rc; + u8 *wr_data = val; + + rc = qcom_fg_sram_request_access(chip); + if(rc) { + dev_err(chip->dev, "Failed to request SRAM access: %d", rc); + return rc; + } + + dev_dbg(chip->dev, + "Reading address 0x%x with offset %d of length %d from SRAM", + addr, len, offset); + + rc = qcom_fg_sram_config_access(chip, 1, (len > 4)); + if(rc) { + dev_err(chip->dev, "Failed to configure SRAM access: %d", rc); + return rc; + } + + while(len > 0) { + /* Set SRAM address register */ + rc = qcom_fg_write(chip->regmap, (u8 *) &addr, + REG_MEM + MEM_INTF_ADDR_LSB, 2); + if(rc) { + dev_err(chip->dev, "Failed to set SRAM address: %d", rc); + return rc; + } + + rc = qcom_fg_write(chip->regmap, wr_data, + REG_MEM + MEM_INTF_WR_DATA0 + offset, len); + + addr += 4; + + if(rc) + return rc; + + wr_data += 4 - offset; + len -= 4 - offset; + offset = 0; + } + + rc = qcom_fg_sram_release_access(chip); + + return rc; +} + /************************* * Battery Status RW * ***********************/ -static int pmi8998_fg_get_capacity(struct pmi8998_fg_chip *chip, int *val) +static int qcom_fg_get_capacity(struct qcom_fg_chip *chip, int *val) { u8 cap[2]; - int error = pmi8998_read(chip->regmap, cap, REG_BASE + BATT_MONOTONIC_SOC, 2); + int error = qcom_fg_read(chip->regmap, cap, REG_BASE + BATT_MONOTONIC_SOC, 2); if (error) return error; if (cap[0] != cap[1]) { @@ -128,12 +358,66 @@ static int pmi8998_fg_get_capacity(struct pmi8998_fg_chip *chip, int *val) return 0; } -static int pmi8998_fg_get_temperature(struct pmi8998_fg_chip *chip, int *val) +static int qcom_fg_get_temperature(struct qcom_fg_chip *chip, int *val) { int rc, temp; u8 readval[2]; - rc = pmi8998_read(chip->regmap, readval, REG_BATT + PARAM_ADDR_BATT_TEMP, 2); + rc = qcom_fg_sram_read(chip, readval, 0x550, 2, 2); + if(rc) { + dev_err(chip->dev, "Failed to read temperature: %d", rc); + return rc; + } + + temp = readval[1] << 8 | readval[0]; + *val = temp * 625 / 1000 - 2730; + return 0; +} + +static int qcom_fg_get_current(struct qcom_fg_chip *chip, int *val) +{ + int rc, temp; + u8 readval[2]; + + rc = qcom_fg_sram_read(chip, readval, 0x5CC, 2, 3); + if(rc) { + dev_err(chip->dev, "Failed to read current: %d", rc); + return rc; + } + //handle rev 1 too + temp = readval[1] << 8 | readval[0]; + temp = twos_compliment_extend(temp, 15); + *val = div_s64((s64)temp * 152587, 1000); + return 0; +} + +static int qcom_fg_get_voltage(struct qcom_fg_chip *chip, int *val) +{ + int rc, temp; + u8 readval[2]; + + rc = qcom_fg_sram_read(chip, readval, 0x5CC, 2, 1); + if(rc) { + dev_err(chip->dev, "Failed to read voltage: %d", rc); + return rc; + } + + temp = readval[1] << 8 | readval[0]; + *val = div_u64((u64)temp * 152587, 1000); + + return 0; +} + +/************************* + * Battery Status RW, Gen3 + * ***********************/ + +static int qcom_fg_gen3_get_temperature(struct qcom_fg_chip *chip, int *val) +{ + int rc, temp; + u8 readval[2]; + + rc = qcom_fg_read(chip->regmap, readval, REG_BATT + PARAM_ADDR_BATT_TEMP, 2); if (rc) { pr_err("Failed to read temperature\n"); return rc; @@ -146,12 +430,12 @@ static int pmi8998_fg_get_temperature(struct pmi8998_fg_chip *chip, int *val) return 0; } -static int pmi8998_fg_get_current(struct pmi8998_fg_chip *chip, int *val) +static int qcom_fg_gen3_get_current(struct qcom_fg_chip *chip, int *val) { int rc, temp; u8 readval[2]; - rc = pmi8998_read(chip->regmap, readval, REG_BATT + PARAM_ADDR_BATT_CURRENT, 2); + rc = qcom_fg_read(chip->regmap, readval, REG_BATT + PARAM_ADDR_BATT_CURRENT, 2); if (rc) { pr_err("Failed to read current\n"); return rc; @@ -163,12 +447,12 @@ static int pmi8998_fg_get_current(struct pmi8998_fg_chip *chip, int *val) return 0; } -static int pmi8998_fg_get_voltage(struct pmi8998_fg_chip *chip, int *val) +static int qcom_fg_gen3_get_voltage(struct qcom_fg_chip *chip, int *val) { int rc, temp; u8 readval[2]; - rc = pmi8998_read(chip->regmap, readval, REG_BATT + PARAM_ADDR_BATT_VOLTAGE, 2); + rc = qcom_fg_read(chip->regmap, readval, REG_BATT + PARAM_ADDR_BATT_VOLTAGE, 2); if (rc) { pr_err("Failed to read voltage\n"); return rc; @@ -183,13 +467,13 @@ static int pmi8998_fg_get_voltage(struct pmi8998_fg_chip *chip, int *val) * Init stuff * ******************/ -static int pmi8998_iacs_clear_sequence(struct pmi8998_fg_chip *chip) +static int qcom_fg_iacs_clear_sequence(struct qcom_fg_chip *chip) { int rc = 0; u8 temp; /* clear the error */ - rc = pmi8998_masked_write(chip->regmap, REG_MEM + MEM_INTF_IMA_CFG, + rc = qcom_fg_masked_write(chip->regmap, REG_MEM + MEM_INTF_IMA_CFG, BIT(2), BIT(2)); if (rc) { pr_err("Error writing to IMA_CFG, rc=%d\n", rc); @@ -197,26 +481,26 @@ static int pmi8998_iacs_clear_sequence(struct pmi8998_fg_chip *chip) } temp = 0x4; - rc = pmi8998_write(chip->regmap, &temp, REG_MEM + MEM_INTF_ADDR_LSB + 1, 1); + rc = qcom_fg_write(chip->regmap, &temp, REG_MEM + MEM_INTF_ADDR_LSB + 1, 1); if (rc) { pr_err("Error writing to MEM_INTF_ADDR_MSB, rc=%d\n", rc); return rc; } temp = 0x0; - rc = pmi8998_write(chip->regmap, &temp, REG_MEM + MEM_INTF_WR_DATA0 + 3, 1); + rc = qcom_fg_write(chip->regmap, &temp, REG_MEM + MEM_INTF_WR_DATA0 + 3, 1); if (rc) { pr_err("Error writing to WR_DATA3, rc=%d\n", rc); return rc; } - rc = pmi8998_read(chip->regmap, &temp, REG_MEM + MEM_INTF_RD_DATA0 + 3, 1); + rc = qcom_fg_read(chip->regmap, &temp, REG_MEM + MEM_INTF_RD_DATA0 + 3, 1); if (rc) { pr_err("Error writing to RD_DATA3, rc=%d\n", rc); return rc; } - rc = pmi8998_masked_write(chip->regmap, REG_MEM + MEM_INTF_IMA_CFG, + rc = qcom_fg_masked_write(chip->regmap, REG_MEM + MEM_INTF_IMA_CFG, BIT(2), 0); if (rc) { pr_err("Error writing to IMA_CFG, rc=%d\n", rc); @@ -225,21 +509,21 @@ static int pmi8998_iacs_clear_sequence(struct pmi8998_fg_chip *chip) return rc; } -static int pmi8998_clear_ima(struct pmi8998_fg_chip *chip, +static int qcom_fg_clear_ima(struct qcom_fg_chip *chip, bool check_hw_sts) { int rc = 0, ret = 0; u8 err_sts = 0, exp_sts = 0, hw_sts = 0; bool run_err_clr_seq = false; - rc = pmi8998_read(chip->regmap, &err_sts, + rc = qcom_fg_read(chip->regmap, &err_sts, REG_MEM + MEM_INTF_IMA_ERR_STS, 1); if (rc) { dev_err(chip->dev, "failed to read IMA_ERR_STS, rc=%d\n", rc); return rc; } - rc = pmi8998_read(chip->regmap, &exp_sts, + rc = qcom_fg_read(chip->regmap, &exp_sts, REG_MEM + MEM_INTF_IMA_EXP_STS, 1); if (rc) { dev_err(chip->dev, "Error in reading IMA_EXP_STS, rc=%d\n", rc); @@ -247,7 +531,7 @@ static int pmi8998_clear_ima(struct pmi8998_fg_chip *chip, } if (check_hw_sts) { - rc = pmi8998_read(chip->regmap, &hw_sts, + rc = qcom_fg_read(chip->regmap, &hw_sts, REG_MEM + MEM_INTF_IMA_HW_STS, 1); if (rc) { dev_err(chip->dev, "Error in reading IMA_HW_STS, rc=%d\n", rc); @@ -272,7 +556,7 @@ static int pmi8998_clear_ima(struct pmi8998_fg_chip *chip, } if (run_err_clr_seq) { - ret = pmi8998_iacs_clear_sequence(chip); + ret = qcom_fg_iacs_clear_sequence(chip); if (!ret) return -EAGAIN; else @@ -282,7 +566,7 @@ static int pmi8998_clear_ima(struct pmi8998_fg_chip *chip, return rc; } -int pmi8998_get_prop_usb_online(struct pmi8998_fg_chip *chip, int *val){ +int qcom_fg_get_prop_usb_online(struct qcom_fg_chip *chip, int *val){ unsigned int stat; int rc; @@ -297,13 +581,13 @@ int pmi8998_get_prop_usb_online(struct pmi8998_fg_chip *chip, int *val){ return rc; } -int pmi8998_get_prop_batt_status(struct pmi8998_fg_chip *chip, int *val){ +int qcom_fg_get_prop_batt_status(struct qcom_fg_chip *chip, int *val){ int usb_online_val; unsigned int stat; int rc; bool usb_online; - rc = pmi8998_get_prop_usb_online(chip, &usb_online_val); + rc = qcom_fg_get_prop_usb_online(chip, &usb_online_val); if (rc < 0) { dev_err(chip->dev, "Couldn't get usb online property rc=%d\n", rc); return rc; @@ -346,7 +630,7 @@ int pmi8998_get_prop_batt_status(struct pmi8998_fg_chip *chip, int *val){ return rc; } -int pmi8998_get_prop_health_status(struct pmi8998_fg_chip *chip, int *val){ +int qcom_fg_get_prop_health_status(struct qcom_fg_chip *chip, int *val){ unsigned int stat; int rc; @@ -370,7 +654,42 @@ int pmi8998_get_prop_health_status(struct pmi8998_fg_chip *chip, int *val){ return rc; } -static int pmi8998_get_temp_threshold(struct pmi8998_fg_chip *chip, +static int qcom_fg_get_temp_threshold(struct qcom_fg_chip *chip, + enum power_supply_property psp, int *val) +{ + int rc; + u8 temp; + int offset; + + switch (psp) { + case POWER_SUPPLY_PROP_TEMP_MIN: + offset = 0; + break; + case POWER_SUPPLY_PROP_TEMP_MAX: + offset = 1; + break; + case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: + offset = 2; + break; + case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: + offset = 3; + break; + default: + return -EINVAL; + } + + rc = qcom_fg_sram_read(chip, &temp, 0x454, 1, offset); + if (rc < 0) { + dev_err(chip->dev, "Error in reading jeita level for psp:%d, rc=%d\n", psp, rc); + return rc; + } + + *val = (temp - 30) * 10; + + return 0; +} + +static int qcom_fg_gen3_get_temp_threshold(struct qcom_fg_chip *chip, enum power_supply_property psp, int *val) { int rc; @@ -394,7 +713,7 @@ static int pmi8998_get_temp_threshold(struct pmi8998_fg_chip *chip, return -EINVAL; } - rc = pmi8998_read(chip->regmap, &temp, reg, 1); + rc = qcom_fg_read(chip->regmap, &temp, reg, 1); if (rc < 0) { dev_err(chip->dev, "Error in reading jeita level for psp:%d, rc=%d\n", psp, rc); return rc; @@ -405,17 +724,66 @@ static int pmi8998_get_temp_threshold(struct pmi8998_fg_chip *chip, return 0; } -static void fg_get_model_name(struct pmi8998_fg_chip *chip, union power_supply_propval *val) +static int qcom_fg_set_temp_threshold(struct qcom_fg_chip *chip, + enum power_supply_property psp, int val) +{ + int rc; + u8 temp; + int offset; + + switch (psp) { + case POWER_SUPPLY_PROP_TEMP_MIN: + offset = 0; + break; + case POWER_SUPPLY_PROP_TEMP_MAX: + offset = 1; + break; + case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: + offset = 2; + break; + case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: + offset = 3; + break; + default: + return -EINVAL; + } + + temp = val / 10 + 30; + + rc = qcom_fg_sram_write(chip, &temp, 0x454, 1, offset); + if (rc < 0) { + dev_err(chip->dev, "Error in writing jeita level for psp:%d, rc=%d\n", psp, rc); + return rc; + } + + return 0; +} + +static int qcom_fg_gen3_set_temp_threshold(struct qcom_fg_chip *chip, + enum power_supply_property psp, int val) +{ + /* Not implemented yet */ + return -ENOTSUPP; +} + + +static void qcom_fg_get_model_name(struct qcom_fg_chip *chip, union power_supply_propval *val) { switch (chip->subtype) { + case PMI8994_SUBTYPE: + val->strval = "PMI8994 Battery"; + break; + case PMI8996_SUBTYPE: + val->strval = "PMI8996 Battery"; + break; case PMI8998_SUBTYPE: val->strval = "PMI8998 Battery"; break; case PM8998_SUBTYPE: val->strval = "PM8998 Battery"; break; - //handle pm660 and other socs that use fg3 + /* Handle other PMICs */ default: val->strval = "Unknown PMIC Battery"; } @@ -425,7 +793,7 @@ static int fg_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct pmi8998_fg_chip *chip = power_supply_get_drvdata(psy); + struct qcom_fg_chip *chip = power_supply_get_drvdata(psy); int error = 0; dev_dbg(chip->dev, "Getting property: %d", psp); @@ -435,19 +803,19 @@ static int fg_get_property(struct power_supply *psy, val->strval = "Qualcomm"; break; case POWER_SUPPLY_PROP_MODEL_NAME: - fg_get_model_name(chip, val); + qcom_fg_get_model_name(chip, val); break; case POWER_SUPPLY_PROP_TECHNOLOGY: val->intval = POWER_SUPPLY_TECHNOLOGY_LION; break; case POWER_SUPPLY_PROP_CAPACITY: - error = pmi8998_fg_get_capacity(chip, &val->intval); + error = chip->ops->get_capacity(chip, &val->intval); break; case POWER_SUPPLY_PROP_CURRENT_NOW: - error = pmi8998_fg_get_current(chip, &val->intval); + error = chip->ops->get_current(chip, &val->intval); break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: - error = pmi8998_fg_get_voltage(chip, &val->intval); + error = chip->ops->get_voltage(chip, &val->intval); break; case POWER_SUPPLY_PROP_VOLTAGE_MIN: case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: @@ -458,23 +826,23 @@ static int fg_get_property(struct power_supply *psy, val->intval = chip->batt_max_voltage_uv; break; case POWER_SUPPLY_PROP_STATUS: - error = pmi8998_get_prop_batt_status(chip, &val->intval); + error = chip->ops->get_batt_status(chip, &val->intval); break; case POWER_SUPPLY_PROP_HEALTH: - error = pmi8998_get_prop_health_status(chip, &val->intval); + error = chip->ops->get_health_status(chip, &val->intval); break; case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: case POWER_SUPPLY_PROP_CHARGE_FULL: /* TODO: Implement capacity learning */ val->intval = chip->batt_cap_uah; break; case POWER_SUPPLY_PROP_TEMP: - error = pmi8998_fg_get_temperature(chip, &val->intval); + error = chip->ops->get_temperature(chip, &val->intval); break; case POWER_SUPPLY_PROP_TEMP_MIN: case POWER_SUPPLY_PROP_TEMP_MAX: case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: - error = pmi8998_get_temp_threshold(chip, psp, &val->intval); + error = chip->ops->get_temp_threshold(chip, psp, &val->intval); break; //POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,POWER_SUPPLY_PROP_TIME_TO_FULL_AVG - calculate time remaining for full charge - implementable //POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG - calculate time remaining when discharging - implementable @@ -500,8 +868,8 @@ static const struct power_supply_desc bms_psy_desc = { .get_property = fg_get_property, }; -irqreturn_t pmi8998_handle_usb_plugin(int irq, void *data){ - struct pmi8998_fg_chip *chip = data; +irqreturn_t qcom_fg_handle_usb_plugin(int irq, void *data){ + struct qcom_fg_chip *chip = data; int rc; unsigned int stat; bool vbus_rising; @@ -527,10 +895,47 @@ irqreturn_t pmi8998_handle_usb_plugin(int irq, void *data){ return IRQ_HANDLED; } -static int pmi8998_fg_probe(struct platform_device *pdev) +irqreturn_t qcom_fg_handle_mem_avail(int irq, void *data){ + struct qcom_fg_chip *chip = data; + + if(qcom_fg_sram_check_access(chip)) { + complete_all(&chip->sram_access_granted); + dev_dbg(chip->dev, "SRAM access granted"); + } else { + dev_dbg(chip->dev, "SRAM access revoked"); + } + + return IRQ_HANDLED; +} + +/* Pre-Gen3 fuel gauge. PMI8996 and older */ +static const struct qcom_fg_ops ops_fg = { + .get_capacity = qcom_fg_get_capacity, + .get_temperature = qcom_fg_get_temperature, + .get_current = qcom_fg_get_current, + .get_voltage = qcom_fg_get_voltage, + .get_batt_status = qcom_fg_get_prop_batt_status, + .get_health_status = qcom_fg_get_prop_health_status, + .get_temp_threshold = qcom_fg_get_temp_threshold, + .set_temp_threshold = qcom_fg_set_temp_threshold, +}; + +/* Gen3 fuel gauge. PMI8998 and newer */ +static const struct qcom_fg_ops ops_fg_gen3 = { + .get_capacity = qcom_fg_get_capacity, + .get_temperature = qcom_fg_gen3_get_temperature, + .get_current = qcom_fg_gen3_get_current, + .get_voltage = qcom_fg_gen3_get_voltage, + .get_batt_status = qcom_fg_get_prop_batt_status, + .get_health_status = qcom_fg_get_prop_health_status, + .get_temp_threshold = qcom_fg_gen3_get_temp_threshold, + .set_temp_threshold = qcom_fg_gen3_set_temp_threshold, +}; + +static int qcom_fg_probe(struct platform_device *pdev) { struct power_supply_config supply_config = {}; - struct pmi8998_fg_chip *chip; + struct qcom_fg_chip *chip; const __be32 *prop_addr; int rc = 0, irq; u8 dma_status; @@ -542,6 +947,8 @@ static int pmi8998_fg_probe(struct platform_device *pdev) } chip->dev = &pdev->dev; + chip->ops = of_device_get_match_data(&pdev->dev); + mutex_init(&chip->lock); chip->regmap = dev_get_regmap(pdev->dev.parent, NULL); @@ -587,7 +994,7 @@ static int pmi8998_fg_probe(struct platform_device *pdev) } // Init memif fn inlined here (chip hardware info) - rc = pmi8998_read(chip->regmap, chip->revision, REG_MEM + DIG_MINOR, 4); + rc = qcom_fg_read(chip->regmap, chip->revision, REG_MEM + DIG_MINOR, 4); if (rc) { dev_err(chip->dev, "Unable to read FG revision rc=%d\n", rc); return rc; @@ -599,7 +1006,7 @@ static int pmi8998_fg_probe(struct platform_device *pdev) return rc; } - dev_dbg(chip->dev, "pmi8998 revision DIG:%d.%d ANA:%d.%d\n", + dev_dbg(chip->dev, "PMIC revision DIG:%d.%d ANA:%d.%d\n", chip->revision[DIG_MAJOR], chip->revision[DIG_MINOR], chip->revision[ANA_MAJOR], chip->revision[ANA_MINOR]); @@ -609,7 +1016,7 @@ static int pmi8998_fg_probe(struct platform_device *pdev) * that the next transaction starts only after the hw is ready. * IACS_INTR_SRC_SLCT is BIT(3) */ - rc = pmi8998_masked_write(chip->regmap, + rc = qcom_fg_masked_write(chip->regmap, REG_MEM + MEM_INTF_IMA_CFG, BIT(3), BIT(3)); if (rc) { dev_err(chip->dev, @@ -618,21 +1025,21 @@ static int pmi8998_fg_probe(struct platform_device *pdev) return rc; } - rc = pmi8998_clear_ima(chip, true); + rc = qcom_fg_clear_ima(chip, true); if (rc && rc != -EAGAIN) { dev_err(chip->dev, "Error clearing IMA, exception rc=%d", rc); return rc; } // Check and clear DMA errors - rc = pmi8998_read(chip->regmap, &dma_status, REG_MEM + 0x70, 1); + rc = qcom_fg_read(chip->regmap, &dma_status, REG_MEM + 0x70, 1); if (rc < 0) { pr_err("failed to read dma_status, rc=%d\n", rc); return rc; } error_present = dma_status & (BIT(1) | BIT(2)); - rc = pmi8998_masked_write(chip->regmap, REG_MEM + 0x71, BIT(0), + rc = qcom_fg_masked_write(chip->regmap, REG_MEM + 0x71, BIT(0), error_present ? BIT(0) : 0); if (rc < 0) { pr_err("failed to write dma_ctl, rc=%d\n", rc); @@ -651,39 +1058,90 @@ static int pmi8998_fg_probe(struct platform_device *pdev) platform_set_drvdata(pdev, chip); - irq = of_irq_get_byname(pdev->dev.of_node, "usb-plugin"); - if (irq < 0) { - dev_err(&pdev->dev, "Couldn't get irq usb-plugin byname\n"); - return irq; + /* Initialize IRQs */ + switch(chip->subtype) + { + case PMI8994_SUBTYPE: + case PMI8996_SUBTYPE: + irq = of_irq_get_byname(pdev->dev.of_node, "mem-avail"); + if (irq < 0) { + dev_err(&pdev->dev, "Couldn't get irq mem-avail byname\n"); + return irq; + } + + rc = devm_request_threaded_irq(chip->dev, irq, NULL, + qcom_fg_handle_mem_avail, + IRQF_ONESHOT, "mem-avail", chip); + if (rc < 0) { + pr_err("Couldn't request irq %d\n", irq); + return rc; + } + + init_completion(&chip->sram_access_granted); + break; + + case PM8998_SUBTYPE: + case PMI8998_SUBTYPE: + irq = of_irq_get_byname(pdev->dev.of_node, "usb-plugin"); + if (irq < 0) { + dev_err(&pdev->dev, "Couldn't get irq usb-plugin byname\n"); + return irq; + } + + rc = devm_request_threaded_irq(chip->dev, irq, NULL, + qcom_fg_handle_usb_plugin, + IRQF_ONESHOT, "usb-plugin", chip); + if (rc < 0) { + pr_err("Couldn't request irq %d\n", irq); + return rc; + } + + break; } - rc = devm_request_threaded_irq(chip->dev, irq, NULL, - pmi8998_handle_usb_plugin, - IRQF_ONESHOT, "usb-plugin", chip); - if (rc < 0) { - pr_err("Couldn't request irq %d\n", irq); + // Set default temperature thresholds + rc = chip->ops->set_temp_threshold(chip, + POWER_SUPPLY_PROP_TEMP_MIN, + BATT_TEMP_JEITA_COLD); + rc = chip->ops->set_temp_threshold(chip, + POWER_SUPPLY_PROP_TEMP_MAX, + BATT_TEMP_JEITA_WARM); + rc = chip->ops->set_temp_threshold(chip, + POWER_SUPPLY_PROP_TEMP_ALERT_MIN, + BATT_TEMP_JEITA_COOL); + rc = chip->ops->set_temp_threshold(chip, + POWER_SUPPLY_PROP_TEMP_ALERT_MAX, + BATT_TEMP_JEITA_HOT); + if(rc == -ENOTSUPP) { + dev_warn(chip->dev, + "Setting temperature thresholds not supported"); + } + else if(rc < 0) { + dev_err(chip->dev, + "Setting temperature thresholds failed: %d\n", rc); return rc; } return 0; } -static int pmi8998_fg_remove(struct platform_device *pdev) +static int qcom_fg_remove(struct platform_device *pdev) { return 0; } static const struct of_device_id fg_match_id_table[] = { - { .compatible = "qcom,pmi8998-fg" }, + { .compatible = "qcom,pmi8994-fg", .data = &ops_fg }, + { .compatible = "qcom,pmi8998-fg", .data = &ops_fg_gen3 }, { /* sentinal */ } }; MODULE_DEVICE_TABLE(of, fg_match_id_table); static struct platform_driver qcom_fg_driver = { - .probe = pmi8998_fg_probe, - .remove = pmi8998_fg_remove, + .probe = qcom_fg_probe, + .remove = qcom_fg_remove, .driver = { - .name = "pmi8998-fg", + .name = "qcom-fg", .of_match_table = fg_match_id_table, }, }; @@ -692,5 +1150,6 @@ module_platform_driver(qcom_fg_driver); MODULE_AUTHOR("Caleb Connolly "); MODULE_AUTHOR("Joel Selvaraj "); -MODULE_DESCRIPTION("Qualcomm PMI8998 Fuel Guage Driver"); +MODULE_AUTHOR("Yassine Oudjana "); +MODULE_DESCRIPTION("Qualcomm PMIC Fuel Gauge Driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/supply/pmi8998_fg.h b/drivers/power/supply/qcom_fg.h similarity index 60% rename from drivers/power/supply/pmi8998_fg.h rename to drivers/power/supply/qcom_fg.h index b44f88bc9395..2bc6df2fdd9d 100644 --- a/drivers/power/supply/pmi8998_fg.h +++ b/drivers/power/supply/qcom_fg.h @@ -3,19 +3,18 @@ /**** Registers *****/ -// pmi8998 v2 specific -#define BATT_INFO_CHARGE_MAX_DESIGN 0x4a +#define PMIC_SUBTYPE 0x105 + +#define PMI8994_SUBTYPE 0x0a +#define PMI8996_SUBTYPE 0x13 +#define PM8998_SUBTYPE 0x14 +#define PMI8998_SUBTYPE 0x15 + + +/* SRAM */ +#define MEM_INTF_STS 0x10 #define MEM_INTF_CFG 0x50 -#define MEM_INTF_ADDR_LSB 0x61 -#define MEM_INTF_RD_DATA0 0x67 -#define MEM_INTF_WR_DATA0 0x63 - -#define PMIC_SUBTYPE 0x105 - -#define PM8998_SUBTYPE 0x14 -#define PMI8998_SUBTYPE 0x15 - -// pm8950 / pm89988 common +#define MEM_INTF_CTL 0x51 #define MEM_INTF_IMA_CFG 0x52 #define MEM_INTF_IMA_OPR_STS 0x54 #define MEM_INTF_IMA_EXP_STS 0x55 @@ -23,7 +22,18 @@ #define MEM_INTF_BEAT_COUNT 0x57 #define MEM_INTF_IMA_ERR_STS 0x5f #define MEM_INTF_IMA_BYTE_EN 0x60 +#define MEM_INTF_ADDR_LSB 0x61 +#define MEM_INTF_RD_DATA0 0x67 +#define MEM_INTF_WR_DATA0 0x63 +#define MEM_INTF_AVAIL BIT(0) +#define MEM_INTF_CTL_BURST BIT(7) +#define MEM_INTF_CTL_WR_EN BIT(6) +#define RIF_MEM_ACCESS_REQ BIT(7) + + +/* Battery info */ +#define BATT_INFO_CHARGE_MAX_DESIGN 0x4a #define BATT_INFO_THERM_C1 0x5c #define BATT_INFO_VBATT_LSB 0xa0 #define BATT_INFO_VBATT_MSB 0xa1 @@ -36,45 +46,58 @@ #define BATT_TEMP_LSB_MASK GENMASK(7, 0) #define BATT_TEMP_MSB_MASK GENMASK(2, 0) -#define REG_BASE 0x4000 -#define REG_BATT 0x4100 -#define REG_MEM 0x4400 + +/* Base addresses */ +#define REG_BASE 0x4000 +#define REG_BATT 0x4100 +#define REG_MEM 0x4400 + /* Interrupt offsets */ #define INT_RT_STS 0x10 #define INT_EN_CLR 0x16 -// Param addresses -#define PARAM_ADDR_BATT_TEMP 0x50 -#define PARAM_ADDR_BATT_VOLTAGE 0xa0 -#define PARAM_ADDR_BATT_CURRENT 0xa2 + +/* Param addresses */ +#define PARAM_ADDR_BATT_TEMP 0x50 +#define PARAM_ADDR_BATT_VOLTAGE 0xa0 +#define PARAM_ADDR_BATT_CURRENT 0xa2 + + +#define MISC_BASE 0x1600 +#define USBIN_BASE 0x1300 + #define BATT_INFO_JEITA_COLD(chip) (REG_BATT + 0x62) #define BATT_INFO_JEITA_COOL(chip) (REG_BATT + 0x63) #define BATT_INFO_JEITA_WARM(chip) (REG_BATT + 0x64) #define BATT_INFO_JEITA_HOT(chip) (REG_BATT + 0x65) -#define MISC_BASE 0x1600 -#define USBIN_BASE 0x1300 +#define BATT_TEMP_JEITA_COLD 100 +#define BATT_TEMP_JEITA_COOL 50 +#define BATT_TEMP_JEITA_WARM 400 +#define BATT_TEMP_JEITA_HOT 450 #define BATTERY_CHARGER_STATUS_REG(chip) (chip->chg_base + 0x06) -#define BATTERY_HEALTH_STATUS_REG(chip) (chip->chg_base + 0x07) +#define BATTERY_HEALTH_STATUS_REG(chip) (chip->chg_base + 0x07) #define BATTERY_CHARGER_STATUS_MASK GENMASK(2, 0) #define POWER_PATH_STATUS_REG (MISC_BASE + 0x0B) +#define MEM_IF_TIMEOUT_MS 5000 + enum wa_flags { PMI8998_V1_REV_WA, PMI8998_V2_REV_WA, }; -enum pmi8998_rev_offsets { +enum pmic_rev_offsets { DIG_MINOR = 0x0, DIG_MAJOR = 0x1, ANA_MINOR = 0x2, ANA_MAJOR = 0x3, }; -enum pmi8998_rev { +enum pmic_rev { DIG_REV_1 = 0x1, DIG_REV_2 = 0x2, DIG_REV_3 = 0x3, @@ -112,18 +135,35 @@ static enum power_supply_property fg_properties[] = { POWER_SUPPLY_PROP_TEMP_ALERT_MAX, }; -struct pmi8998_fg_chip { +struct qcom_fg_chip; + +struct qcom_fg_ops { + int (*get_capacity)(struct qcom_fg_chip *chip, int *); + int (*get_temperature)(struct qcom_fg_chip *chip, int *); + int (*get_current)(struct qcom_fg_chip *chip, int *); + int (*get_voltage)(struct qcom_fg_chip *chip, int *); + int (*get_batt_status)(struct qcom_fg_chip *chip, int *); + int (*get_health_status)(struct qcom_fg_chip *chip, int *); + int (*get_temp_threshold)(struct qcom_fg_chip *chip, + enum power_supply_property psp, int *); + int (*set_temp_threshold)(struct qcom_fg_chip *chip, + enum power_supply_property psp, int); +}; + +struct qcom_fg_chip { struct device *dev; unsigned int base; unsigned int chg_base; struct regmap *regmap; struct mutex lock; unsigned int subtype; + const struct qcom_fg_ops *ops; struct power_supply *bms_psy; u8 revision[4]; bool ima_supported; + struct completion sram_access_granted; int batt_cap_uah; int batt_max_voltage_uv; From 1918745f5f67f153ea103909a20d408a2166e038 Mon Sep 17 00:00:00 2001 From: Jami Kettunen Date: Sun, 25 Apr 2021 17:33:06 +0300 Subject: [PATCH 0311/1823] power: supply: qcom_fg: Report online property --- drivers/power/supply/qcom_fg.c | 4 ++++ drivers/power/supply/qcom_fg.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/drivers/power/supply/qcom_fg.c b/drivers/power/supply/qcom_fg.c index dc41f6072828..7e3bfad0de0c 100644 --- a/drivers/power/supply/qcom_fg.c +++ b/drivers/power/supply/qcom_fg.c @@ -594,6 +594,7 @@ int qcom_fg_get_prop_batt_status(struct qcom_fg_chip *chip, int *val){ } dev_dbg(chip->dev, "USB ONLINE val : %d\n", usb_online_val); usb_online = (bool)usb_online_val; + chip->online = usb_online; if (!usb_online) { *val = POWER_SUPPLY_STATUS_DISCHARGING; @@ -828,6 +829,9 @@ static int fg_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_STATUS: error = chip->ops->get_batt_status(chip, &val->intval); break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = chip->online; + break; case POWER_SUPPLY_PROP_HEALTH: error = chip->ops->get_health_status(chip, &val->intval); break; diff --git a/drivers/power/supply/qcom_fg.h b/drivers/power/supply/qcom_fg.h index 2bc6df2fdd9d..2617d0203e57 100644 --- a/drivers/power/supply/qcom_fg.h +++ b/drivers/power/supply/qcom_fg.h @@ -125,6 +125,7 @@ static enum power_supply_property fg_properties[] = { POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, @@ -171,4 +172,5 @@ struct qcom_fg_chip { int health; int status; + bool online; }; From 4b341bc46389906839e93be7a25622dfcf1da676 Mon Sep 17 00:00:00 2001 From: Jami Kettunen Date: Tue, 27 Jul 2021 13:33:19 +0300 Subject: [PATCH 0312/1823] [ANNOTATION] Import WIP "Qualcomm SPMI Fuel Gauge" driver https://gitlab.com/sdm845-mainline/linux/-/commits/driver/pmi8998_fg/ + drop pmi8994 DTS changes though. + random pieces from https://gitlab.com/sdm845-mainline/linux/-/commits/sdm845/5.19-release :/ From e2c631ebcf7d51647b1c0c979f9f5ecfdee19c1a Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Fri, 10 Dec 2021 02:27:00 +0000 Subject: [PATCH 0313/1823] arm64: dts: qcom: pmi8998: introduce spmi haptics Add bindings for Qualcomm SPMI haptics on platforms using pmi8998. Signed-off-by: Caleb Connolly (JAMI: fixup for v6.0-rc2 & v7 of driver patches) --- arch/arm64/boot/dts/qcom/pmi8998.dtsi | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/pmi8998.dtsi b/arch/arm64/boot/dts/qcom/pmi8998.dtsi index 79d47540fa8b..8783175a83b0 100644 --- a/arch/arm64/boot/dts/qcom/pmi8998.dtsi +++ b/arch/arm64/boot/dts/qcom/pmi8998.dtsi @@ -72,5 +72,16 @@ status = "disabled"; }; + + pmi8998_haptics: haptics@c000 { + compatible = "qcom,pmi8998-haptics"; + reg = <0xc000>; + + interrupts = <0x3 0xc0 0x0 IRQ_TYPE_EDGE_BOTH>, + <0x3 0xc0 0x1 IRQ_TYPE_EDGE_BOTH>; + interrupt-names = "short", "play"; + + status = "disabled"; + }; }; }; From 119430dc18aa71295b06e6f6974dfe90ad411f20 Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Wed, 23 Mar 2022 16:28:17 +0000 Subject: [PATCH 0314/1823] arm64: dts: qcom: pmi8998: add rradc node Add a DT node for the Round Robin ADC found in the PMI8998 PMIC. Signed-off-by: Caleb Connolly --- arch/arm64/boot/dts/qcom/pmi8998.dtsi | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/pmi8998.dtsi b/arch/arm64/boot/dts/qcom/pmi8998.dtsi index 8783175a83b0..b22aedd948de 100644 --- a/arch/arm64/boot/dts/qcom/pmi8998.dtsi +++ b/arch/arm64/boot/dts/qcom/pmi8998.dtsi @@ -28,6 +28,14 @@ status = "disabled"; }; + + pmi8998_rradc: rradc@4500 { + compatible = "qcom,pmi8998-rradc"; + reg = <0x4500>; + #io-channel-cells = <1>; + + status = "disabled"; + }; }; pmi8998_lsid1: pmic@3 { From dae71dc9a5fe9b114506705fb4f7feb3eb42989f Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Thu, 28 Apr 2022 11:42:29 +0100 Subject: [PATCH 0315/1823] arm64: dts: qcom: pmi8998: add charger node Add a node for the smb2 charger hardware found on the pmi8998. Signed-off-by: Caleb Connolly --- arch/arm64/boot/dts/qcom/pmi8998.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/pmi8998.dtsi b/arch/arm64/boot/dts/qcom/pmi8998.dtsi index b22aedd948de..2ba0497c7031 100644 --- a/arch/arm64/boot/dts/qcom/pmi8998.dtsi +++ b/arch/arm64/boot/dts/qcom/pmi8998.dtsi @@ -9,6 +9,23 @@ #address-cells = <1>; #size-cells = <0>; + pmi8998_charger: charger@1000 { + compatible = "qcom,pmi8998-charger"; + reg = <0x1000>; + + interrupts = <0x2 0x13 0x4 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x12 0x2 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x16 0x1 IRQ_TYPE_EDGE_RISING>, + <0x2 0x13 0x6 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "usb-plugin", "bat-ov", "wdog-bark", "usbin-icl-change"; + + io-channels = <&pmi8998_rradc 3>, + <&pmi8998_rradc 4>; + io-channel-names = "usbin_i", "usbin_v"; + + status = "disabled"; + }; + pmi8998_gpio: gpios@c000 { compatible = "qcom,pmi8998-gpio", "qcom,spmi-gpio"; reg = <0xc000>; From 72ffd69b453c0e21d11b3b523b66986b64f4482e Mon Sep 17 00:00:00 2001 From: Jami Kettunen Date: Wed, 7 Oct 2020 23:06:21 +0300 Subject: [PATCH 0316/1823] [HACK] scripts: Stop appending "+" to localversion This always gets added and doesn't look nice in the version string, so it's purely for aesthetic reasons :p --- scripts/setlocalversion | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/setlocalversion b/scripts/setlocalversion index af4754a35e66..a42f4008a9dc 100755 --- a/scripts/setlocalversion +++ b/scripts/setlocalversion @@ -139,7 +139,7 @@ elif [ "${LOCALVERSION+set}" != "set" ]; then # If the variable LOCALVERSION is set (including being set # to an empty string), we don't want to append a plus sign. scm=$(scm_version --short) - res="$res${scm:++}" + #res="$res${scm:++}" fi echo "$res" From 4d927cb2d92718a8ef7aeff6055359dacad4e23a Mon Sep 17 00:00:00 2001 From: Jami Kettunen Date: Tue, 27 Apr 2021 20:24:20 +0300 Subject: [PATCH 0317/1823] drm/msm: Mention DSI 10nm PHY support for MSM8998 --- drivers/gpu/drm/msm/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig index 3c9dfdb0b328..8950aa390a6c 100644 --- a/drivers/gpu/drm/msm/Kconfig +++ b/drivers/gpu/drm/msm/Kconfig @@ -133,7 +133,7 @@ config DRM_MSM_DSI_14NM_PHY Choose this option if DSI PHY on 8996 is used on the platform. config DRM_MSM_DSI_10NM_PHY - bool "Enable DSI 10nm PHY driver in MSM DRM (used by SDM845)" + bool "Enable DSI 10nm PHY driver in MSM DRM (used by MSM8998/SDM845)" depends on DRM_MSM_DSI default y help From 46f0bbbca46b7d4cbd546e7f40da12cab63f8a84 Mon Sep 17 00:00:00 2001 From: Jami Kettunen Date: Fri, 28 Jan 2022 18:39:57 +0200 Subject: [PATCH 0318/1823] drm/msm: add missing a540 MODULE_FIRMWARE declarations --- drivers/gpu/drm/msm/adreno/adreno_device.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/msm/adreno/adreno_device.c b/drivers/gpu/drm/msm/adreno/adreno_device.c index 628806423f7d..883f03995697 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_device.c +++ b/drivers/gpu/drm/msm/adreno/adreno_device.c @@ -371,6 +371,11 @@ MODULE_FIRMWARE("qcom/a530_zap.mdt"); MODULE_FIRMWARE("qcom/a530_zap.b00"); MODULE_FIRMWARE("qcom/a530_zap.b01"); MODULE_FIRMWARE("qcom/a530_zap.b02"); +MODULE_FIRMWARE("qcom/a540_gpmu.fw2"); +MODULE_FIRMWARE("qcom/a540_zap.mdt"); +MODULE_FIRMWARE("qcom/a540_zap.b00"); +MODULE_FIRMWARE("qcom/a540_zap.b01"); +MODULE_FIRMWARE("qcom/a540_zap.b02"); MODULE_FIRMWARE("qcom/a619_gmu.bin"); MODULE_FIRMWARE("qcom/a630_sqe.fw"); MODULE_FIRMWARE("qcom/a630_gmu.bin"); From 019ec6d0a5a4d3b7f5755c49e494cecdd9504213 Mon Sep 17 00:00:00 2001 From: Jami Kettunen Date: Tue, 12 Jan 2021 19:50:09 +0200 Subject: [PATCH 0319/1823] [HACK] ath10k: Fake MSA ready event after event server arrival This finally allowed me to get past the 2 ath10k_info()'s in qmi.c! However, this doesn't fix everything; it seems you *have* to run diag-router from https://github.com/andersson/diag to calm the firmware down and stop constantly crashing; need to look into a way to get Wi-Fi working later without needing this debug crap to be running. This should possible be submitted as a quirk to the ath10k driver as modem on mainline 8998 should be running now as well... --- drivers/net/wireless/ath/ath10k/qmi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c index 66cb7a1e628a..a0b1b556f251 100644 --- a/drivers/net/wireless/ath/ath10k/qmi.c +++ b/drivers/net/wireless/ath/ath10k/qmi.c @@ -1004,6 +1004,8 @@ static void ath10k_qmi_driver_event_work(struct work_struct *work) switch (event->type) { case ATH10K_QMI_EVENT_SERVER_ARRIVE: ath10k_qmi_event_server_arrive(qmi); + // HACK: Fake MSA being ready + ath10k_qmi_event_msa_ready(qmi); break; case ATH10K_QMI_EVENT_SERVER_EXIT: ath10k_qmi_event_server_exit(qmi); From f639e1729fd4f641a1355aa5c2ba4d845878eee5 Mon Sep 17 00:00:00 2001 From: Jami Kettunen Date: Thu, 24 Feb 2022 02:59:40 +0200 Subject: [PATCH 0320/1823] [HACK] net: ipa: Disable automatic suspend 1aac309d3207 ("net: ipa: use autosuspend") currently causes my device to enter 900E crashdump mode as soon as IPA is probed :/ With this we at least can run ModemManager once again... --- drivers/net/ipa/ipa_power.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ipa/ipa_power.c b/drivers/net/ipa/ipa_power.c index 8420f93128a2..ed904cf90273 100644 --- a/drivers/net/ipa/ipa_power.c +++ b/drivers/net/ipa/ipa_power.c @@ -395,6 +395,7 @@ ipa_power_init(struct device *dev, const struct ipa_power_data *data) pm_runtime_set_autosuspend_delay(dev, IPA_AUTOSUSPEND_DELAY); pm_runtime_use_autosuspend(dev); + pm_runtime_forbid(dev); pm_runtime_enable(dev); return power; From 37df9e8b9c5a58032d617d53acb7aaf0eadc8208 Mon Sep 17 00:00:00 2001 From: Jami Kettunen Date: Tue, 27 Jul 2021 14:04:12 +0300 Subject: [PATCH 0321/1823] arm64: dts: qcom: msm8998: add IPA information Add support for the MSM8998 SoC, which includes IPA version 3.1. --- arch/arm64/boot/dts/qcom/msm8998.dtsi | 55 +++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/msm8998.dtsi b/arch/arm64/boot/dts/qcom/msm8998.dtsi index 7e565517be10..f5d6ddf24c69 100644 --- a/arch/arm64/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998.dtsi @@ -852,6 +852,17 @@ interrupt-controller; #interrupt-cells = <2>; }; + + ipa_smp2p_out: ipa-ap-to-modem { + qcom,entry-name = "ipa"; + #qcom,smem-state-cells = <1>; + }; + + ipa_smp2p_in: ipa-modem-to-ap { + qcom,entry-name = "ipa"; + interrupt-controller; + #interrupt-cells = <2>; + }; }; smp2p-slpi { @@ -1702,6 +1713,50 @@ }; }; + ipa: ipa@1e40000 { + compatible = "qcom,msm8998-ipa"; + + iommus = <&anoc2_smmu 0x18e0 0x0>, + <&anoc2_smmu 0x18e2 0x0>; + reg = <0x1e40000 0x7000>, + <0x1e47000 0x2000>, + <0x1e04000 0x2c000>; + reg-names = "ipa-reg", + "ipa-shared", + "gsi"; + + interrupts-extended = <&intc GIC_SPI 333 IRQ_TYPE_EDGE_RISING>, + <&intc GIC_SPI 432 IRQ_TYPE_LEVEL_HIGH>, + <&ipa_smp2p_in 0 IRQ_TYPE_EDGE_RISING>, + <&ipa_smp2p_in 1 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "ipa", + "gsi", + "ipa-clock-query", + "ipa-setup-ready"; + + clocks = <&rpmcc RPM_SMD_IPA_CLK>; + clock-names = "core"; + +/* Elder's proposed interconnects: + <&aggre2_noc MASTER_IPA 0 &mem_noc SLAVE_EBI1 0>, + <&aggre2_noc MASTER_IPA 0 &system_noc SLAVE_IMEM 0>, + <&gladiator_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_IPA_CFG 0> +*/ + interconnects = <&a2noc MASTER_IPA &bimc SLAVE_EBI>, + <&a2noc MASTER_IPA &snoc SLAVE_IMEM>, + <&gnoc MASTER_APSS_PROC &cnoc SLAVE_IPA>; + interconnect-names = "memory", + "imem", + "config"; + + qcom,smem-states = <&ipa_smp2p_out 0>, + <&ipa_smp2p_out 1>; + qcom,smem-state-names = "ipa-clock-enabled-valid", + "ipa-clock-enabled"; + + status = "disabled"; + }; + tcsr_mutex: hwlock@1f40000 { compatible = "qcom,tcsr-mutex"; reg = <0x01f40000 0x20000>; From b7e8630c76f8f64225066d93e8db41158fc92f16 Mon Sep 17 00:00:00 2001 From: Jami Kettunen Date: Fri, 21 May 2021 04:31:36 +0300 Subject: [PATCH 0322/1823] Input: synaptics-rmi4 - add support for F1A RMI4 F1A supports the simple capacitive buttons function, it's used for example on embedded devices such as smartphones for capacitive Android back and recents buttons. --- drivers/input/rmi4/Kconfig | 8 ++ drivers/input/rmi4/Makefile | 1 + drivers/input/rmi4/rmi_bus.c | 3 + drivers/input/rmi4/rmi_driver.h | 1 + drivers/input/rmi4/rmi_f1a.c | 190 ++++++++++++++++++++++++++++++++ 5 files changed, 203 insertions(+) create mode 100644 drivers/input/rmi4/rmi_f1a.c diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig index c0163b983ce6..fbd3ace41373 100644 --- a/drivers/input/rmi4/Kconfig +++ b/drivers/input/rmi4/Kconfig @@ -82,6 +82,14 @@ config RMI4_F12 touchpads. For sensors that support relative pointing, F12 also provides mouse input. +config RMI4_F1A + bool "RMI4 Function 1A (Simple capacitive buttons)" + depends on OF + help + Say Y here if you want to add support for RMI4 function 1A. + + Function 1A provides GPIO capacitive button support for RMI4 devices. + config RMI4_F30 bool "RMI4 Function 30 (GPIO LED)" help diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile index 02f14c846861..70f942196aa2 100644 --- a/drivers/input/rmi4/Makefile +++ b/drivers/input/rmi4/Makefile @@ -8,6 +8,7 @@ rmi_core-$(CONFIG_RMI4_2D_SENSOR) += rmi_2d_sensor.o rmi_core-$(CONFIG_RMI4_F03) += rmi_f03.o rmi_core-$(CONFIG_RMI4_F11) += rmi_f11.o rmi_core-$(CONFIG_RMI4_F12) += rmi_f12.o +rmi_core-$(CONFIG_RMI4_F1A) += rmi_f1a.o rmi_core-$(CONFIG_RMI4_F30) += rmi_f30.o rmi_core-$(CONFIG_RMI4_F34) += rmi_f34.o rmi_f34v7.o rmi_core-$(CONFIG_RMI4_F3A) += rmi_f3a.o diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c index 50a0134b6901..fc39ba462ab0 100644 --- a/drivers/input/rmi4/rmi_bus.c +++ b/drivers/input/rmi4/rmi_bus.c @@ -360,6 +360,9 @@ static struct rmi_function_handler *fn_handlers[] = { #ifdef CONFIG_RMI4_F12 &rmi_f12_handler, #endif +#ifdef CONFIG_RMI4_F1A + &rmi_f1a_handler, +#endif #ifdef CONFIG_RMI4_F30 &rmi_f30_handler, #endif diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h index 1c6c6086c0e5..a05b8f9d7a46 100644 --- a/drivers/input/rmi4/rmi_driver.h +++ b/drivers/input/rmi4/rmi_driver.h @@ -133,6 +133,7 @@ extern struct rmi_function_handler rmi_f01_handler; extern struct rmi_function_handler rmi_f03_handler; extern struct rmi_function_handler rmi_f11_handler; extern struct rmi_function_handler rmi_f12_handler; +extern struct rmi_function_handler rmi_f1a_handler; extern struct rmi_function_handler rmi_f30_handler; extern struct rmi_function_handler rmi_f34_handler; extern struct rmi_function_handler rmi_f3a_handler; diff --git a/drivers/input/rmi4/rmi_f1a.c b/drivers/input/rmi4/rmi_f1a.c new file mode 100644 index 000000000000..d4d47e59fa0d --- /dev/null +++ b/drivers/input/rmi4/rmi_f1a.c @@ -0,0 +1,190 @@ +// FIXME: Too much stuff is hardcoded in currently! +#include +#include +#include +#include "rmi_driver.h" + +/* Query 1 */ +//#define F1A_BUTTONS_COUNT 0b00011111 /* BIT(1) */ + +#define MAX_NAME_LEN 256 +#define F1A_MAX_BUTTONS 8 + +struct f1a_data { + struct rmi_function *fn; + + unsigned char button_count; + u32 button_map[F1A_MAX_BUTTONS]; + struct input_dev *input; + char input_name[MAX_NAME_LEN]; + char input_phys[MAX_NAME_LEN]; + u8 button_data_buffer; +}; + +static int rmi_f1a_initialize(struct f1a_data *f1a) +{ + struct rmi_function *fn = f1a->fn; + struct device *dev = &fn->dev; + //u8 query[2]; + int error; + + /*error = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr, query, 2); + if (error) { + dev_err(dev, "Failed to read query register (%d).\n", error); + return error; + } + printk("%s: query0: 0x%x, query1: 0x%x\n", __func__, query[0], query[1]);*/ + + error = of_property_read_variable_u32_array(dev_of_node(dev), + "syna,codes", f1a->button_map, 1, F1A_MAX_BUTTONS); + if (error < 0) { + dev_err(dev, "Failed to parse syna,codes from OF device tree (%d).\n", error); + return error; + } + // FIXME: button_count = query[1] & F1A_BUTTONS_COUNT; + f1a->button_count = 0; + for (f1a->button_count = 0; f1a->button_count < F1A_MAX_BUTTONS; f1a->button_count++) + if (f1a->button_map[f1a->button_count] == 0) + break; + rmi_dbg(RMI_DEBUG_FN, dev, "%s: %d button codes defined\n", __func__, f1a->button_count); + + return 0; +} + +static int rmi_f1a_register_device(struct f1a_data *f1a) +{ + struct rmi_function *fn = f1a->fn; + struct device *dev = &fn->dev; + struct rmi_device *rmi_dev = fn->rmi_dev; + struct input_dev *input_dev; + int i, rc; + + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(dev, "Failed to allocate input device.\n"); + return -ENOMEM; + } + + f1a->input = input_dev; + snprintf(f1a->input_name, MAX_NAME_LEN, "%s.fn%02x", + dev_name(&rmi_dev->dev), fn->fd.function_number); + input_dev->name = f1a->input_name; + snprintf(f1a->input_phys, MAX_NAME_LEN, "%s/input0", input_dev->name); + input_dev->phys = f1a->input_phys; + input_dev->dev.parent = &rmi_dev->dev; + input_set_drvdata(input_dev, f1a); + + /* set up any input events */ + set_bit(EV_SYN, input_dev->evbit); + set_bit(EV_KEY, input_dev->evbit); + + /* manage button map using input subsystem */ + input_dev->keycode = f1a->button_map; + input_dev->keycodesize = sizeof(f1a->button_map); /* f1a->button_count */ + input_dev->keycodemax = f1a->button_count; + + /* set bits for each button */ + for (i = 0; i < f1a->button_count; i++) { + set_bit(f1a->button_map[i], input_dev->keybit); + input_set_capability(input_dev, EV_KEY, f1a->button_map[i]); + } + + rc = input_register_device(input_dev); + if (rc < 0) { + dev_err(dev, "Failed to register input device.\n"); + goto error_free_device; + } + + return 0; + +error_free_device: + input_free_device(input_dev); + return rc; +} + +static int rmi_f1a_probe(struct rmi_function *fn) +{ + struct device *dev = &fn->dev; + struct f1a_data *f1a; + int error; + + rmi_dbg(RMI_DEBUG_FN, dev, "%s\n", __func__); + + if (!fn->dev.of_node) { + /* + * Some quirky devices (e.g. OnePlus 5T) report supporting F1A + * (Simple capacitive buttons) despite not having the hardware, + * so in case there isn't an associated device-tree node present + * just pretend we probed successfully. + */ + return 0; + } + + f1a = devm_kzalloc(dev, sizeof(struct f1a_data), GFP_KERNEL); + if (!f1a) + return -ENOMEM; + + f1a->fn = fn; + + error = rmi_f1a_initialize(f1a); + if (error < 0) + return error; + + error = rmi_f1a_register_device(f1a); + if (error < 0) + return error; + + dev_set_drvdata(dev, f1a); + return 0; +} + +static int rmi_f1a_config(struct rmi_function *fn) +{ + fn->rmi_dev->driver->set_irq_bits(fn->rmi_dev, fn->irq_mask); + return 0; +} + +static irqreturn_t rmi_f1a_attention(int irq, void *ctx) +{ + struct rmi_function *fn = ctx; + struct device *dev = &fn->dev; + struct rmi_device *rmi_dev = fn->rmi_dev; + struct f1a_data *f1a = dev_get_drvdata(dev); + int error, button; + + // TODO: use rmi_read_block() to accomodate up to 8 buttons? + error = rmi_read(rmi_dev, fn->fd.data_base_addr, &(f1a->button_data_buffer)); + if (error < 0) { + dev_err(dev, "Failed to read button data registers (%d).\n", error); + return error; + } + /*rmi_dbg(RMI_DEBUG_FN, dev, "%s: button_data=0x%x\n", __func__, f1a->button_data_buffer);*/ + + /* generate events for buttons that change state */ + // TODO: Implement button_data_buffer as array + button_reg = button / 8 + for (button = 0; button < f1a->button_count; button++) { + int button_shift; + bool button_status; + /* bit shift to get button's status */ + button_shift = button % 8; + button_status = ((f1a->button_data_buffer >> button_shift) & 0x01) != 0; + + rmi_dbg(RMI_DEBUG_FN, dev, "button %d (code %d) -> %d\n", + button, f1a->button_map[button], button_status); + /* generate an event here */ + input_report_key(f1a->input, f1a->button_map[button], button_status); + } + input_sync(f1a->input); /* sync after groups of events */ + + return IRQ_HANDLED; +} + +struct rmi_function_handler rmi_f1a_handler = { + .driver = { + .name = "rmi4_f1a", + }, + .func = 0x1a, + .probe = rmi_f1a_probe, + .config = rmi_f1a_config, + .attention = rmi_f1a_attention, +}; From 5cdf191d578f9b3df71773edd4e390144ea3ec47 Mon Sep 17 00:00:00 2001 From: Jami Kettunen Date: Sat, 20 Nov 2021 22:14:36 +0200 Subject: [PATCH 0323/1823] module: demote invalid ELF header magic error message to pr_debug This happens anytime busybox modprobe loads a compressed module and looks especially ugly on postmarketOS for example (or basically any initramfs); this error is rather pointless since the module is loaded anyway afterwards. Upstream (busybox) has no plans to do anything about this error getting spammed: https://www.mail-archive.com/busybox@busybox.net/msg27078.html --- kernel/module/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/module/main.c b/kernel/module/main.c index d02d39c7174e..f99e0e50a6af 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -1661,7 +1661,7 @@ static int elf_validity_check(struct load_info *info) } if (memcmp(info->hdr->e_ident, ELFMAG, SELFMAG) != 0) { - pr_err("Invalid ELF header magic: != %s\n", ELFMAG); + pr_debug("Invalid ELF header magic: != %s\n", ELFMAG); goto no_exec; } if (info->hdr->e_type != ET_REL) { From 37d158366530eb2852397911ee5f79b4eebfe299 Mon Sep 17 00:00:00 2001 From: Jami Kettunen Date: Sun, 11 Sep 2022 15:56:44 +0300 Subject: [PATCH 0324/1823] [HACK] drm/msm: Always report CTL status as started for MSM8998 This register always appears to read 0 on hw, CTL_START) & BIT(0)); + return true; } static inline void dpu_hw_ctl_trigger_pending(struct dpu_hw_ctl *ctx) From c974cc82e4177cf461f0ab5ce5d6a3ec7936f7bf Mon Sep 17 00:00:00 2001 From: Jami Kettunen Date: Tue, 27 Jul 2021 14:13:34 +0300 Subject: [PATCH 0325/1823] [ANNOTATION] Apply miscellaneous hacks and patches From bca845878745289b0dff82fa6e05dd319c914978 Mon Sep 17 00:00:00 2001 From: Jami Kettunen Date: Wed, 30 Mar 2022 03:27:39 +0300 Subject: [PATCH 0326/1823] [HACK] pinctrl: aw9523: Add workaround for F(x)tec Pro1 keyboard This is a hack because the correct interrupt type(s) should should be defined in msm8998-fxtec-pro1.dts instead of the driver. (originally from Danct12) This is a workaround for keyboard not working on Fxtec Pro1. genirq: Setting trigger mode 12 for irq 123 failed (aw9523_gpio_irq_type+0x0/0x20) gpio-fastmatrix-keyboard gpio-keyboard: Cannot get IRQ for gpio302 --- drivers/pinctrl/pinctrl-aw9523.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/pinctrl/pinctrl-aw9523.c b/drivers/pinctrl/pinctrl-aw9523.c index bc8e1e9d6876..b0e2a1498584 100644 --- a/drivers/pinctrl/pinctrl-aw9523.c +++ b/drivers/pinctrl/pinctrl-aw9523.c @@ -434,6 +434,9 @@ static int aw9523_gpio_irq_type(struct irq_data *d, unsigned int type) switch (type) { case IRQ_TYPE_NONE: case IRQ_TYPE_EDGE_BOTH: + case IRQ_TYPE_LEVEL_MASK: + case IRQ_TYPE_LEVEL_HIGH: + case IRQ_TYPE_LEVEL_LOW: return 0; default: return -EINVAL; From b5da4afe501c94eec159386252ca63cf00820294 Mon Sep 17 00:00:00 2001 From: Jami Kettunen Date: Wed, 30 Mar 2022 03:19:32 +0300 Subject: [PATCH 0327/1823] pinctrl: Fix compilation of Awinic AW9523/B I2C GPIO Expander Fixes the following link-time error: LD .tmp_vmlinux.kallsyms1 aarch64-linux-gnu-ld: Unexpected GOT/PLT entries detected! aarch64-linux-gnu-ld: Unexpected run-time procedure linkages detected! aarch64-linux-gnu-ld: drivers/pinctrl/pinctrl-aw9523.o: in function `aw9523_probe': .../drivers/pinctrl/pinctrl-aw9523.c:1004: undefined reference to `__devm_regmap_init_i2c' make[1]: *** [.../Makefile:1155: vmlinux] Error 1 Fixes: 96e86abbb4db ("pinctrl: Add driver for Awinic AW9523/B I2C GPIO Expander") --- drivers/pinctrl/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 4db0b6d3fd49..5c02ed55d6db 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -135,7 +135,7 @@ config PINCTRL_AW9523 select GENERIC_PINCONF select GPIOLIB select GPIOLIB_IRQCHIP - select REGMAP + select REGMAP_I2C help The Awinic AW9523/AW9523B is a multi-function I2C GPIO expander with PWM functionality. This driver bundles a From fdd5838e2bc40cacddb281f3b918cef000d6dd37 Mon Sep 17 00:00:00 2001 From: Danct12 Date: Wed, 28 Apr 2021 02:23:45 +0700 Subject: [PATCH 0328/1823] arm64: dts: msm8998-fxtec-pro1: Configure PMI8998 fuel gauge This will account for battery statistics reporting on the Fxtec Pro1 (QX1000) --- arch/arm64/boot/dts/qcom/msm8998-fxtec-pro1.dts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/msm8998-fxtec-pro1.dts b/arch/arm64/boot/dts/qcom/msm8998-fxtec-pro1.dts index 76a44d9049c9..4c11d8af5ce2 100644 --- a/arch/arm64/boot/dts/qcom/msm8998-fxtec-pro1.dts +++ b/arch/arm64/boot/dts/qcom/msm8998-fxtec-pro1.dts @@ -594,6 +594,14 @@ regulator-max-microvolt = <1100000>; }; +&pmi8998_fg { + status = "okay"; + + qcom,max-voltage-uv = <4400000>; + qcom,min-voltage-uv = <3700000>; + qcom,battery-capacity-ua = <3150000>; +}; + &pm8998_gpio { unknown_pin_a: unk-active-state { pins = "gpio5"; From 8535dcd7c2135aacc07778f27558468a04cd7981 Mon Sep 17 00:00:00 2001 From: Danct12 Date: Thu, 29 Apr 2021 00:21:49 +0700 Subject: [PATCH 0329/1823] arm64: dts: msm8998-fxtec-pro1: Enable PMI8998 haptics --- arch/arm64/boot/dts/qcom/msm8998-fxtec-pro1.dts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/msm8998-fxtec-pro1.dts b/arch/arm64/boot/dts/qcom/msm8998-fxtec-pro1.dts index 4c11d8af5ce2..718bf8505f8a 100644 --- a/arch/arm64/boot/dts/qcom/msm8998-fxtec-pro1.dts +++ b/arch/arm64/boot/dts/qcom/msm8998-fxtec-pro1.dts @@ -588,6 +588,12 @@ }; }; +&pmi8998_haptics { + status = "okay"; + + qcom,wave-play-rate-us = <5000>; +}; + /* HACK! Push GPU voltage high until GPU CPR is hooked up */ &pm8005_s1 { regulator-min-microvolt = <988000>; From 158d9831bf23d0043b53f64217d773c2d5b3c83f Mon Sep 17 00:00:00 2001 From: Danct12 Date: Sun, 2 May 2021 06:51:56 +0700 Subject: [PATCH 0330/1823] arm64: dts: msm8998-fxtec-pro1: Add remoteproc configuration --- arch/arm64/boot/dts/qcom/msm8998-fxtec-pro1.dts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/msm8998-fxtec-pro1.dts b/arch/arm64/boot/dts/qcom/msm8998-fxtec-pro1.dts index 718bf8505f8a..439b6dbc2dcb 100644 --- a/arch/arm64/boot/dts/qcom/msm8998-fxtec-pro1.dts +++ b/arch/arm64/boot/dts/qcom/msm8998-fxtec-pro1.dts @@ -337,6 +337,7 @@ zap-shader { memory-region = <&zap_shader_region>; + firmware-name = "qcom/a540_zap.mbn"; }; }; @@ -499,6 +500,22 @@ status = "okay"; }; +&remoteproc_adsp { + status = "okay"; + + firmware-name = "qcom/msm8998/fxtec/adsp.mbn"; +}; + +&remoteproc_mss { + firmware-name = "qcom/msm8998/fxtec/mba.mbn", "qcom/msm8998/fxtec/modem.mbn"; +}; + +&remoteproc_slpi { + status = "okay"; + + firmware-name = "qcom/msm8998/fxtec/slpi_v2.mbn"; +}; + &mmcc { status = "okay"; }; From 35d1ed29cc8401d518f42b28e1541313aa2ce966 Mon Sep 17 00:00:00 2001 From: Jami Kettunen Date: Tue, 27 Jul 2021 15:13:42 +0300 Subject: [PATCH 0331/1823] [ANNOTATION] Apply some F(x)tec Pro1 patches From b408f19a4f8403a49844e623f97568af3e3badde Mon Sep 17 00:00:00 2001 From: Jami Kettunen Date: Fri, 10 Dec 2021 02:27:20 +0000 Subject: [PATCH 0332/1823] arm64: dts: qcom: msm8998-oneplus-common: Enable PMI8998 haptics The OnePlus 5 and 5T both have a haptics engine connected to PMI8998. Signed-off-by: Jami Kettunen Signed-off-by: Caleb Connolly --- arch/arm64/boot/dts/qcom/msm8998-oneplus-common.dtsi | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/msm8998-oneplus-common.dtsi b/arch/arm64/boot/dts/qcom/msm8998-oneplus-common.dtsi index 62bda23791bb..f7896b337927 100644 --- a/arch/arm64/boot/dts/qcom/msm8998-oneplus-common.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998-oneplus-common.dtsi @@ -279,6 +279,12 @@ }; }; +&pmi8998_haptics { + status = "okay"; + + qcom,wave-play-rate-us = <4255>; +}; + &qusb2phy { status = "okay"; From 39cd6b976dcea7f5e9401babe34e6d946f985dd8 Mon Sep 17 00:00:00 2001 From: Jami Kettunen Date: Mon, 28 Jun 2021 23:25:13 +0300 Subject: [PATCH 0333/1823] arm64: dts: qcom: msm8998-oneplus-common: Enable PMI8998 LPG leds Now the RGB notification LEDs can each be controlled individually :) --- .../boot/dts/qcom/msm8998-oneplus-common.dtsi | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/msm8998-oneplus-common.dtsi b/arch/arm64/boot/dts/qcom/msm8998-oneplus-common.dtsi index f7896b337927..febaecee503d 100644 --- a/arch/arm64/boot/dts/qcom/msm8998-oneplus-common.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8998-oneplus-common.dtsi @@ -9,6 +9,7 @@ /dts-v1/; #include +#include #include #include "msm8998.dtsi" #include "pm8005.dtsi" @@ -285,6 +286,33 @@ qcom,wave-play-rate-us = <4255>; }; +&pmi8998_lpg { + status = "okay"; + + multi-led { + color = ; + function = LED_FUNCTION_STATUS; + + #address-cells = <1>; + #size-cells = <0>; + + led@3 { + reg = <3>; + color = ; + }; + + led@4 { + reg = <4>; + color = ; + }; + + led@5 { + reg = <5>; + color = ; + }; + }; +}; + &qusb2phy { status = "okay"; From 73e0c82858ae43f31c0525e00215d43ccb5825e4 Mon Sep 17 00:00:00 2001 From: Jami Kettunen Date: Sun, 21 Jun 2020 21:52:02 +0300 Subject: [PATCH 0334/1823] drm/panel: Add panel driver for Samsung S6E3FA5 The S6E3FA5 is 5.5" 1080x1920 MIPI DSI command mode AMOLED LCD display found on OnePlus 5 (2017) smartphones. The panel needs to be enabled from a device tree using the "samsung,s6e3fa5" compatible. This driver was generated using the following & includes some minor cleanup (such as s/to_s6e3fa5/to_s6e3fa5_panel/g): $ python3 lmdpdg.py cheeseburger.dtb -r vddio Signed-off-by: Jami Kettunen --- drivers/gpu/drm/panel/Kconfig | 15 + drivers/gpu/drm/panel/Makefile | 1 + drivers/gpu/drm/panel/panel-samsung-s6e3fa5.c | 338 ++++++++++++++++++ 3 files changed, 354 insertions(+) create mode 100644 drivers/gpu/drm/panel/panel-samsung-s6e3fa5.c diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index a582ddd583c2..7818b4e9b9c9 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -484,6 +484,21 @@ config DRM_PANEL_SAMSUNG_S6D27A1 This panel can be found in Samsung Galaxy Ace 2 GT-I8160 mobile phone. +config DRM_PANEL_SAMSUNG_S6E3FA5 + tristate "Samsung S6E3FA5 DSI command mode panel" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + select VIDEOMODE_HELPERS + help + Say Y here if you want to enable support for Samsung S6E3FA5 AMOLED + command mode panel as found in OnePlus 5 (2017) devices. The panel has a + FHD (1080x1920) resolution and uses 24 bit RGB per pixel. It provides a + MIPI DSI interface to the host and has a built-in LED backlight. + + To compile this driver as a module, choose M here: the module + will be called panel-samsung-s6e3fa5. + config DRM_PANEL_SAMSUNG_S6E3HA2 tristate "Samsung S6E3HA2 DSI video mode panel" depends on OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 34e717382dbb..b8becd6b4084 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_DB7430) += panel-samsung-db7430.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D16D0) += panel-samsung-s6d16d0.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D27A1) += panel-samsung-s6d27a1.o +obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E3FA5) += panel-samsung-s6e3fa5.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2) += panel-samsung-s6e3ha2.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03) += panel-samsung-s6e63j0x03.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63M0) += panel-samsung-s6e63m0.o diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e3fa5.c b/drivers/gpu/drm/panel/panel-samsung-s6e3fa5.c new file mode 100644 index 000000000000..90faee3f5a50 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-samsung-s6e3fa5.c @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2021 Jami Kettunen + * Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include + +#include