* [PATCH 2/2] ARM: dts: ux500: Remove ab8500_ldo_usb regulator from device tree
From: Stephan Gerhold @ 2019-08-22 11:07 UTC (permalink / raw)
To: Linus Walleij
Cc: Mark Rutland, devicetree, Stephan Gerhold, linux-kernel,
Rob Herring, linux-arm-kernel
In-Reply-To: <20190822110720.118828-1-stephan@gerhold.net>
Support for the USB regulator of AB8500 was removed in
commit 41a06aa738ad ("regulator: ab8500: Remove USB regulator").
However, the configuration was never removed from the device tree.
It does no longer have any effect, remove it from the device tree.
Signed-off-by: Stephan Gerhold <stephan@gerhold.net>
---
arch/arm/boot/dts/ste-ab8500.dtsi | 4 ----
arch/arm/boot/dts/ste-href.dtsi | 4 ----
arch/arm/boot/dts/ste-snowball.dts | 4 ----
3 files changed, 12 deletions(-)
diff --git a/arch/arm/boot/dts/ste-ab8500.dtsi b/arch/arm/boot/dts/ste-ab8500.dtsi
index 3ef1906e375c..55fff4d44277 100644
--- a/arch/arm/boot/dts/ste-ab8500.dtsi
+++ b/arch/arm/boot/dts/ste-ab8500.dtsi
@@ -182,10 +182,6 @@
ab8500_ldo_tvout_reg: ab8500_ldo_tvout {
};
- // supply for ab8500-usb; USB LDO
- ab8500_ldo_usb_reg: ab8500_ldo_usb {
- };
-
// supply for ab8500-vaudio; VAUDIO LDO
ab8500_ldo_audio_reg: ab8500_ldo_audio {
};
diff --git a/arch/arm/boot/dts/ste-href.dtsi b/arch/arm/boot/dts/ste-href.dtsi
index 6422c53f2046..4f6acbd8c040 100644
--- a/arch/arm/boot/dts/ste-href.dtsi
+++ b/arch/arm/boot/dts/ste-href.dtsi
@@ -234,10 +234,6 @@
regulator-name = "V-TVOUT";
};
- ab8500_ldo_usb_reg: ab8500_ldo_usb {
- regulator-name = "dummy";
- };
-
ab8500_ldo_audio_reg: ab8500_ldo_audio {
regulator-name = "V-AUD";
};
diff --git a/arch/arm/boot/dts/ste-snowball.dts b/arch/arm/boot/dts/ste-snowball.dts
index 3428290644ba..c6074912c368 100644
--- a/arch/arm/boot/dts/ste-snowball.dts
+++ b/arch/arm/boot/dts/ste-snowball.dts
@@ -474,10 +474,6 @@
regulator-name = "V-TVOUT";
};
- ab8500_ldo_usb_reg: ab8500_ldo_usb {
- regulator-name = "dummy";
- };
-
ab8500_ldo_audio_reg: ab8500_ldo_audio {
regulator-name = "V-AUD";
};
--
2.22.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH 0/1] arch: arm64: imx8mq: Add mux-controller to iomuxc_gpr
From: Guido Günther @ 2019-08-22 11:07 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, NXP Linux Team,
Lucas Stach, Abel Vesa, Anson Huang, Carlo Caione,
Angus Ainslie (Purism), Guido Günther, Andrey Smirnov,
devicetree, linux-arm-kernel, linux-kernel
This adds a mux-controller to the iomuxc_gpr syscon. It will e.g. be used by
the NWL DSI host controller driver.
Guido Günther (1):
arm64: dts: imx8mq: Add mux controller to iomuxc_gpr
arch/arm64/boot/dts/freescale/imx8mq.dtsi | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v3 2/2] PM / devfreq: Use dev_pm_qos for sysfs min/max_freq
From: Chanwoo Choi @ 2019-08-22 11:10 UTC (permalink / raw)
To: Leonard Crestez, MyungJoo Ham, Kyungmin Park, Viresh Kumar
Cc: Artur Świgoń, Jacky Bai, Saravana Kannan,
linux-pm@vger.kernel.org, Krzysztof Kozlowski, Alexandre Bailon,
cpgs (cpgs@samsung.com), Georgi Djakov,
linux-arm-kernel@lists.infradead.org
In-Reply-To: <VI1PR04MB7023D551332373BE98CA60E8EEA50@VI1PR04MB7023.eurprd04.prod.outlook.com>
On 19. 8. 22. 오후 7:58, Leonard Crestez wrote:
> On 8/22/2019 1:03 PM, Chanwoo Choi wrote:
>> On 19. 8. 21. 오후 10:03, Leonard Crestez wrote:
>>> On 21.08.2019 05:02, Chanwoo Choi wrote:
>>>> On 19. 8. 21. 오전 12:24, Leonard Crestez wrote:
>>>>> Now that devfreq supports dev_pm_qos requests we can use them to handle
>>>>> the min/max_freq values set by userspace in sysfs, similar to cpufreq.
>>>>>
>>>>> Since dev_pm_qos handles frequencies as kHz this change reduces the
>>>>> precision of min_freq and max_freq. This shouldn't introduce problems
>>>>> because frequencies which are not an integer number of kHz are likely
>>>>> not an integer number of Hz either.
>>>>>
>>>>> Try to ensure compatibilitity by rounding min values down and rounding
>>>>> max values up.
>>>>>
>>>>> Simplify the {min,max}_freq_store code by setting "null" values of 0 and
>>>>> MAX_S32 respectively instead of clamping to what freq tables are
>>>>> actually supported. Values are already automatically clamped on
>>>>> readback.
>>>>>
>>>>> Also simplify by droping the limitation that userspace min_freq must be
>>>>> lower than userspace max_freq, it is already documented that max_freq
>>>>> takes precedence.
>>>>>
>>>>> @@ -1358,33 +1371,20 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr,
>>>>>
>>>>> ret = sscanf(buf, "%lu", &value);
>>>>> if (ret != 1)
>>>>> return -EINVAL;
>>>>>
>>>>> - mutex_lock(&df->lock);
>>>>> -
>>>>> - if (value) {
>>>>> - if (value > df->max_freq) {
>>>>> - ret = -EINVAL;
>>>>> - goto unlock;
>>>>> - }
>>>>
>>>> Actually, the user can input the value they want.
>>>> So, the above code is not necessary because the devfreq->scaling_max_freq
>>>> is never overflow from supported maximum frequency. The devfreq->scaling_max_freq
>>>> is based on OPP entries from DT.
>>>>
>>>> But, if we replace the existing request way of devfreq-cooling.c
>>>> with dev_pm_qos, devfreq->scaling_max_freq doesn't guarantee
>>>> the supported maximum frequency. >
>>>> We need to keep the supported min_freq/max_freq value without dev_pm_qos
>>>> requirement because the dev_pm_qos requirement might have the overflow value.
>>>> the dev_pm_qos doesn't know the supported minimum and maximum frequency
>>>> of devfreq device.
>>>
>>> I'm not sure I understand what you mean. My patch allows user to set
>>> entirely arbitrary min/max rates and this is good because we already
>>> have a well-defined way to handle this: max overrides min.
>>>
>>> The scaling_min_freq and scaling_max_freq variables can just be kept
>>> around indefinitely no matter what happens to thermal. They're just a
>>> cache for dev_pm_opp_find_freq_ceil and dev_pm_opp_find_freq_floor.
>>
>> This patch doesn't check the range of input value
>> with the supported frequencies of devfreq device.
>>
>> For example,
>> The devfreq0 device has the following available frequencies
>> 100000000 200000000 300000000
>>
>> and then user enters the 500000000 as following:
>> echo 500000000 > /sys/class/devfreq/devfreq0/min_freq
>>
>> In result, get_effective_min_freq() will return the 500000000.
>> It is wrong value. it show the unsupported frequency through
>> min_freq sysfs path.
>
> Through dev_pm_qos devices can also ask for a freq higher than the
> maximum OPP and unlike sysfs there is no way to reject such requests,
> instead PM qos claims it's based on "best effort".
>
> There are many requests in the kernel for "PM_QOS_CPU_DMA_LATENCY 0" and
> I think that DEV_PM_QOS_MIN_FREQUENCY, MAX_S32 is a reasonable way for a
> device to request "max performance" from devfreq.
>
> Rejecting min > max is complicated (what happens to rejected requests
> when max goes back up?) and I think it's better to just stick with a
> "max overrides min" policy since it can already deal with conflicts.
>
> Do you have a usecase for rejecting out-of-range min_freq from
> userspace? cpufreq also accepts arbitrary min/max values and handles them.
I don't mean the rejecting when user enter the out-of-range frequency.
As I commented, user can enter the value they want. But, we should
check the range of input because devfreq have to show the correct supported
frequency through sysfs.
>
> In theory pm_qos could be extended to differentiate between "soft"
> requests (current behavior) and "hard" requests which return errors if
> they can't be guaranteed but this is far out of scope.
I think that you agreed the limitation of dev_pm_qos when entering
or requesting the unsupported frequency.
Until fixing it on dev_pm_qos, it have to be handled on consumer
like devfreq. I think that get_min_freq() and get_max_freq() have
to check the range of return value of dev_pm_qos_read_value().
--
Best Regards,
Chanwoo Choi
Samsung Electronics
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v3 05/10] KVM: arm64: Support stolen time reporting via shared structure
From: Steven Price @ 2019-08-22 11:00 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Mark Rutland, Radim Krčmář, kvm, Suzuki K Pouloze,
Marc Zyngier, linux-doc, linux-kernel, Russell King, James Morse,
linux-arm-kernel, Catalin Marinas, Paolo Bonzini, Will Deacon,
kvmarm, Julien Thierry
In-Reply-To: <20190822113942.0000701f@huawei.com>
On 22/08/2019 11:39, Jonathan Cameron wrote:
> On Wed, 21 Aug 2019 16:36:51 +0100
> Steven Price <steven.price@arm.com> wrote:
>
>> Implement the service call for configuring a shared structure between a
>> VCPU and the hypervisor in which the hypervisor can write the time
>> stolen from the VCPU's execution time by other tasks on the host.
>>
>> The hypervisor allocates memory which is placed at an IPA chosen by user
>> space. The hypervisor then updates the shared structure using
>> kvm_put_guest() to ensure single copy atomicity of the 64-bit value
>> reporting the stolen time in nanoseconds.
>>
>> Whenever stolen time is enabled by the guest, the stolen time counter is
>> reset.
>>
>> The stolen time itself is retrieved from the sched_info structure
>> maintained by the Linux scheduler code. We enable SCHEDSTATS when
>> selecting KVM Kconfig to ensure this value is meaningful.
>>
>> Signed-off-by: Steven Price <steven.price@arm.com>
>
> One totally trivial comment inline... Feel free to ignore :)
>
[...]
>> +int kvm_hypercall_stolen_time(struct kvm_vcpu *vcpu)
>> +{
>> + u64 ret;
>> + int err;
>> +
>> + /*
>> + * Start counting stolen time from the time the guest requests
>> + * the feature enabled.
>> + */
>> + vcpu->arch.steal.steal = 0;
>> + vcpu->arch.steal.last_steal = current->sched_info.run_delay;
>> +
>> + err = kvm_update_stolen_time(vcpu, true);
>> +
>> + if (err)
>> + ret = SMCCC_RET_NOT_SUPPORTED;
>
> Trivial by why not
> return SMCCC_RET_NOT_SUPPORTED;
>
> return vcpu->kvm->arch.pvtime.st_base +
> ...
> Drops the indentation a bit and puts the error handling out of
> line which is slightly nicer to read (to my eyes).
Yes that's a nice change - drops the extra "ret" variable too.
Thanks,
Steve
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v3 2/2] PM / devfreq: Use dev_pm_qos for sysfs min/max_freq
From: Leonard Crestez @ 2019-08-22 10:58 UTC (permalink / raw)
To: Chanwoo Choi, MyungJoo Ham, Kyungmin Park, Viresh Kumar
Cc: Artur Świgoń, Jacky Bai, Saravana Kannan,
linux-pm@vger.kernel.org, Krzysztof Kozlowski, Alexandre Bailon,
cpgs (cpgs@samsung.com), Georgi Djakov,
linux-arm-kernel@lists.infradead.org
In-Reply-To: <921d9eb8-aa38-6e67-ac2e-55e01bf630f5@samsung.com>
On 8/22/2019 1:03 PM, Chanwoo Choi wrote:
> On 19. 8. 21. 오후 10:03, Leonard Crestez wrote:
>> On 21.08.2019 05:02, Chanwoo Choi wrote:
>>> On 19. 8. 21. 오전 12:24, Leonard Crestez wrote:
>>>> Now that devfreq supports dev_pm_qos requests we can use them to handle
>>>> the min/max_freq values set by userspace in sysfs, similar to cpufreq.
>>>>
>>>> Since dev_pm_qos handles frequencies as kHz this change reduces the
>>>> precision of min_freq and max_freq. This shouldn't introduce problems
>>>> because frequencies which are not an integer number of kHz are likely
>>>> not an integer number of Hz either.
>>>>
>>>> Try to ensure compatibilitity by rounding min values down and rounding
>>>> max values up.
>>>>
>>>> Simplify the {min,max}_freq_store code by setting "null" values of 0 and
>>>> MAX_S32 respectively instead of clamping to what freq tables are
>>>> actually supported. Values are already automatically clamped on
>>>> readback.
>>>>
>>>> Also simplify by droping the limitation that userspace min_freq must be
>>>> lower than userspace max_freq, it is already documented that max_freq
>>>> takes precedence.
>>>>
>>>> @@ -1358,33 +1371,20 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr,
>>>>
>>>> ret = sscanf(buf, "%lu", &value);
>>>> if (ret != 1)
>>>> return -EINVAL;
>>>>
>>>> - mutex_lock(&df->lock);
>>>> -
>>>> - if (value) {
>>>> - if (value > df->max_freq) {
>>>> - ret = -EINVAL;
>>>> - goto unlock;
>>>> - }
>>>
>>> Actually, the user can input the value they want.
>>> So, the above code is not necessary because the devfreq->scaling_max_freq
>>> is never overflow from supported maximum frequency. The devfreq->scaling_max_freq
>>> is based on OPP entries from DT.
>>>
>>> But, if we replace the existing request way of devfreq-cooling.c
>>> with dev_pm_qos, devfreq->scaling_max_freq doesn't guarantee
>>> the supported maximum frequency. >
>>> We need to keep the supported min_freq/max_freq value without dev_pm_qos
>>> requirement because the dev_pm_qos requirement might have the overflow value.
>>> the dev_pm_qos doesn't know the supported minimum and maximum frequency
>>> of devfreq device.
>>
>> I'm not sure I understand what you mean. My patch allows user to set
>> entirely arbitrary min/max rates and this is good because we already
>> have a well-defined way to handle this: max overrides min.
>>
>> The scaling_min_freq and scaling_max_freq variables can just be kept
>> around indefinitely no matter what happens to thermal. They're just a
>> cache for dev_pm_opp_find_freq_ceil and dev_pm_opp_find_freq_floor.
>
> This patch doesn't check the range of input value
> with the supported frequencies of devfreq device.
>
> For example,
> The devfreq0 device has the following available frequencies
> 100000000 200000000 300000000
>
> and then user enters the 500000000 as following:
> echo 500000000 > /sys/class/devfreq/devfreq0/min_freq
>
> In result, get_effective_min_freq() will return the 500000000.
> It is wrong value. it show the unsupported frequency through
> min_freq sysfs path.
Through dev_pm_qos devices can also ask for a freq higher than the
maximum OPP and unlike sysfs there is no way to reject such requests,
instead PM qos claims it's based on "best effort".
There are many requests in the kernel for "PM_QOS_CPU_DMA_LATENCY 0" and
I think that DEV_PM_QOS_MIN_FREQUENCY, MAX_S32 is a reasonable way for a
device to request "max performance" from devfreq.
Rejecting min > max is complicated (what happens to rejected requests
when max goes back up?) and I think it's better to just stick with a
"max overrides min" policy since it can already deal with conflicts.
Do you have a usecase for rejecting out-of-range min_freq from
userspace? cpufreq also accepts arbitrary min/max values and handles them.
In theory pm_qos could be extended to differentiate between "soft"
requests (current behavior) and "hard" requests which return errors if
they can't be guaranteed but this is far out of scope.
--
Regards,
Leonard
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v3 07/10] KVM: arm64: Provide a PV_TIME device to user space
From: Jonathan Cameron @ 2019-08-22 10:57 UTC (permalink / raw)
To: Steven Price
Cc: Mark Rutland, kvm, Radim Krčmář, Marc Zyngier,
Suzuki K Pouloze, linux-doc, linux-kernel, Russell King,
James Morse, Julien Thierry, Catalin Marinas, Paolo Bonzini,
Will Deacon, kvmarm, linux-arm-kernel
In-Reply-To: <20190821153656.33429-8-steven.price@arm.com>
On Wed, 21 Aug 2019 16:36:53 +0100
Steven Price <steven.price@arm.com> wrote:
> Allow user space to inform the KVM host where in the physical memory
> map the paravirtualized time structures should be located.
>
> A device is created which provides the base address of an array of
> Stolen Time (ST) structures, one for each VCPU. There must be (64 *
> total number of VCPUs) bytes of memory available at this location.
>
> The address is given in terms of the physical address visible to
> the guest and must be page aligned. The guest will discover the address
> via a hypercall.
>
> Signed-off-by: Steven Price <steven.price@arm.com>
Hi Steven,
One general question inline. I'm not particularly familiar with this area
of the kernel, so maybe I'm missing something obvious, but having
.destroy free the kvm_device which wasn't created in .create seems
'unusual'.
Otherwise, FWIW looks good to me.
Jonathan
> ---
> arch/arm/include/asm/kvm_host.h | 4 ++
> arch/arm64/include/asm/kvm_host.h | 1 +
> arch/arm64/include/uapi/asm/kvm.h | 8 +++
> include/uapi/linux/kvm.h | 2 +
> virt/kvm/arm/arm.c | 1 +
> virt/kvm/arm/pvtime.c | 94 +++++++++++++++++++++++++++++++
> 6 files changed, 110 insertions(+)
>
> diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h
> index 47d2ced99421..b6c8dbc0556b 100644
> --- a/arch/arm/include/asm/kvm_host.h
> +++ b/arch/arm/include/asm/kvm_host.h
> @@ -325,6 +325,10 @@ static inline int kvm_arch_vm_ioctl_check_extension(struct kvm *kvm, long ext)
> int kvm_perf_init(void);
> int kvm_perf_teardown(void);
>
> +static inline void kvm_pvtime_init(void)
> +{
> +}
> +
> static inline int kvm_hypercall_pv_features(struct kvm_vcpu *vcpu)
> {
> return SMCCC_RET_NOT_SUPPORTED;
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index b6fa7beffd8a..7b2147f62c16 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -489,6 +489,7 @@ void handle_exit_early(struct kvm_vcpu *vcpu, struct kvm_run *run,
> int kvm_perf_init(void);
> int kvm_perf_teardown(void);
>
> +void kvm_pvtime_init(void);
> int kvm_hypercall_pv_features(struct kvm_vcpu *vcpu);
> int kvm_hypercall_stolen_time(struct kvm_vcpu *vcpu);
> int kvm_update_stolen_time(struct kvm_vcpu *vcpu, bool init);
> diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
> index 9a507716ae2f..209c4de67306 100644
> --- a/arch/arm64/include/uapi/asm/kvm.h
> +++ b/arch/arm64/include/uapi/asm/kvm.h
> @@ -367,6 +367,14 @@ struct kvm_vcpu_events {
> #define KVM_PSCI_RET_INVAL PSCI_RET_INVALID_PARAMS
> #define KVM_PSCI_RET_DENIED PSCI_RET_DENIED
>
> +/* Device Control API: PV_TIME */
> +#define KVM_DEV_ARM_PV_TIME_REGION 0
> +#define KVM_DEV_ARM_PV_TIME_ST 0
> +struct kvm_dev_arm_st_region {
> + __u64 gpa;
> + __u64 size;
> +};
> +
> #endif
>
> #endif /* __ARM_KVM_H__ */
> diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
> index 5e3f12d5359e..265156a984f2 100644
> --- a/include/uapi/linux/kvm.h
> +++ b/include/uapi/linux/kvm.h
> @@ -1222,6 +1222,8 @@ enum kvm_device_type {
> #define KVM_DEV_TYPE_ARM_VGIC_ITS KVM_DEV_TYPE_ARM_VGIC_ITS
> KVM_DEV_TYPE_XIVE,
> #define KVM_DEV_TYPE_XIVE KVM_DEV_TYPE_XIVE
> + KVM_DEV_TYPE_ARM_PV_TIME,
> +#define KVM_DEV_TYPE_ARM_PV_TIME KVM_DEV_TYPE_ARM_PV_TIME
> KVM_DEV_TYPE_MAX,
> };
>
> diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c
> index 5e8343e2dd62..bfb5a842e6ab 100644
> --- a/virt/kvm/arm/arm.c
> +++ b/virt/kvm/arm/arm.c
> @@ -1494,6 +1494,7 @@ static int init_subsystems(void)
>
> kvm_perf_init();
> kvm_coproc_table_init();
> + kvm_pvtime_init();
>
> out:
> on_each_cpu(_kvm_arch_hardware_disable, NULL, 1);
> diff --git a/virt/kvm/arm/pvtime.c b/virt/kvm/arm/pvtime.c
> index 28603689f6e0..3e55c1fb6a49 100644
> --- a/virt/kvm/arm/pvtime.c
> +++ b/virt/kvm/arm/pvtime.c
> @@ -2,7 +2,9 @@
> // Copyright (C) 2019 Arm Ltd.
>
> #include <linux/arm-smccc.h>
> +#include <linux/kvm_host.h>
>
> +#include <asm/kvm_mmu.h>
> #include <asm/pvclock-abi.h>
>
> #include <kvm/arm_hypercalls.h>
> @@ -86,3 +88,95 @@ int kvm_hypercall_stolen_time(struct kvm_vcpu *vcpu)
>
> return ret;
> }
> +
> +static int kvm_arm_pvtime_create(struct kvm_device *dev, u32 type)
> +{
> + return 0;
> +}
> +
> +static void kvm_arm_pvtime_destroy(struct kvm_device *dev)
> +{
> + struct kvm_arch_pvtime *pvtime = &dev->kvm->arch.pvtime;
> +
> + pvtime->st_base = GPA_INVALID;
> + kfree(dev);
Nothing to do with your patch as such... All users do the same.
This seems miss balanced. Why do we need to free the device by hand
when we didn't create it in the create function? I appreciate
the comments say this is needed, but as far as I can see every
single callback does kfree(dev) at the end which seems an
odd thing to do.
> +}
> +
> +static int kvm_arm_pvtime_set_attr(struct kvm_device *dev,
> + struct kvm_device_attr *attr)
> +{
> + struct kvm *kvm = dev->kvm;
> + struct kvm_arch_pvtime *pvtime = &kvm->arch.pvtime;
> + u64 __user *user = (u64 __user *)attr->addr;
> + struct kvm_dev_arm_st_region region;
> +
> + switch (attr->group) {
> + case KVM_DEV_ARM_PV_TIME_REGION:
> + if (copy_from_user(®ion, user, sizeof(region)))
> + return -EFAULT;
> + if (region.gpa & ~PAGE_MASK)
> + return -EINVAL;
> + if (region.size & ~PAGE_MASK)
> + return -EINVAL;
> + switch (attr->attr) {
> + case KVM_DEV_ARM_PV_TIME_ST:
> + if (pvtime->st_base != GPA_INVALID)
> + return -EEXIST;
> + pvtime->st_base = region.gpa;
> + pvtime->st_size = region.size;
> + return 0;
> + }
> + break;
> + }
> + return -ENXIO;
> +}
> +
> +static int kvm_arm_pvtime_get_attr(struct kvm_device *dev,
> + struct kvm_device_attr *attr)
> +{
> + struct kvm_arch_pvtime *pvtime = &dev->kvm->arch.pvtime;
> + u64 __user *user = (u64 __user *)attr->addr;
> + struct kvm_dev_arm_st_region region;
> +
> + switch (attr->group) {
> + case KVM_DEV_ARM_PV_TIME_REGION:
> + switch (attr->attr) {
> + case KVM_DEV_ARM_PV_TIME_ST:
> + region.gpa = pvtime->st_base;
> + region.size = pvtime->st_size;
> + if (copy_to_user(user, ®ion, sizeof(region)))
> + return -EFAULT;
> + return 0;
> + }
> + break;
> + }
> + return -ENXIO;
> +}
> +
> +static int kvm_arm_pvtime_has_attr(struct kvm_device *dev,
> + struct kvm_device_attr *attr)
> +{
> + switch (attr->group) {
> + case KVM_DEV_ARM_PV_TIME_REGION:
> + switch (attr->attr) {
> + case KVM_DEV_ARM_PV_TIME_ST:
> + return 0;
> + }
> + break;
> + }
> + return -ENXIO;
> +}
> +
> +static const struct kvm_device_ops pvtime_ops = {
> + "Arm PV time",
> + .create = kvm_arm_pvtime_create,
> + .destroy = kvm_arm_pvtime_destroy,
> + .set_attr = kvm_arm_pvtime_set_attr,
> + .get_attr = kvm_arm_pvtime_get_attr,
> + .has_attr = kvm_arm_pvtime_has_attr
> +};
> +
> +void kvm_pvtime_init(void)
> +{
> + kvm_register_device_ops(&pvtime_ops, KVM_DEV_TYPE_ARM_PV_TIME);
> +}
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v10 09/23] iommu/io-pgtable-arm-v7s: Extend to support PA[33:32] for MediaTek
From: Robin Murphy @ 2019-08-22 10:57 UTC (permalink / raw)
To: Will Deacon
Cc: youlin.pei, devicetree, Nicolas Boichat, cui.zhang,
srv_heupstream, Tomasz Figa, Joerg Roedel, linux-kernel,
Evan Green, chao.hao, iommu, Rob Herring, linux-mediatek,
linux-arm-kernel, Matthias Brugger, ming-fan.chen, anan.sun,
Matthias Kaehlcke, Yong Wu
In-Reply-To: <20190822101749.3kwzd5lb7zinsord@willie-the-truck>
On 2019-08-22 11:17 am, Will Deacon wrote:
> On Thu, Aug 22, 2019 at 11:08:58AM +0100, Robin Murphy wrote:
>> On 2019-08-22 9:56 am, Yong Wu wrote:
>>> On Wed, 2019-08-21 at 16:24 +0100, Will Deacon wrote:
>>>> On Wed, Aug 21, 2019 at 09:53:12PM +0800, Yong Wu wrote:
>>>>> MediaTek extend the arm v7s descriptor to support up to 34 bits PA where
>>>>> the bit32 and bit33 are encoded in the bit9 and bit4 of the PTE
>>>>> respectively. Meanwhile the iova still is 32bits.
>>>>>
>>>>> Regarding whether the pagetable address could be over 4GB, the mt8183
>>>>> support it while the previous mt8173 don't, thus keep it as is.
>>>>>
>>>>> Signed-off-by: Yong Wu <yong.wu@mediatek.com>
>>>>> ---
>>>>> drivers/iommu/io-pgtable-arm-v7s.c | 32 +++++++++++++++++++++++++-------
>>>>> include/linux/io-pgtable.h | 7 +++----
>>>>> 2 files changed, 28 insertions(+), 11 deletions(-)
>>>>
>>>> [...]
>>>>
>>>>> @@ -731,7 +747,9 @@ static struct io_pgtable *arm_v7s_alloc_pgtable(struct io_pgtable_cfg *cfg,
>>>>> {
>>>>> struct arm_v7s_io_pgtable *data;
>>>>> - if (cfg->ias > ARM_V7S_ADDR_BITS || cfg->oas > ARM_V7S_ADDR_BITS)
>>>>> + if (cfg->ias > ARM_V7S_ADDR_BITS ||
>>>>> + (cfg->oas > ARM_V7S_ADDR_BITS &&
>>>>> + !(cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_EXT)))
>>>>
>>>> Please can you instead change arm_v7s_alloc_pgtable() so that it allows an
>>>> ias of up to 34 when the IO_PGTABLE_QUIRK_ARM_MTK_EXT is set?
>>>
>>> Here I only simply skip the oas checking for our case. then which way do
>>> your prefer? something like you commented before:?
>>>
>>>
>>> if (cfg->ias > ARM_V7S_ADDR_BITS)
>>> return NULL;
>>>
>>> if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_EXT) {
>>> if (!IS_ENABLED(CONFIG_PHYS_ADDR_T_64BIT))
>>> cfg->oas = min(cfg->oas, ARM_V7S_ADDR_BITS);
>>> else if (cfg->oas > 34)
>>> return NULL;
>>> } else if (cfg->oas > ARM_V7S_ADDR_BITS) {
>>> return NULL;
>>> }
>>
>> All it should take is something like:
>>
>> if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_EXT)
>> max_oas = 34;
>> else
>> max_oas = 32;
>> if (cfg->oas > max_oas)
>> return NULL;
>>
>> or even just:
>>
>> if (cfg->oas > 32 ||
>> (cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_EXT && cfg->oas > 34))
>> return NULL;
>>
>> (and if we prefer the latter style, perhaps we could introduce some kind of
>> "is_mtk_4gb()" helper to save on verbosity)
>
> I wondered the same thing, but another place we'd want the check is in
> iopte_to_paddr() which probably needs the PHYS_ADDR_T check to avoid GCC
> warnings, although I didn't try it.
I'm pretty sure I confirmed that "paddr |= BIT_ULL(32)" doesn't warn
when phys_addt_t is 32-bit - it's well-defined unsigned integer
truncation after all, and if GCC starts warning about all the valid
no-op code it optimises away then it's going to run up against
IS_ENABLED() first and foremost ;)
> So if we did:
>
> static bool cfg_mtk_ext_enabled(struct io_pgtable_cfg *cfg)
> {
> return IS_ENABLED(CONFIG_PHYS_ADDR_T_64BIT) &&
> cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_EXT;
> }
>
> Then I suppose we could do this in _alloc():
>
> if (cfg->oas > cfg_mtk_ext_enabled(cfg) ? 34 : ARM_V7S_ADDR_BITS)
> return NULL;
>
> and then this in iopte_to_paddr():
>
> [...]
>
> paddr = pte & mask;
> if (!cfg_mtk_ext_enabled(cfg))
> return paddr;
>
> if (pte & ARM_V7S_ATTR_MTK_PA_BIT32)
> paddr |= ...
>
> [...]
>
> What do you reckon?
Yeah, that's the general shape of things I was picturing - I'm not that
fussed about the PHYS_ADDR_T_64BIT thing, especially if it's wrapped up
in just one place, so if you do want to keep it as belt-and-braces I'll
just consider it a slight code size optimisation for 32-bit builds.
Robin.
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v2 2/3] dt-bindings: display/bridge: Add binding for NWL mipi dsi host controller
From: Guido Günther @ 2019-08-22 10:49 UTC (permalink / raw)
To: Laurent Pinchart
Cc: Mark Rutland, devicetree, Jernej Skrabec, Pengutronix Kernel Team,
Sam Ravnborg, Neil Armstrong, David Airlie, Fabio Estevam,
Sascha Hauer, Jonas Karlman, linux-kernel, dri-devel,
Andrzej Hajda, Rob Herring, NXP Linux Team, Daniel Vetter,
Robert Chiras, Lee Jones, Shawn Guo, linux-arm-kernel
In-Reply-To: <20190821181518.GB26759@pendragon.ideasonboard.com>
Hi Laurent,
thanks for having a look!
On Wed, Aug 21, 2019 at 09:15:18PM +0300, Laurent Pinchart wrote:
> Hi Guido,
>
> Thank you for the patch.
>
> On Fri, Aug 09, 2019 at 06:24:22PM +0200, Guido Günther wrote:
> > The Northwest Logic MIPI DSI IP core can be found in NXPs i.MX8 SoCs.
> >
> > Signed-off-by: Guido Günther <agx@sigxcpu.org>
> > ---
> > .../bindings/display/bridge/nwl-dsi.yaml | 155 ++++++++++++++++++
> > 1 file changed, 155 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml b/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml
> > new file mode 100644
> > index 000000000000..5ed8bc4a4d18
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml
> > @@ -0,0 +1,155 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/display/bridge/imx-nwl-dsi.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Northwest Logic MIPI-DSI on imx SoCs
> > +
> > +maintainers:
> > + - Guido Gúnther <agx@sigxcpu.org>
> > + - Robert Chiras <robert.chiras@nxp.com>
> > +
> > +description: |
> > + NWL MIPI-DSI host controller found on i.MX8 platforms. This is a dsi bridge for
> > + the SOCs NWL MIPI-DSI host controller.
> > +
> > +properties:
> > + compatible:
> > + oneOf:
> > + - items:
> > + - const: fsl,imx8mq-nwl-dsi
> > +
> > + reg:
> > + maxItems: 1
> > +
> > + interrupts:
> > + maxItems: 1
> > +
> > + clocks:
> > + items:
> > + - description: DSI core clock
> > + - description: RX_ESC clock (used in escape mode)
> > + - description: TX_ESC clock (used in escape mode)
> > + - description: PHY_REF clock
> > +
> > + clock-names:
> > + items:
> > + - const: core
> > + - const: rx_esc
> > + - const: tx_esc
> > + - const: phy_ref
> > +
> > + phys:
> > + maxItems: 1
> > + description:
> > + A phandle to the phy module representing the DPHY
> > +
> > + phy-names:
> > + items:
> > + - const: dphy
> > +
> > + power-domains:
> > + maxItems: 1
> > + description:
> > + A phandle to the power domain
> > +
> > + resets:
> > + maxItems: 4
> > + description:
> > + A phandle to the reset controller
> > +
> > + reset-names:
> > + items:
> > + - const: byte
> > + - const: dpi
> > + - const: esc
> > + - const: pclk
> > +
> > + mux-sel:
> > + maxItems: 1
> > + description:
> > + A phandle to the MUX register set
>
> Did you mean the MUX syscon ? A phandle to a register set sounds a bit
> strange.
That one is gone in v3 in favour of mux-controls.
>
> > +
> > + port:
> > + type: object
> > + description:
> > + A input put or output port node.
>
> s/input put/input/
Thanks! I've shuffled this around in v3 and added some more detail.
Cheers,
-- Guido
>
> > +
> > + ports:
> > + type: object
> > + description:
> > + A node containing DSI input & output port nodes with endpoint
> > + definitions as documented in
> > + Documentation/devicetree/bindings/graph.txt.
> > +
> > +patternProperties:
> > + "^panel@[0-9]+$": true
> > +
> > +allOf:
> > + - if:
> > + properties:
> > + compatible:
> > + contains:
> > + enum:
> > + - fsl,imx8mq-nwl-dsi
> > + then:
> > + required:
> > + - resets
> > + - reset-names
> > + - mux-sel
> > +
> > +required:
> > + - compatible
> > + - reg
> > + - interrupts
> > + - clocks
> > + - clock-names
> > + - phys
> > + - phy-names
> > +
> > +examples:
> > + - |
> > +
> > + mipi_dsi: mipi_dsi@30a00000 {
> > + #address-cells = <1>;
> > + #size-cells = <0>;
> > + compatible = "fsl,imx8mq-nwl-dsi";
> > + reg = <0x30A00000 0x300>;
> > + clocks = <&clk 163>, <&clk 244>, <&clk 245>, <&clk 164>;
> > + clock-names = "core", "rx_esc", "tx_esc", "phy_ref";
> > + interrupts = <0 34 4>;
> > + power-domains = <&pgc_mipi>;
> > + resets = <&src 0>, <&src 1>, <&src 2>, <&src 3>;
> > + reset-names = "byte", "dpi", "esc", "pclk";
> > + mux-sel = <&iomuxc_gpr>;
> > + phys = <&dphy>;
> > + phy-names = "dphy";
> > +
> > + panel@0 {
> > + compatible = "...";
> > + port@0 {
> > + panel_in: endpoint {
> > + remote-endpoint = <&mipi_dsi_out>;
> > + };
> > + };
> > + };
> > +
> > + ports {
> > + #address-cells = <1>;
> > + #size-cells = <0>;
> > +
> > + port@0 {
> > + reg = <0>;
> > + mipi_dsi_in: endpoint {
> > + remote-endpoint = <&lcdif_mipi_dsi>;
> > + };
> > + };
> > + port@1 {
> > + reg = <1>;
> > + mipi_dsi_out: endpoint {
> > + remote-endpoint = <&panel_in>;
> > + };
> > + };
> > + };
> > + };
>
> --
> Regards,
>
> Laurent Pinchart
>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH v3 2/2] drm/bridge: Add NWL MIPI DSI host controller support
From: Guido Günther @ 2019-08-22 10:44 UTC (permalink / raw)
To: David Airlie, Daniel Vetter, Rob Herring, Mark Rutland, Shawn Guo,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
NXP Linux Team, Andrzej Hajda, Neil Armstrong, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Lee Jones, Guido Günther,
dri-devel, devicetree, linux-arm-kernel, linux-kernel,
Robert Chiras, Sam Ravnborg, Arnd Bergmann
In-Reply-To: <cover.1566470526.git.agx@sigxcpu.org>
This adds initial support for the NWL MIPI DSI Host controller found on
i.MX8 SoCs.
It adds support for the i.MX8MQ but the same IP can be found on
e.g. the i.MX8QXP.
It has been tested on the Librem 5 devkit using mxsfb.
Signed-off-by: Guido Günther <agx@sigxcpu.org>
Co-developed-by: Robert Chiras <robert.chiras@nxp.com>
---
drivers/gpu/drm/bridge/Kconfig | 2 +
drivers/gpu/drm/bridge/Makefile | 1 +
drivers/gpu/drm/bridge/nwl-dsi/Kconfig | 16 +
drivers/gpu/drm/bridge/nwl-dsi/Makefile | 4 +
drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.c | 501 ++++++++++++++++
drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.h | 65 +++
drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.c | 700 +++++++++++++++++++++++
drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.h | 112 ++++
8 files changed, 1401 insertions(+)
create mode 100644 drivers/gpu/drm/bridge/nwl-dsi/Kconfig
create mode 100644 drivers/gpu/drm/bridge/nwl-dsi/Makefile
create mode 100644 drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.c
create mode 100644 drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.h
create mode 100644 drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.c
create mode 100644 drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.h
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 1cc9f502c1f2..7980b5c2156f 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -154,6 +154,8 @@ source "drivers/gpu/drm/bridge/analogix/Kconfig"
source "drivers/gpu/drm/bridge/adv7511/Kconfig"
+source "drivers/gpu/drm/bridge/nwl-dsi/Kconfig"
+
source "drivers/gpu/drm/bridge/synopsys/Kconfig"
endmenu
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index 4934fcf5a6f8..d9f6c0f77592 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -16,4 +16,5 @@ obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/
obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/
obj-$(CONFIG_DRM_TI_SN65DSI86) += ti-sn65dsi86.o
obj-$(CONFIG_DRM_TI_TFP410) += ti-tfp410.o
+obj-$(CONFIG_DRM_NWL_MIPI_DSI) += nwl-dsi/
obj-y += synopsys/
diff --git a/drivers/gpu/drm/bridge/nwl-dsi/Kconfig b/drivers/gpu/drm/bridge/nwl-dsi/Kconfig
new file mode 100644
index 000000000000..3b157a9f2229
--- /dev/null
+++ b/drivers/gpu/drm/bridge/nwl-dsi/Kconfig
@@ -0,0 +1,16 @@
+config DRM_NWL_MIPI_DSI
+ tristate "Support for Northwest Logic MIPI DSI Host controller"
+ depends on DRM
+ depends on COMMON_CLK
+ depends on OF && HAS_IOMEM
+ select DRM_KMS_HELPER
+ select DRM_MIPI_DSI
+ select DRM_PANEL_BRIDGE
+ select GENERIC_PHY_MIPI_DPHY
+ select MFD_SYSCON
+ select MULTIPLEXER
+ select REGMAP_MMIO
+ help
+ This enables the Northwest Logic MIPI DSI Host controller as
+ for example found on NXP's i.MX8 Processors.
+
diff --git a/drivers/gpu/drm/bridge/nwl-dsi/Makefile b/drivers/gpu/drm/bridge/nwl-dsi/Makefile
new file mode 100644
index 000000000000..804baf2f1916
--- /dev/null
+++ b/drivers/gpu/drm/bridge/nwl-dsi/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+nwl-mipi-dsi-y := nwl-drv.o nwl-dsi.o
+obj-$(CONFIG_DRM_NWL_MIPI_DSI) += nwl-mipi-dsi.o
+header-test-y += nwl-drv.h nwl-dsi.h
diff --git a/drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.c b/drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.c
new file mode 100644
index 000000000000..e457438738c0
--- /dev/null
+++ b/drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.c
@@ -0,0 +1,501 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * i.MX8 NWL MIPI DSI host driver
+ *
+ * Copyright (C) 2017 NXP
+ * Copyright (C) 2019 Purism SPC
+ */
+
+#include <linux/clk.h>
+#include <linux/irq.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/mux/consumer.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/reset.h>
+#include <linux/regmap.h>
+#include <linux/sys_soc.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+
+#include "nwl-drv.h"
+#include "nwl-dsi.h"
+
+#define DRV_NAME "nwl-dsi"
+
+/* Possible platform specific clocks */
+#define NWL_DSI_CLK_CORE "core"
+
+static const struct regmap_config nwl_dsi_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = NWL_DSI_IRQ_MASK2,
+ .name = DRV_NAME,
+};
+
+struct nwl_dsi_platform_data {
+ int (*poweron)(struct nwl_dsi *dsi);
+ int (*poweroff)(struct nwl_dsi *dsi);
+ int (*select_input)(struct nwl_dsi *dsi);
+ int (*deselect_input)(struct nwl_dsi *dsi);
+ struct nwl_dsi_plat_clk_config clk_config[NWL_DSI_MAX_PLATFORM_CLOCKS];
+};
+
+static inline struct nwl_dsi *bridge_to_dsi(struct drm_bridge *bridge)
+{
+ return container_of(bridge, struct nwl_dsi, bridge);
+}
+
+static int nwl_dsi_set_platform_clocks(struct nwl_dsi *dsi, bool enable)
+{
+ struct device *dev = dsi->dev;
+ const char *id;
+ struct clk *clk;
+ size_t i;
+ unsigned long rate;
+ int ret, result = 0;
+
+ DRM_DEV_DEBUG_DRIVER(dev, "%s platform clocks\n",
+ enable ? "enabling" : "disabling");
+ for (i = 0; i < ARRAY_SIZE(dsi->pdata->clk_config); i++) {
+ if (!dsi->clk_config[i].present)
+ continue;
+ id = dsi->clk_config[i].id;
+ clk = dsi->clk_config[i].clk;
+
+ if (enable) {
+ ret = clk_prepare_enable(clk);
+ if (ret < 0) {
+ DRM_DEV_ERROR(dev,
+ "Failed to enable %s clk: %d\n",
+ id, ret);
+ result = result ?: ret;
+ }
+ rate = clk_get_rate(clk);
+ DRM_DEV_DEBUG_DRIVER(dev, "Enabled %s clk @%lu Hz\n",
+ id, rate);
+ } else {
+ clk_disable_unprepare(clk);
+ DRM_DEV_DEBUG_DRIVER(dev, "Disabled %s clk\n", id);
+ }
+ }
+
+ return result;
+}
+
+static int nwl_dsi_plat_enable(struct nwl_dsi *dsi)
+{
+ struct device *dev = dsi->dev;
+ int ret;
+
+ if (dsi->pdata->select_input)
+ dsi->pdata->select_input(dsi);
+
+ ret = nwl_dsi_set_platform_clocks(dsi, true);
+ if (ret < 0)
+ return ret;
+
+ ret = dsi->pdata->poweron(dsi);
+ if (ret < 0)
+ DRM_DEV_ERROR(dev, "Failed to power on DSI: %d\n", ret);
+ return ret;
+}
+
+static void nwl_dsi_plat_disable(struct nwl_dsi *dsi)
+{
+ dsi->pdata->poweroff(dsi);
+ nwl_dsi_set_platform_clocks(dsi, false);
+ if (dsi->pdata->deselect_input)
+ dsi->pdata->deselect_input(dsi);
+}
+
+static void nwl_dsi_bridge_disable(struct drm_bridge *bridge)
+{
+ struct nwl_dsi *dsi = bridge_to_dsi(bridge);
+
+ nwl_dsi_disable(dsi);
+ nwl_dsi_plat_disable(dsi);
+ pm_runtime_put(dsi->dev);
+}
+
+static int nwl_dsi_get_dphy_params(struct nwl_dsi *dsi,
+ const struct drm_display_mode *mode,
+ union phy_configure_opts *phy_opts)
+{
+ unsigned long rate;
+ int ret;
+
+ if (dsi->lanes < 1 || dsi->lanes > 4)
+ return -EINVAL;
+
+ /*
+ * So far the DPHY spec minimal timings work for both mixel
+ * dphy and nwl dsi host
+ */
+ ret = phy_mipi_dphy_get_default_config(
+ mode->crtc_clock * 1000,
+ mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes,
+ &phy_opts->mipi_dphy);
+ if (ret < 0)
+ return ret;
+
+ rate = clk_get_rate(dsi->tx_esc_clk);
+ DRM_DEV_DEBUG_DRIVER(dsi->dev, "LP clk is @%lu Hz\n", rate);
+ phy_opts->mipi_dphy.lp_clk_rate = rate;
+
+ return 0;
+}
+
+static bool nwl_dsi_bridge_mode_fixup(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ /* At least LCDIF + NWL needs active high sync */
+ adjusted_mode->flags |= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
+ adjusted_mode->flags &= ~(DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC);
+
+ return true;
+}
+
+static enum drm_mode_status
+nwl_dsi_bridge_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode)
+{
+ struct nwl_dsi *dsi = bridge_to_dsi(bridge);
+ int bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
+
+ if (mode->clock * bpp > 15000000)
+ return MODE_CLOCK_HIGH;
+
+ if (mode->clock * bpp < 80000)
+ return MODE_CLOCK_LOW;
+
+ return MODE_OK;
+}
+
+static void
+nwl_dsi_bridge_mode_set(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ const struct drm_display_mode *adjusted_mode)
+{
+ struct nwl_dsi *dsi = bridge_to_dsi(bridge);
+ struct device *dev = dsi->dev;
+ union phy_configure_opts new_cfg;
+ unsigned long phy_ref_rate;
+ int ret;
+
+ ret = nwl_dsi_get_dphy_params(dsi, adjusted_mode, &new_cfg);
+ if (ret < 0)
+ return;
+
+ /*
+ * If hs clock is unchanged, we're all good - all parameters are
+ * derived from it atm.
+ */
+ if (new_cfg.mipi_dphy.hs_clk_rate == dsi->phy_cfg.mipi_dphy.hs_clk_rate)
+ return;
+
+ phy_ref_rate = clk_get_rate(dsi->phy_ref_clk);
+ DRM_DEV_DEBUG_DRIVER(dev, "PHY at ref rate: %lu\n", phy_ref_rate);
+ /* Save the new desired phy config */
+ memcpy(&dsi->phy_cfg, &new_cfg, sizeof(new_cfg));
+
+ memcpy(&dsi->mode, adjusted_mode, sizeof(dsi->mode));
+ drm_mode_debug_printmodeline(adjusted_mode);
+}
+
+static void nwl_dsi_bridge_pre_enable(struct drm_bridge *bridge)
+{
+ struct nwl_dsi *dsi = bridge_to_dsi(bridge);
+
+ pm_runtime_get_sync(dsi->dev);
+ nwl_dsi_plat_enable(dsi);
+ nwl_dsi_enable(dsi);
+}
+
+static int nwl_dsi_bridge_attach(struct drm_bridge *bridge)
+{
+ struct nwl_dsi *dsi = bridge->driver_private;
+
+ return drm_bridge_attach(bridge->encoder, dsi->panel_bridge, bridge);
+}
+
+static const struct drm_bridge_funcs nwl_dsi_bridge_funcs = {
+ .pre_enable = nwl_dsi_bridge_pre_enable,
+ .disable = nwl_dsi_bridge_disable,
+ .mode_fixup = nwl_dsi_bridge_mode_fixup,
+ .mode_set = nwl_dsi_bridge_mode_set,
+ .mode_valid = nwl_dsi_bridge_mode_valid,
+ .attach = nwl_dsi_bridge_attach,
+};
+
+static int nwl_dsi_parse_dt(struct nwl_dsi *dsi)
+{
+ struct platform_device *pdev = to_platform_device(dsi->dev);
+ struct clk *clk;
+ const char *clk_id;
+ void __iomem *base;
+ int i, ret;
+
+ dsi->phy = devm_phy_get(dsi->dev, "dphy");
+ if (IS_ERR(dsi->phy)) {
+ ret = PTR_ERR(dsi->phy);
+ if (ret != -EPROBE_DEFER)
+ DRM_DEV_ERROR(dsi->dev, "Could not get PHY: %d\n", ret);
+ return ret;
+ }
+
+ /* Platform dependent clocks */
+ memcpy(dsi->clk_config, dsi->pdata->clk_config,
+ sizeof(dsi->pdata->clk_config));
+
+ for (i = 0; i < ARRAY_SIZE(dsi->pdata->clk_config); i++) {
+ if (!dsi->clk_config[i].present)
+ continue;
+
+ clk_id = dsi->clk_config[i].id;
+ clk = devm_clk_get(dsi->dev, clk_id);
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ DRM_DEV_ERROR(dsi->dev, "Failed to get %s clock: %d\n",
+ clk_id, ret);
+ return ret;
+ }
+ DRM_DEV_DEBUG_DRIVER(dsi->dev, "Setup clk %s (rate: %lu)\n",
+ clk_id, clk_get_rate(clk));
+ dsi->clk_config[i].clk = clk;
+ }
+
+ /* DSI clocks */
+ clk = devm_clk_get(dsi->dev, "phy_ref");
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ DRM_DEV_ERROR(dsi->dev, "Failed to get phy_ref clock: %d\n",
+ ret);
+ return ret;
+ }
+ dsi->phy_ref_clk = clk;
+
+ clk = devm_clk_get(dsi->dev, "rx_esc");
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ DRM_DEV_ERROR(dsi->dev, "Failed to get rx_esc clock: %d\n",
+ ret);
+ return ret;
+ }
+ dsi->rx_esc_clk = clk;
+
+ clk = devm_clk_get(dsi->dev, "tx_esc");
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ DRM_DEV_ERROR(dsi->dev, "Failed to get tx_esc clock: %d\n",
+ ret);
+ return ret;
+ }
+ dsi->tx_esc_clk = clk;
+
+ dsi->mux = devm_mux_control_get(dsi->dev, NULL);
+ if (IS_ERR(dsi->mux)) {
+ ret = PTR_ERR(dsi->mux);
+ if (ret != -EPROBE_DEFER)
+ DRM_DEV_ERROR(dsi->dev, "Failed to get mux: %d\n", ret);
+ return ret;
+ }
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ dsi->regmap =
+ devm_regmap_init_mmio(dsi->dev, base, &nwl_dsi_regmap_config);
+ if (IS_ERR(dsi->regmap)) {
+ ret = PTR_ERR(dsi->regmap);
+ DRM_DEV_ERROR(dsi->dev, "Failed to create NWL DSI regmap: %d\n",
+ ret);
+ return ret;
+ }
+
+ dsi->irq = platform_get_irq(pdev, 0);
+ if (dsi->irq < 0) {
+ DRM_DEV_ERROR(dsi->dev, "Failed to get device IRQ: %d\n",
+ dsi->irq);
+ return dsi->irq;
+ }
+
+ dsi->rstc = devm_reset_control_array_get(dsi->dev, false, true);
+ if (IS_ERR(dsi->rstc)) {
+ DRM_DEV_ERROR(dsi->dev, "Failed to get resets: %ld\n",
+ PTR_ERR(dsi->rstc));
+ return PTR_ERR(dsi->rstc);
+ }
+
+ return 0;
+}
+
+static int imx8mq_dsi_select_input(struct nwl_dsi *dsi)
+{
+ struct device_node *remote;
+ u32 use_dcss = 1;
+ int ret;
+
+ remote = of_graph_get_remote_node(dsi->dev->of_node, 0, 0);
+ if (strcmp(remote->name, "lcdif") == 0)
+ use_dcss = 0;
+
+ DRM_DEV_INFO(dsi->dev, "Using %s as input source\n",
+ (use_dcss) ? "DCSS" : "LCDIF");
+
+ ret = mux_control_try_select(dsi->mux, use_dcss);
+ if (ret < 0)
+ DRM_DEV_ERROR(dsi->dev, "Failed to select input: %d\n", ret);
+
+ of_node_put(remote);
+ return ret;
+}
+
+
+static int imx8mq_dsi_deselect_input(struct nwl_dsi *dsi)
+{
+ int ret;
+
+ ret = mux_control_deselect(dsi->mux);
+ if (ret < 0)
+ DRM_DEV_ERROR(dsi->dev, "Failed to deselect input: %d\n", ret);
+
+ return ret;
+}
+
+
+static int imx8mq_dsi_poweron(struct nwl_dsi *dsi)
+{
+ int ret = 0;
+
+ /* otherwise the display stays blank */
+ usleep_range(200, 300);
+
+ if (dsi->rstc)
+ ret = reset_control_deassert(dsi->rstc);
+
+ return ret;
+}
+
+static int imx8mq_dsi_poweroff(struct nwl_dsi *dsi)
+{
+ int ret = 0;
+
+ if (dsi->quirks & SRC_RESET_QUIRK)
+ return 0;
+
+ if (dsi->rstc)
+ ret = reset_control_assert(dsi->rstc);
+ return ret;
+}
+
+static const struct drm_bridge_timings nwl_dsi_timings = {
+ .input_bus_flags = DRM_BUS_FLAG_DE_LOW,
+};
+
+static const struct nwl_dsi_platform_data imx8mq_dev = {
+ .poweron = &imx8mq_dsi_poweron,
+ .poweroff = &imx8mq_dsi_poweroff,
+ .select_input = &imx8mq_dsi_select_input,
+ .deselect_input = &imx8mq_dsi_deselect_input,
+ .clk_config = {
+ { .id = NWL_DSI_CLK_CORE, .present = true },
+ },
+};
+
+static const struct of_device_id nwl_dsi_dt_ids[] = {
+ { .compatible = "fsl,imx8mq-nwl-dsi", .data = &imx8mq_dev, },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, nwl_dsi_dt_ids);
+
+static const struct soc_device_attribute nwl_dsi_quirks_match[] = {
+ { .soc_id = "i.MX8MQ", .revision = "2.0",
+ .data = (void *)(E11418_HS_MODE_QUIRK | SRC_RESET_QUIRK) },
+ { /* sentinel. */ },
+};
+
+static int nwl_dsi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct of_device_id *of_id = of_match_device(nwl_dsi_dt_ids, dev);
+ const struct nwl_dsi_platform_data *pdata = of_id->data;
+ const struct soc_device_attribute *attr;
+ struct nwl_dsi *dsi;
+ int ret;
+
+ dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
+ if (!dsi)
+ return -ENOMEM;
+
+ dsi->dev = dev;
+ dsi->pdata = pdata;
+
+ ret = nwl_dsi_parse_dt(dsi);
+ if (ret)
+ return ret;
+
+ ret = devm_request_irq(dev, dsi->irq, nwl_dsi_irq_handler, 0,
+ dev_name(dev), dsi);
+ if (ret < 0) {
+ DRM_DEV_ERROR(dev, "Failed to request IRQ %d: %d\n", dsi->irq,
+ ret);
+ return ret;
+ }
+
+ dsi->dsi_host.ops = &nwl_dsi_host_ops;
+ dsi->dsi_host.dev = dev;
+ ret = mipi_dsi_host_register(&dsi->dsi_host);
+ if (ret) {
+ DRM_DEV_ERROR(dev, "Failed to register MIPI host: %d\n", ret);
+ return ret;
+ }
+
+ attr = soc_device_match(nwl_dsi_quirks_match);
+ if (attr)
+ dsi->quirks = (uintptr_t)attr->data;
+
+ dsi->bridge.driver_private = dsi;
+ dsi->bridge.funcs = &nwl_dsi_bridge_funcs;
+ dsi->bridge.of_node = dev->of_node;
+ dsi->bridge.timings = &nwl_dsi_timings;
+
+ drm_bridge_add(&dsi->bridge);
+
+ dev_set_drvdata(dev, dsi);
+ pm_runtime_enable(dev);
+ return 0;
+}
+
+static int nwl_dsi_remove(struct platform_device *pdev)
+{
+ struct nwl_dsi *dsi = platform_get_drvdata(pdev);
+
+ mipi_dsi_host_unregister(&dsi->dsi_host);
+ pm_runtime_disable(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver nwl_dsi_driver = {
+ .probe = nwl_dsi_probe,
+ .remove = nwl_dsi_remove,
+ .driver = {
+ .of_match_table = nwl_dsi_dt_ids,
+ .name = DRV_NAME,
+ },
+};
+
+module_platform_driver(nwl_dsi_driver);
+
+MODULE_AUTHOR("NXP Semiconductor");
+MODULE_AUTHOR("Purism SPC");
+MODULE_DESCRIPTION("Northwest Logic MIPI-DSI driver");
+MODULE_LICENSE("GPL"); /* GPLv2 or later */
diff --git a/drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.h b/drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.h
new file mode 100644
index 000000000000..1e72a9221401
--- /dev/null
+++ b/drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.h
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * NWL MIPI DSI host driver
+ *
+ * Copyright (C) 2017 NXP
+ * Copyright (C) 2019 Purism SPC
+ */
+
+#ifndef __NWL_DRV_H__
+#define __NWL_DRV_H__
+
+#include <linux/mux/consumer.h>
+#include <linux/phy/phy.h>
+
+#include <drm/drm_bridge.h>
+#include <drm/drm_mipi_dsi.h>
+
+struct nwl_dsi_platform_data;
+
+/* i.MX8 NWL quirks */
+/* i.MX8MQ errata E11418 */
+#define E11418_HS_MODE_QUIRK BIT(0)
+/* Skip DSI bits in SRC on disable to avoid blank display on enable */
+#define SRC_RESET_QUIRK BIT(1)
+
+#define NWL_DSI_MAX_PLATFORM_CLOCKS 1
+struct nwl_dsi_plat_clk_config {
+ const char *id;
+ struct clk *clk;
+ bool present;
+};
+
+struct nwl_dsi {
+ struct drm_bridge bridge;
+ struct mipi_dsi_host dsi_host;
+ struct drm_bridge *panel_bridge;
+ struct device *dev;
+ struct phy *phy;
+ union phy_configure_opts phy_cfg;
+ unsigned int quirks;
+
+ struct regmap *regmap;
+ int irq;
+ struct reset_control *rstc;
+ struct mux_control *mux;
+
+ /* DSI clocks */
+ struct clk *phy_ref_clk;
+ struct clk *rx_esc_clk;
+ struct clk *tx_esc_clk;
+ /* Platform dependent clocks */
+ struct nwl_dsi_plat_clk_config clk_config[NWL_DSI_MAX_PLATFORM_CLOCKS];
+
+ /* dsi lanes */
+ u32 lanes;
+ enum mipi_dsi_pixel_format format;
+ struct drm_display_mode mode;
+ unsigned long dsi_mode_flags;
+
+ struct nwl_dsi_transfer *xfer;
+
+ const struct nwl_dsi_platform_data *pdata;
+};
+
+#endif /* __NWL_DRV_H__ */
diff --git a/drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.c b/drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.c
new file mode 100644
index 000000000000..fd030af55bb4
--- /dev/null
+++ b/drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.c
@@ -0,0 +1,700 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * NWL MIPI DSI host driver
+ *
+ * Copyright (C) 2017 NXP
+ * Copyright (C) 2019 Purism SPC
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/irq.h>
+#include <linux/regmap.h>
+#include <linux/time64.h>
+
+#include <video/mipi_display.h>
+#include <video/videomode.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
+
+#include "nwl-drv.h"
+#include "nwl-dsi.h"
+
+#define NWL_DSI_MIPI_FIFO_TIMEOUT msecs_to_jiffies(500)
+
+/*
+ * PKT_CONTROL format:
+ * [15: 0] - word count
+ * [17:16] - virtual channel
+ * [23:18] - data type
+ * [24] - LP or HS select (0 - LP, 1 - HS)
+ * [25] - perform BTA after packet is sent
+ * [26] - perform BTA only, no packet tx
+ */
+#define NWL_DSI_WC(x) FIELD_PREP(GENMASK(15, 0), (x))
+#define NWL_DSI_TX_VC(x) FIELD_PREP(GENMASK(17, 16), (x))
+#define NWL_DSI_TX_DT(x) FIELD_PREP(GENMASK(23, 18), (x))
+#define NWL_DSI_HS_SEL(x) FIELD_PREP(GENMASK(24, 24), (x))
+#define NWL_DSI_BTA_TX(x) FIELD_PREP(GENMASK(25, 25), (x))
+#define NWL_DSI_BTA_NO_TX(x) FIELD_PREP(GENMASK(26, 26), (x))
+
+/*
+ * RX_PKT_HEADER format:
+ * [15: 0] - word count
+ * [21:16] - data type
+ * [23:22] - virtual channel
+ */
+#define NWL_DSI_RX_DT(x) FIELD_GET(GENMASK(21, 16), (x))
+#define NWL_DSI_RX_VC(x) FIELD_GET(GENMASK(23, 22), (x))
+
+/* DSI Video mode */
+#define NWL_DSI_VM_BURST_MODE_WITH_SYNC_PULSES 0
+#define NWL_DSI_VM_NON_BURST_MODE_WITH_SYNC_EVENTS BIT(0)
+#define NWL_DSI_VM_BURST_MODE BIT(1)
+
+/* * DPI color coding */
+#define NWL_DSI_DPI_16_BIT_565_PACKED 0
+#define NWL_DSI_DPI_16_BIT_565_ALIGNED 1
+#define NWL_DSI_DPI_16_BIT_565_SHIFTED 2
+#define NWL_DSI_DPI_18_BIT_PACKED 3
+#define NWL_DSI_DPI_18_BIT_ALIGNED 4
+#define NWL_DSI_DPI_24_BIT 5
+
+/* * DPI Pixel format */
+#define NWL_DSI_PIXEL_FORMAT_16 0
+#define NWL_DSI_PIXEL_FORMAT_18 BIT(0)
+#define NWL_DSI_PIXEL_FORMAT_18L BIT(1)
+#define NWL_DSI_PIXEL_FORMAT_24 (BIT(0) | BIT(1))
+
+enum transfer_direction {
+ DSI_PACKET_SEND,
+ DSI_PACKET_RECEIVE,
+};
+
+struct nwl_dsi_transfer {
+ const struct mipi_dsi_msg *msg;
+ struct mipi_dsi_packet packet;
+ struct completion completed;
+
+ int status; /* status of transmission */
+ enum transfer_direction direction;
+ bool need_bta;
+ u8 cmd;
+ u16 rx_word_count;
+ size_t tx_len; /* in bytes */
+ size_t rx_len; /* in bytes */
+};
+
+static int nwl_dsi_write(struct nwl_dsi *dsi, unsigned int reg, u32 val)
+{
+ int ret;
+
+ ret = regmap_write(dsi->regmap, reg, val);
+ if (ret < 0)
+ DRM_DEV_ERROR(dsi->dev,
+ "Failed to write NWL DSI reg 0x%x: %d\n", reg,
+ ret);
+ return ret;
+}
+
+static u32 nwl_dsi_read(struct nwl_dsi *dsi, u32 reg)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(dsi->regmap, reg, &val);
+ if (ret < 0)
+ DRM_DEV_ERROR(dsi->dev, "Failed to read NWL DSI reg 0x%x: %d\n",
+ reg, ret);
+
+ return val;
+}
+
+static u32 nwl_dsi_get_dpi_pixel_format(enum mipi_dsi_pixel_format format)
+{
+ switch (format) {
+ case MIPI_DSI_FMT_RGB565:
+ return NWL_DSI_PIXEL_FORMAT_16;
+ case MIPI_DSI_FMT_RGB666:
+ return NWL_DSI_PIXEL_FORMAT_18L;
+ case MIPI_DSI_FMT_RGB666_PACKED:
+ return NWL_DSI_PIXEL_FORMAT_18;
+ case MIPI_DSI_FMT_RGB888:
+ return NWL_DSI_PIXEL_FORMAT_24;
+ default:
+ return -EINVAL;
+ }
+}
+
+#define PSEC_PER_SEC 1000000000000LL
+/*
+ * ps2bc - Picoseconds to byte clock cycles
+ */
+static u32 ps2bc(struct nwl_dsi *dsi, unsigned long long ps)
+{
+ int bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
+
+ return DIV_ROUND_UP(ps * dsi->mode.clock * 1000 * bpp,
+ dsi->lanes * 8 * PSEC_PER_SEC);
+}
+
+/*
+ * ui2bc - UI time periods to byte clock cycles
+ */
+static u32 ui2bc(struct nwl_dsi *dsi, unsigned long long ui)
+{
+ int bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
+
+ return DIV_ROUND_UP(ui * dsi->lanes, dsi->mode.clock * 1000 * bpp);
+}
+
+/*
+ * us2bc - micro seconds to lp clock cycles
+ */
+static u32 us2lp(u32 lp_clk_rate, unsigned long us)
+{
+ return DIV_ROUND_UP(us * lp_clk_rate, USEC_PER_SEC);
+}
+
+static int nwl_dsi_config_host(struct nwl_dsi *dsi)
+{
+ u32 cycles;
+ struct phy_configure_opts_mipi_dphy *cfg = &dsi->phy_cfg.mipi_dphy;
+
+ if (dsi->lanes < 1 || dsi->lanes > 4)
+ return -EINVAL;
+
+ DRM_DEV_DEBUG_DRIVER(dsi->dev, "DSI Lanes %d\n", dsi->lanes);
+ nwl_dsi_write(dsi, NWL_DSI_CFG_NUM_LANES, dsi->lanes - 1);
+
+ if (dsi->dsi_mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) {
+ nwl_dsi_write(dsi, NWL_DSI_CFG_NONCONTINUOUS_CLK, 0x01);
+ nwl_dsi_write(dsi, NWL_DSI_CFG_AUTOINSERT_EOTP, 0x01);
+ } else {
+ nwl_dsi_write(dsi, NWL_DSI_CFG_NONCONTINUOUS_CLK, 0x00);
+ nwl_dsi_write(dsi, NWL_DSI_CFG_AUTOINSERT_EOTP, 0x00);
+ }
+
+ /* values in byte clock cycles */
+ cycles = ui2bc(dsi, cfg->clk_pre);
+ DRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_t_pre: 0x%x\n", cycles);
+ nwl_dsi_write(dsi, NWL_DSI_CFG_T_PRE, cycles);
+ cycles = ps2bc(dsi, cfg->lpx + cfg->clk_prepare + cfg->clk_zero);
+ DRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_tx_gap (pre): 0x%x\n", cycles);
+ cycles += ui2bc(dsi, cfg->clk_pre);
+ DRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_tx_gap: 0x%x\n", cycles);
+ nwl_dsi_write(dsi, NWL_DSI_CFG_T_POST, cycles);
+ cycles = ps2bc(dsi, cfg->hs_exit);
+ DRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_tx_gap: 0x%x\n", cycles);
+ nwl_dsi_write(dsi, NWL_DSI_CFG_TX_GAP, cycles);
+
+ nwl_dsi_write(dsi, NWL_DSI_CFG_EXTRA_CMDS_AFTER_EOTP, 0x01);
+ nwl_dsi_write(dsi, NWL_DSI_CFG_HTX_TO_COUNT, 0x00);
+ nwl_dsi_write(dsi, NWL_DSI_CFG_LRX_H_TO_COUNT, 0x00);
+ nwl_dsi_write(dsi, NWL_DSI_CFG_BTA_H_TO_COUNT, 0x00);
+ /* In LP clock cycles */
+ cycles = us2lp(cfg->lp_clk_rate, cfg->wakeup);
+ DRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_twakeup: 0x%x\n", cycles);
+ nwl_dsi_write(dsi, NWL_DSI_CFG_TWAKEUP, cycles);
+
+ return 0;
+}
+
+static int nwl_dsi_config_dpi(struct nwl_dsi *dsi)
+{
+ u32 color_format, mode;
+ bool burst_mode;
+ int hfront_porch, hback_porch, vfront_porch, vback_porch;
+ int hsync_len, vsync_len;
+
+ hfront_porch = dsi->mode.hsync_start - dsi->mode.hdisplay;
+ hsync_len = dsi->mode.hsync_end - dsi->mode.hsync_start;
+ hback_porch = dsi->mode.htotal - dsi->mode.hsync_end;
+
+ vfront_porch = dsi->mode.vsync_start - dsi->mode.vdisplay;
+ vsync_len = dsi->mode.vsync_end - dsi->mode.vsync_start;
+ vback_porch = dsi->mode.vtotal - dsi->mode.vsync_end;
+
+ DRM_DEV_DEBUG_DRIVER(dsi->dev, "hfront_porch = %d\n", hfront_porch);
+ DRM_DEV_DEBUG_DRIVER(dsi->dev, "hback_porch = %d\n", hback_porch);
+ DRM_DEV_DEBUG_DRIVER(dsi->dev, "hsync_len = %d\n", hsync_len);
+ DRM_DEV_DEBUG_DRIVER(dsi->dev, "hdisplay = %d\n", dsi->mode.hdisplay);
+ DRM_DEV_DEBUG_DRIVER(dsi->dev, "vfront_porch = %d\n", vfront_porch);
+ DRM_DEV_DEBUG_DRIVER(dsi->dev, "vback_porch = %d\n", vback_porch);
+ DRM_DEV_DEBUG_DRIVER(dsi->dev, "vsync_len = %d\n", vsync_len);
+ DRM_DEV_DEBUG_DRIVER(dsi->dev, "vactive = %d\n", dsi->mode.vdisplay);
+ DRM_DEV_DEBUG_DRIVER(dsi->dev, "clock = %d kHz\n", dsi->mode.clock);
+
+ color_format = nwl_dsi_get_dpi_pixel_format(dsi->format);
+ if (color_format < 0) {
+ DRM_DEV_ERROR(dsi->dev, "Invalid color format 0x%x\n",
+ dsi->format);
+ return color_format;
+ }
+ DRM_DEV_DEBUG_DRIVER(dsi->dev, "pixel fmt = %d\n", dsi->format);
+
+ nwl_dsi_write(dsi, NWL_DSI_INTERFACE_COLOR_CODING, NWL_DSI_DPI_24_BIT);
+ nwl_dsi_write(dsi, NWL_DSI_PIXEL_FORMAT, color_format);
+ /*
+ * Adjusting input polarity based on the video mode results in
+ * a black screen so always pick active low:
+ */
+ nwl_dsi_write(dsi, NWL_DSI_VSYNC_POLARITY,
+ NWL_DSI_VSYNC_POLARITY_ACTIVE_LOW);
+ nwl_dsi_write(dsi, NWL_DSI_HSYNC_POLARITY,
+ NWL_DSI_HSYNC_POLARITY_ACTIVE_LOW);
+
+ burst_mode = (dsi->dsi_mode_flags & MIPI_DSI_MODE_VIDEO_BURST) &&
+ !(dsi->dsi_mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE);
+
+ if (burst_mode) {
+ nwl_dsi_write(dsi, NWL_DSI_VIDEO_MODE, NWL_DSI_VM_BURST_MODE);
+ nwl_dsi_write(dsi, NWL_DSI_PIXEL_FIFO_SEND_LEVEL, 256);
+ } else {
+ mode = ((dsi->dsi_mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) ?
+ NWL_DSI_VM_BURST_MODE_WITH_SYNC_PULSES :
+ NWL_DSI_VM_NON_BURST_MODE_WITH_SYNC_EVENTS);
+ nwl_dsi_write(dsi, NWL_DSI_VIDEO_MODE, mode);
+ nwl_dsi_write(dsi, NWL_DSI_PIXEL_FIFO_SEND_LEVEL,
+ dsi->mode.hdisplay);
+ }
+
+ nwl_dsi_write(dsi, NWL_DSI_HFP, hfront_porch);
+ nwl_dsi_write(dsi, NWL_DSI_HBP, hback_porch);
+ nwl_dsi_write(dsi, NWL_DSI_HSA, hsync_len);
+
+ nwl_dsi_write(dsi, NWL_DSI_ENABLE_MULT_PKTS, 0x0);
+ nwl_dsi_write(dsi, NWL_DSI_BLLP_MODE, 0x1);
+ nwl_dsi_write(dsi, NWL_DSI_ENABLE_MULT_PKTS, 0x0);
+ nwl_dsi_write(dsi, NWL_DSI_USE_NULL_PKT_BLLP, 0x0);
+ nwl_dsi_write(dsi, NWL_DSI_VC, 0x0);
+
+ nwl_dsi_write(dsi, NWL_DSI_PIXEL_PAYLOAD_SIZE, dsi->mode.hdisplay);
+ nwl_dsi_write(dsi, NWL_DSI_VACTIVE, dsi->mode.vdisplay - 1);
+ nwl_dsi_write(dsi, NWL_DSI_VBP, vback_porch);
+ nwl_dsi_write(dsi, NWL_DSI_VFP, vfront_porch);
+
+ return 0;
+}
+
+static void nwl_dsi_init_interrupts(struct nwl_dsi *dsi)
+{
+ u32 irq_enable;
+
+ nwl_dsi_write(dsi, NWL_DSI_IRQ_MASK, 0xffffffff);
+ nwl_dsi_write(dsi, NWL_DSI_IRQ_MASK2, 0x7);
+
+ irq_enable = ~(u32)(NWL_DSI_TX_PKT_DONE_MASK |
+ NWL_DSI_RX_PKT_HDR_RCVD_MASK |
+ NWL_DSI_TX_FIFO_OVFLW_MASK |
+ NWL_DSI_HS_TX_TIMEOUT_MASK);
+
+ nwl_dsi_write(dsi, NWL_DSI_IRQ_MASK, irq_enable);
+}
+
+static int nwl_dsi_host_attach(struct mipi_dsi_host *dsi_host,
+ struct mipi_dsi_device *device)
+{
+ struct nwl_dsi *dsi = container_of(dsi_host, struct nwl_dsi, dsi_host);
+ struct device *dev = dsi->dev;
+ struct drm_bridge *bridge;
+ struct drm_panel *panel;
+ int ret;
+
+ DRM_DEV_INFO(dev, "lanes=%u, format=0x%x flags=0x%lx\n", device->lanes,
+ device->format, device->mode_flags);
+
+ if (device->lanes < 1 || device->lanes > 4)
+ return -EINVAL;
+
+ dsi->lanes = device->lanes;
+ dsi->format = device->format;
+ dsi->dsi_mode_flags = device->mode_flags;
+
+ ret = drm_of_find_panel_or_bridge(dsi->dev->of_node, 1, 0, &panel,
+ &bridge);
+ if (ret)
+ return ret;
+
+ if (panel) {
+ bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_DSI);
+ if (IS_ERR(bridge))
+ return PTR_ERR(bridge);
+ }
+
+ dsi->panel_bridge = bridge;
+ drm_bridge_add(&dsi->bridge);
+
+ return 0;
+}
+
+static int nwl_dsi_host_detach(struct mipi_dsi_host *dsi_host,
+ struct mipi_dsi_device *device)
+{
+ struct nwl_dsi *dsi = container_of(dsi_host, struct nwl_dsi, dsi_host);
+
+ drm_of_panel_bridge_remove(dsi->dev->of_node, 1, 0);
+ drm_bridge_remove(&dsi->bridge);
+
+ return 0;
+}
+
+static bool nwl_dsi_read_packet(struct nwl_dsi *dsi, u32 status)
+{
+ struct device *dev = dsi->dev;
+ struct nwl_dsi_transfer *xfer = dsi->xfer;
+ u8 *payload = xfer->msg->rx_buf;
+ u32 val;
+ u16 word_count;
+ u8 channel;
+ u8 data_type;
+
+ xfer->status = 0;
+
+ if (xfer->rx_word_count == 0) {
+ if (!(status & NWL_DSI_RX_PKT_HDR_RCVD))
+ return false;
+ /* Get the RX header and parse it */
+ val = nwl_dsi_read(dsi, NWL_DSI_RX_PKT_HEADER);
+ word_count = NWL_DSI_WC(val);
+ channel = NWL_DSI_RX_VC(val);
+ data_type = NWL_DSI_RX_DT(val);
+
+ if (channel != xfer->msg->channel) {
+ DRM_DEV_ERROR(dev,
+ "[%02X] Channel mismatch (%u != %u)\n",
+ xfer->cmd, channel, xfer->msg->channel);
+ return true;
+ }
+
+ switch (data_type) {
+ case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE:
+ /* Fall through */
+ case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE:
+ if (xfer->msg->rx_len > 1) {
+ /* read second byte */
+ payload[1] = word_count >> 8;
+ ++xfer->rx_len;
+ }
+ /* Fall through */
+ case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE:
+ /* Fall through */
+ case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE:
+ if (xfer->msg->rx_len > 0) {
+ /* read first byte */
+ payload[0] = word_count & 0xff;
+ ++xfer->rx_len;
+ }
+ xfer->status = xfer->rx_len;
+ return true;
+ case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT:
+ word_count &= 0xff;
+ DRM_DEV_ERROR(dev, "[%02X] DSI error report: 0x%02x\n",
+ xfer->cmd, word_count);
+ xfer->status = -EPROTO;
+ return true;
+ }
+
+ if (word_count > xfer->msg->rx_len) {
+ DRM_DEV_ERROR(
+ dev,
+ "[%02X] Receive buffer too small: %lu (< %u)\n",
+ xfer->cmd, xfer->msg->rx_len, word_count);
+ return true;
+ }
+
+ xfer->rx_word_count = word_count;
+ } else {
+ /* Set word_count from previous header read */
+ word_count = xfer->rx_word_count;
+ }
+
+ /* If RX payload is not yet received, wait for it */
+ if (!(status & NWL_DSI_RX_PKT_PAYLOAD_DATA_RCVD))
+ return false;
+
+ /* Read the RX payload */
+ while (word_count >= 4) {
+ val = nwl_dsi_read(dsi, NWL_DSI_RX_PAYLOAD);
+ payload[0] = (val >> 0) & 0xff;
+ payload[1] = (val >> 8) & 0xff;
+ payload[2] = (val >> 16) & 0xff;
+ payload[3] = (val >> 24) & 0xff;
+ payload += 4;
+ xfer->rx_len += 4;
+ word_count -= 4;
+ }
+
+ if (word_count > 0) {
+ val = nwl_dsi_read(dsi, NWL_DSI_RX_PAYLOAD);
+ switch (word_count) {
+ case 3:
+ payload[2] = (val >> 16) & 0xff;
+ ++xfer->rx_len;
+ /* Fall through */
+ case 2:
+ payload[1] = (val >> 8) & 0xff;
+ ++xfer->rx_len;
+ /* Fall through */
+ case 1:
+ payload[0] = (val >> 0) & 0xff;
+ ++xfer->rx_len;
+ break;
+ }
+ }
+
+ xfer->status = xfer->rx_len;
+
+ return true;
+}
+
+static void nwl_dsi_finish_transmission(struct nwl_dsi *dsi, u32 status)
+{
+ struct nwl_dsi_transfer *xfer = dsi->xfer;
+ bool end_packet = false;
+
+ if (!xfer)
+ return;
+
+ if (status & NWL_DSI_TX_FIFO_OVFLW) {
+ DRM_DEV_ERROR_RATELIMITED(dsi->dev, "tx fifo overflow\n");
+ return;
+ }
+
+ if (status & NWL_DSI_HS_TX_TIMEOUT) {
+ DRM_DEV_ERROR_RATELIMITED(dsi->dev, "HS tx timeout\n");
+ return;
+ }
+
+ if (xfer->direction == DSI_PACKET_SEND &&
+ status & NWL_DSI_TX_PKT_DONE) {
+ xfer->status = xfer->tx_len;
+ end_packet = true;
+ } else if (status & NWL_DSI_DPHY_DIRECTION &&
+ ((status & (NWL_DSI_RX_PKT_HDR_RCVD |
+ NWL_DSI_RX_PKT_PAYLOAD_DATA_RCVD)))) {
+ end_packet = nwl_dsi_read_packet(dsi, status);
+ }
+
+ if (end_packet)
+ complete(&xfer->completed);
+}
+
+static void nwl_dsi_begin_transmission(struct nwl_dsi *dsi)
+{
+ struct nwl_dsi_transfer *xfer = dsi->xfer;
+ struct mipi_dsi_packet *pkt = &xfer->packet;
+ const u8 *payload;
+ size_t length;
+ u16 word_count;
+ u8 hs_mode;
+ u32 val;
+ u32 hs_workaround = 0;
+
+ /* Send the payload, if any */
+ length = pkt->payload_length;
+ payload = pkt->payload;
+
+ while (length >= 4) {
+ val = *(u32 *)payload;
+ hs_workaround |= !(val & 0xFFFF00);
+ nwl_dsi_write(dsi, NWL_DSI_TX_PAYLOAD, val);
+ payload += 4;
+ length -= 4;
+ }
+ /* Send the rest of the payload */
+ val = 0;
+ switch (length) {
+ case 3:
+ val |= payload[2] << 16;
+ /* Fall through */
+ case 2:
+ val |= payload[1] << 8;
+ hs_workaround |= !(val & 0xFFFF00);
+ /* Fall through */
+ case 1:
+ val |= payload[0];
+ nwl_dsi_write(dsi, NWL_DSI_TX_PAYLOAD, val);
+ break;
+ }
+ xfer->tx_len = pkt->payload_length;
+
+ /*
+ * Send the header
+ * header[0] = Virtual Channel + Data Type
+ * header[1] = Word Count LSB (LP) or first param (SP)
+ * header[2] = Word Count MSB (LP) or second param (SP)
+ */
+ word_count = pkt->header[1] | (pkt->header[2] << 8);
+ if (hs_workaround && (dsi->quirks & E11418_HS_MODE_QUIRK)) {
+ DRM_DEV_DEBUG_DRIVER(dsi->dev,
+ "Using hs mode workaround for cmd 0x%x\n",
+ xfer->cmd);
+ hs_mode = 1;
+ } else {
+ hs_mode = (xfer->msg->flags & MIPI_DSI_MSG_USE_LPM) ? 0 : 1;
+ }
+ val = NWL_DSI_WC(word_count) | NWL_DSI_TX_VC(xfer->msg->channel) |
+ NWL_DSI_TX_DT(xfer->msg->type) | NWL_DSI_HS_SEL(hs_mode) |
+ NWL_DSI_BTA_TX(xfer->need_bta);
+ nwl_dsi_write(dsi, NWL_DSI_PKT_CONTROL, val);
+
+ /* Send packet command */
+ nwl_dsi_write(dsi, NWL_DSI_SEND_PACKET, 0x1);
+}
+
+static ssize_t nwl_dsi_host_transfer(struct mipi_dsi_host *dsi_host,
+ const struct mipi_dsi_msg *msg)
+{
+ struct nwl_dsi *dsi = container_of(dsi_host, struct nwl_dsi, dsi_host);
+ struct nwl_dsi_transfer xfer;
+ ssize_t ret = 0;
+
+ /* Create packet to be sent */
+ dsi->xfer = &xfer;
+ ret = mipi_dsi_create_packet(&xfer.packet, msg);
+ if (ret < 0) {
+ dsi->xfer = NULL;
+ return ret;
+ }
+
+ if ((msg->type & MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM ||
+ msg->type & MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM ||
+ msg->type & MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM ||
+ msg->type & MIPI_DSI_DCS_READ) &&
+ msg->rx_len > 0 && msg->rx_buf != NULL)
+ xfer.direction = DSI_PACKET_RECEIVE;
+ else
+ xfer.direction = DSI_PACKET_SEND;
+
+ xfer.need_bta = (xfer.direction == DSI_PACKET_RECEIVE);
+ xfer.need_bta |= (msg->flags & MIPI_DSI_MSG_REQ_ACK) ? 1 : 0;
+ xfer.msg = msg;
+ xfer.status = -ETIMEDOUT;
+ xfer.rx_word_count = 0;
+ xfer.rx_len = 0;
+ xfer.cmd = 0x00;
+ if (msg->tx_len > 0)
+ xfer.cmd = ((u8 *)(msg->tx_buf))[0];
+ init_completion(&xfer.completed);
+
+ ret = clk_prepare_enable(dsi->rx_esc_clk);
+ if (ret < 0) {
+ DRM_DEV_ERROR(dsi->dev, "Failed to enable rx_esc clk: %zd\n",
+ ret);
+ return ret;
+ }
+ DRM_DEV_DEBUG_DRIVER(dsi->dev, "Enabled rx_esc clk @%lu Hz\n",
+ clk_get_rate(dsi->rx_esc_clk));
+
+ /* Initiate the DSI packet transmision */
+ nwl_dsi_begin_transmission(dsi);
+
+ if (!wait_for_completion_timeout(&xfer.completed,
+ NWL_DSI_MIPI_FIFO_TIMEOUT)) {
+ DRM_DEV_ERROR(dsi_host->dev, "[%02X] DSI transfer timed out\n",
+ xfer.cmd);
+ ret = -ETIMEDOUT;
+ } else {
+ ret = xfer.status;
+ }
+
+ clk_disable_unprepare(dsi->rx_esc_clk);
+
+ return ret;
+}
+
+const struct mipi_dsi_host_ops nwl_dsi_host_ops = {
+ .attach = nwl_dsi_host_attach,
+ .detach = nwl_dsi_host_detach,
+ .transfer = nwl_dsi_host_transfer,
+};
+
+irqreturn_t nwl_dsi_irq_handler(int irq, void *data)
+{
+ u32 irq_status;
+ struct nwl_dsi *dsi = data;
+
+ irq_status = nwl_dsi_read(dsi, NWL_DSI_IRQ_STATUS);
+
+ if (irq_status & NWL_DSI_TX_PKT_DONE ||
+ irq_status & NWL_DSI_RX_PKT_HDR_RCVD ||
+ irq_status & NWL_DSI_RX_PKT_PAYLOAD_DATA_RCVD)
+ nwl_dsi_finish_transmission(dsi, irq_status);
+
+ return IRQ_HANDLED;
+}
+
+int nwl_dsi_enable(struct nwl_dsi *dsi)
+{
+ struct device *dev = dsi->dev;
+ union phy_configure_opts *phy_cfg = &dsi->phy_cfg;
+ int ret;
+
+ if (!dsi->lanes) {
+ DRM_DEV_ERROR(dev, "Need DSI lanes: %d\n", dsi->lanes);
+ return -EINVAL;
+ }
+
+ ret = phy_init(dsi->phy);
+ if (ret < 0) {
+ DRM_DEV_ERROR(dev, "Failed to init DSI phy: %d\n", ret);
+ return ret;
+ }
+
+ ret = phy_configure(dsi->phy, phy_cfg);
+ if (ret < 0) {
+ DRM_DEV_ERROR(dev, "Failed to configure DSI phy: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(dsi->tx_esc_clk);
+ if (ret < 0) {
+ DRM_DEV_ERROR(dsi->dev, "Failed to enable tx_esc clk: %d\n",
+ ret);
+ return ret;
+ }
+ DRM_DEV_DEBUG_DRIVER(dsi->dev, "Enabled tx_esc clk @%lu Hz\n",
+ clk_get_rate(dsi->tx_esc_clk));
+
+ ret = nwl_dsi_config_host(dsi);
+ if (ret < 0) {
+ DRM_DEV_ERROR(dev, "Failed to set up DSI: %d", ret);
+ return ret;
+ }
+
+ ret = nwl_dsi_config_dpi(dsi);
+ if (ret < 0) {
+ DRM_DEV_ERROR(dev, "Failed to set up DPI: %d", ret);
+ return ret;
+ }
+
+ ret = phy_power_on(dsi->phy);
+ if (ret < 0) {
+ DRM_DEV_ERROR(dev, "Failed to power on DPHY (%d)\n", ret);
+ return ret;
+ }
+
+ nwl_dsi_init_interrupts(dsi);
+
+ return 0;
+}
+
+int nwl_dsi_disable(struct nwl_dsi *dsi)
+{
+ struct device *dev = dsi->dev;
+
+ DRM_DEV_DEBUG_DRIVER(dev, "Disabling clocks and phy\n");
+
+ phy_power_off(dsi->phy);
+ phy_exit(dsi->phy);
+
+ /* Disabling the clock before the phy breaks enabling dsi again */
+ clk_disable_unprepare(dsi->tx_esc_clk);
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.h b/drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.h
new file mode 100644
index 000000000000..579b366de652
--- /dev/null
+++ b/drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.h
@@ -0,0 +1,112 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * NWL MIPI DSI host driver
+ *
+ * Copyright (C) 2017 NXP
+ * Copyright (C) 2019 Purism SPC
+ */
+#ifndef __NWL_DSI_H__
+#define __NWL_DSI_H__
+
+#include <linux/irqreturn.h>
+
+#include <drm/drm_mipi_dsi.h>
+
+#include "nwl-drv.h"
+
+/* DSI HOST registers */
+#define NWL_DSI_CFG_NUM_LANES 0x0
+#define NWL_DSI_CFG_NONCONTINUOUS_CLK 0x4
+#define NWL_DSI_CFG_T_PRE 0x8
+#define NWL_DSI_CFG_T_POST 0xc
+#define NWL_DSI_CFG_TX_GAP 0x10
+#define NWL_DSI_CFG_AUTOINSERT_EOTP 0x14
+#define NWL_DSI_CFG_EXTRA_CMDS_AFTER_EOTP 0x18
+#define NWL_DSI_CFG_HTX_TO_COUNT 0x1c
+#define NWL_DSI_CFG_LRX_H_TO_COUNT 0x20
+#define NWL_DSI_CFG_BTA_H_TO_COUNT 0x24
+#define NWL_DSI_CFG_TWAKEUP 0x28
+#define NWL_DSI_CFG_STATUS_OUT 0x2c
+#define NWL_DSI_RX_ERROR_STATUS 0x30
+
+/* DSI DPI registers */
+#define NWL_DSI_PIXEL_PAYLOAD_SIZE 0x200
+#define NWL_DSI_PIXEL_FIFO_SEND_LEVEL 0x204
+#define NWL_DSI_INTERFACE_COLOR_CODING 0x208
+#define NWL_DSI_PIXEL_FORMAT 0x20c
+#define NWL_DSI_VSYNC_POLARITY 0x210
+#define NWL_DSI_VSYNC_POLARITY_ACTIVE_LOW 0
+#define NWL_DSI_VSYNC_POLARITY_ACTIVE_HIGH BIT(1)
+
+#define NWL_DSI_HSYNC_POLARITY 0x214
+#define NWL_DSI_HSYNC_POLARITY_ACTIVE_LOW 0
+#define NWL_DSI_HSYNC_POLARITY_ACTIVE_HIGH BIT(1)
+
+#define NWL_DSI_VIDEO_MODE 0x218
+#define NWL_DSI_HFP 0x21c
+#define NWL_DSI_HBP 0x220
+#define NWL_DSI_HSA 0x224
+#define NWL_DSI_ENABLE_MULT_PKTS 0x228
+#define NWL_DSI_VBP 0x22c
+#define NWL_DSI_VFP 0x230
+#define NWL_DSI_BLLP_MODE 0x234
+#define NWL_DSI_USE_NULL_PKT_BLLP 0x238
+#define NWL_DSI_VACTIVE 0x23c
+#define NWL_DSI_VC 0x240
+
+/* DSI APB PKT control */
+#define NWL_DSI_TX_PAYLOAD 0x280
+#define NWL_DSI_PKT_CONTROL 0x284
+#define NWL_DSI_SEND_PACKET 0x288
+#define NWL_DSI_PKT_STATUS 0x28c
+#define NWL_DSI_PKT_FIFO_WR_LEVEL 0x290
+#define NWL_DSI_PKT_FIFO_RD_LEVEL 0x294
+#define NWL_DSI_RX_PAYLOAD 0x298
+#define NWL_DSI_RX_PKT_HEADER 0x29c
+
+/* DSI IRQ handling */
+#define NWL_DSI_IRQ_STATUS 0x2a0
+#define NWL_DSI_SM_NOT_IDLE BIT(0)
+#define NWL_DSI_TX_PKT_DONE BIT(1)
+#define NWL_DSI_DPHY_DIRECTION BIT(2)
+#define NWL_DSI_TX_FIFO_OVFLW BIT(3)
+#define NWL_DSI_TX_FIFO_UDFLW BIT(4)
+#define NWL_DSI_RX_FIFO_OVFLW BIT(5)
+#define NWL_DSI_RX_FIFO_UDFLW BIT(6)
+#define NWL_DSI_RX_PKT_HDR_RCVD BIT(7)
+#define NWL_DSI_RX_PKT_PAYLOAD_DATA_RCVD BIT(8)
+#define NWL_DSI_BTA_TIMEOUT BIT(29)
+#define NWL_DSI_LP_RX_TIMEOUT BIT(30)
+#define NWL_DSI_HS_TX_TIMEOUT BIT(31)
+
+#define NWL_DSI_IRQ_STATUS2 0x2a4
+#define NWL_DSI_SINGLE_BIT_ECC_ERR BIT(0)
+#define NWL_DSI_MULTI_BIT_ECC_ERR BIT(1)
+#define NWL_DSI_CRC_ERR BIT(2)
+
+#define NWL_DSI_IRQ_MASK 0x2a8
+#define NWL_DSI_SM_NOT_IDLE_MASK BIT(0)
+#define NWL_DSI_TX_PKT_DONE_MASK BIT(1)
+#define NWL_DSI_DPHY_DIRECTION_MASK BIT(2)
+#define NWL_DSI_TX_FIFO_OVFLW_MASK BIT(3)
+#define NWL_DSI_TX_FIFO_UDFLW_MASK BIT(4)
+#define NWL_DSI_RX_FIFO_OVFLW_MASK BIT(5)
+#define NWL_DSI_RX_FIFO_UDFLW_MASK BIT(6)
+#define NWL_DSI_RX_PKT_HDR_RCVD_MASK BIT(7)
+#define NWL_DSI_RX_PKT_PAYLOAD_DATA_RCVD_MASK BIT(8)
+#define NWL_DSI_BTA_TIMEOUT_MASK BIT(29)
+#define NWL_DSI_LP_RX_TIMEOUT_MASK BIT(30)
+#define NWL_DSI_HS_TX_TIMEOUT_MASK BIT(31)
+
+#define NWL_DSI_IRQ_MASK2 0x2ac
+#define NWL_DSI_SINGLE_BIT_ECC_ERR_MASK BIT(0)
+#define NWL_DSI_MULTI_BIT_ECC_ERR_MASK BIT(1)
+#define NWL_DSI_CRC_ERR_MASK BIT(2)
+
+extern const struct mipi_dsi_host_ops nwl_dsi_host_ops;
+
+irqreturn_t nwl_dsi_irq_handler(int irq, void *data);
+int nwl_dsi_enable(struct nwl_dsi *dsi);
+int nwl_dsi_disable(struct nwl_dsi *dsi);
+
+#endif /* __NWL_DSI_H__ */
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v3 0/2] drm: bridge: Add NWL MIPI DSI host controller support
From: Guido Günther @ 2019-08-22 10:44 UTC (permalink / raw)
To: David Airlie, Daniel Vetter, Rob Herring, Mark Rutland, Shawn Guo,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
NXP Linux Team, Andrzej Hajda, Neil Armstrong, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Lee Jones, Guido Günther,
dri-devel, devicetree, linux-arm-kernel, linux-kernel,
Robert Chiras, Sam Ravnborg, Arnd Bergmann
This adds initial support for the NWL MIPI DSI Host controller found on i.MX8
SoCs.
It adds support for the i.MX8MQ but the same IP core can also be found on e.g.
i.MX8QXP. I added the necessary hooks to support other imx8 variants but since
I only have imx8mq boards to test I omitted the platform data for other SoCs.
The code is based on NXPs BSP so I added Robert Chiras as
Co-authored-by. Robert, if this looks sane could you add your
Signed-off-by:?
The most notable changes over the BSP driver are
- Calculate HS mode timing from phy_configure_opts_mipi_dphy
- Perform all clock setup via DT
- Merge nwl-imx and nwl drivers
- Add B0 silion revision quirk
- become a bridge driver to hook into mxsfb (from what I read[0] DCSS, which
also can drive the nwl on the imx8mq will likely not become part of
imx-display-subsystem so it makes sense to make it drive a bridge for dsi as
well).
- Use panel_bridge to attach the panel
- Use multiplex framework instead of accessing syscon directly
This has been tested on a Librem 5 devkit using mxsfb with Robert's patches[1]
and the rocktech-jh057n00900 panel driver on next-20190821. The DCSS can later
on also act as input source too.
Changes from v2:
- Per review comments by Rob Herring
https://lists.freedesktop.org/archives/dri-devel/2019-August/230448.html
- bindings:
- Simplify by restricting to fsl,imx8mq-nwl-dsi
- document reset lines
- add port@{0,1}
- use a real compatible string for the panel
- resets are required
- Per review comments by Arnd Bergmann
https://lists.freedesktop.org/archives/dri-devel/2019-August/230868.html
- Don't access iomuxc_gpr regs directly. This allows us to drop the
first patch in the series with the iomuxc_gpr field defines.
- Per review comments by Laurent Pinchart
- Fix wording in bindings
- Add mux-controls to bindings
- Don't print error message on dphy probe deferal
Changes from v1:
- Per review comments by Sam Ravnborg
https://lists.freedesktop.org/archives/dri-devel/2019-July/228130.html
- Change binding docs to YAML
- build: Don't always visit imx-nwl/
- build: Add header-test-y
- Sort headers according to DRM convention
- Use drm_display_mode instead of videmode
- Per review comments by Fabio Estevam
https://lists.freedesktop.org/archives/dri-devel/2019-July/228299.html
- Don't restrict build to ARCH_MXC
- Drop unused includes
- Drop unreachable code in imx_nwl_dsi_bridge_mode_fixup()
- Drop remaining calls of dev_err() and use DRM_DEV_ERR()
consistently.
- Use devm_platform_ioremap_resource()
- Drop devm_free_irq() in probe() error path
- Use single line comments where sufficient
- Use <linux/time64.h> instead of defining USEC_PER_SEC
- Make input source select imx8 specific
- Drop <asm/unaligned.h> inclusion (after removal of get_unaligned_le32)
- Drop all EXPORT_SYMBOL_GPL() for functions used in the same module
but different source files.
- Drop nwl_dsi_enable_{rx,tx}_clock() by invoking clk_prepare_enable()
directly
- Remove pointless comment
- Laurent Pinchart
https://lists.freedesktop.org/archives/dri-devel/2019-July/228313.html
https://lists.freedesktop.org/archives/dri-devel/2019-July/228308.html
- Drop (on iMX8MQ) unused csr regmap
- Use NWL_MAX_PLATFORM_CLOCKS everywhere
- Drop get_unaligned_le32() usage
- remove duplicate 'for the' in binding docs
- Don't include unused <linux/clk-provider.h>
- Don't include unused <linux/component.h>
- Drop dpms_mode for tracking state, trust the drm layer on that
- Use pm_runtime_put() instead of pm_runtime_put_sync()
- Don't overwrite encoder type
- Make imx_nwl_platform_data const
- Use the reset controller API instead of open coding that platform specific
part
- Use <linux/bitfield.h> intead of making up our own defines
- name mipi_dsi_transfer less generic: nwl_dsi_transfer
- ensure clean in .remove by calling mipi_dsi_host_unregister.
- prefix constants by NWL_DSI_
- properly format transfer_direction enum
- simplify platform clock handling
- Don't modify state in mode_fixup() and use mode_set() instead
- Drop bridge detach(), already handle by nwl_dsi_host_detach()
- Drop USE_*_QUIRK() macros
- Drop (for now) unused clock defnitions. 'pixel' and 'bypass' clock will be
used for i.MX8 SoCs but since they're unused atm drop the definitions - but
keep the logic to enable/disable several clocks in place since we know we'll
need it in the future.
Changes from v0:
- Add quirk for IMQ8MQ silicon B0 revision to not mess with the
system reset controller on power down since enable() won't work
otherwise.
- Drop devm_free_irq() handled by the device driver core
- Disable tx esc clock after the phy power down to unbreak
disable/enable (unblank/blank)
- Add ports to dt binding docs
- Select GENERIC_PHY_MIPI_DPHY instead of GENERIC_PHY for
phy_mipi_dphy_get_default_config
- Select DRM_MIPI_DSI
- Include drm_print.h to fix build on next-20190408
- Drop some debugging messages
- Newline terminate all DRM_ printouts
- Turn component driver into a drm bridge
[0]: https://lists.freedesktop.org/archives/dri-devel/2019-May/219484.html
[1]: https://patchwork.freedesktop.org/series/62822/
To: David Airlie <airlied@linux.ie>, Daniel Vetter <daniel@ffwll.ch>, Rob Herring <robh+dt@kernel.org>, Mark Rutland <mark.rutland@arm.com>, Shawn Guo <shawnguo@kernel.org>, Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix Kernel Team <kernel@pengutronix.de>, Fabio Estevam <festevam@gmail.com>, NXP Linux Team <linux-imx@nxp.com>, Andrzej Hajda <a.hajda@samsung.com>, Neil Armstrong <narmstrong@baylibre.com>, Laurent Pinchart <Laurent.pinchart@ideasonboard.com>, Jonas Karlman <jonas@kwiboo.se>, Jernej Skrabec <jernej.skrabec@siol.net>, Lee Jones <lee.jones@linaro.org>, Guido Günther <agx@sigxcpu.org>, dri-devel@lists.freedesktop.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Robert Chiras <robert.chiras@nxp.com>, Sam Ravnborg <sam@ravnborg.org>, Fabio Estevam <festevam@gmail.com>, Arnd Bergmann <arnd@arndb.de>
Guido Günther (2):
dt-bindings: display/bridge: Add binding for NWL mipi dsi host
controller
drm/bridge: Add NWL MIPI DSI host controller support
.../bindings/display/bridge/nwl-dsi.yaml | 155 ++++
drivers/gpu/drm/bridge/Kconfig | 2 +
drivers/gpu/drm/bridge/Makefile | 1 +
drivers/gpu/drm/bridge/nwl-dsi/Kconfig | 16 +
drivers/gpu/drm/bridge/nwl-dsi/Makefile | 4 +
drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.c | 501 +++++++++++++
drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.h | 65 ++
drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.c | 700 ++++++++++++++++++
drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.h | 112 +++
9 files changed, 1556 insertions(+)
create mode 100644 Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml
create mode 100644 drivers/gpu/drm/bridge/nwl-dsi/Kconfig
create mode 100644 drivers/gpu/drm/bridge/nwl-dsi/Makefile
create mode 100644 drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.c
create mode 100644 drivers/gpu/drm/bridge/nwl-dsi/nwl-drv.h
create mode 100644 drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.c
create mode 100644 drivers/gpu/drm/bridge/nwl-dsi/nwl-dsi.h
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH v3 1/2] dt-bindings: display/bridge: Add binding for NWL mipi dsi host controller
From: Guido Günther @ 2019-08-22 10:44 UTC (permalink / raw)
To: David Airlie, Daniel Vetter, Rob Herring, Mark Rutland, Shawn Guo,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
NXP Linux Team, Andrzej Hajda, Neil Armstrong, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Lee Jones, Guido Günther,
dri-devel, devicetree, linux-arm-kernel, linux-kernel,
Robert Chiras, Sam Ravnborg, Arnd Bergmann
In-Reply-To: <cover.1566470526.git.agx@sigxcpu.org>
The Northwest Logic MIPI DSI IP core can be found in NXPs i.MX8 SoCs.
Signed-off-by: Guido Günther <agx@sigxcpu.org>
---
.../bindings/display/bridge/nwl-dsi.yaml | 155 ++++++++++++++++++
1 file changed, 155 insertions(+)
create mode 100644 Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml
diff --git a/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml b/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml
new file mode 100644
index 000000000000..24d17a6310dc
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml
@@ -0,0 +1,155 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/bridge/nwl-dsi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Northwest Logic MIPI-DSI controller on i.MX SoCs
+
+maintainers:
+ - Guido Gúnther <agx@sigxcpu.org>
+ - Robert Chiras <robert.chiras@nxp.com>
+
+description: |
+ NWL MIPI-DSI host controller found on i.MX8 platforms. This is a dsi bridge for
+ the SOCs NWL MIPI-DSI host controller.
+
+properties:
+ compatible:
+ const: fsl,imx8mq-nwl-dsi
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: DSI core clock
+ - description: RX_ESC clock (used in escape mode)
+ - description: TX_ESC clock (used in escape mode)
+ - description: PHY_REF clock
+
+ clock-names:
+ items:
+ - const: core
+ - const: rx_esc
+ - const: tx_esc
+ - const: phy_ref
+
+ mux-controls:
+ description:
+ mux controller node to use for operating the input mux
+
+ phys:
+ maxItems: 1
+ description:
+ A phandle to the phy module representing the DPHY
+
+ phy-names:
+ items:
+ - const: dphy
+
+ power-domains:
+ maxItems: 1
+ description:
+ A phandle to the power domain
+
+ resets:
+ description:
+ phandles to the reset controller
+ items:
+ - description: dsi byte reset line
+ - description: dsi dpi reset line
+ - description: dsi esc reset line
+ - description: dsi pclk reset line
+
+ reset-names:
+ items:
+ - const: byte
+ - const: dpi
+ - const: esc
+ - const: pclk
+
+ ports:
+ type: object
+ description:
+ A node containing DSI input & output port nodes with endpoint
+ definitions as documented in
+ Documentation/devicetree/bindings/graph.txt.
+
+ port@0:
+ type: object
+ description:
+ Input port node to receive pixel data from the
+ display controller
+
+ port@1:
+ type: object
+ description:
+ DSI output port node to the panel or the next bridge
+ in the chain
+
+patternProperties:
+ "^panel@[0-9]+$": true
+
+required:
+ - clock-names
+ - clocks
+ - compatible
+ - interrupts
+ - mux-controls
+ - phy-names
+ - phys
+ - ports
+ - reg
+ - reset-names
+ - resets
+
+examples:
+ - |
+
+ mipi_dsi: mipi_dsi@30a00000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,imx8mq-nwl-dsi";
+ reg = <0x30A00000 0x300>;
+ clocks = <&clk 163>, <&clk 244>, <&clk 245>, <&clk 164>;
+ clock-names = "core", "rx_esc", "tx_esc", "phy_ref";
+ interrupts = <0 34 4>;
+ mux-controls = <&mux 0>;
+ power-domains = <&pgc_mipi>;
+ resets = <&src 0>, <&src 1>, <&src 2>, <&src 3>;
+ reset-names = "byte", "dpi", "esc", "pclk";
+ phys = <&dphy>;
+ phy-names = "dphy";
+
+ panel@0 {
+ compatible = "rocktech,jh057n00900";
+ reg = <0>;
+ port@0 {
+ panel_in: endpoint {
+ remote-endpoint = <&mipi_dsi_out>;
+ };
+ };
+ };
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ mipi_dsi_in: endpoint {
+ remote-endpoint = <&lcdif_mipi_dsi>;
+ };
+ };
+ port@1 {
+ reg = <1>;
+ mipi_dsi_out: endpoint {
+ remote-endpoint = <&panel_in>;
+ };
+ };
+ };
+ };
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* Re: [PATCH v4] arm64: implement KPROBES_ON_FTRACE
From: Jisheng Zhang @ 2019-08-22 10:44 UTC (permalink / raw)
To: Naveen N. Rao
Cc: Mark Rutland, linux-doc@vger.kernel.org, Peter Zijlstra,
Catalin Marinas, Jonathan Corbet, linux-kernel@vger.kernel.org,
Steven Rostedt, Ingo Molnar, Masami Hiramatsu, Thomas Gleixner,
Will Deacon, linux-arm-kernel@lists.infradead.org
In-Reply-To: <1566468150.x8u1577wgh.naveen@linux.ibm.com>
On Thu, 22 Aug 2019 15:52:05 +0530
"Naveen N. Rao" <naveen.n.rao@linux.vnet.ibm.com> wrote:
>
>
> Jisheng Zhang wrote:
> > Hi,
> >
> > On Thu, 22 Aug 2019 12:23:58 +0530
> > "Naveen N. Rao" <naveen.n.rao@linux.vnet.ibm.com> wrote:
> >> Jisheng Zhang wrote:
> ...
> >> > +/* Ftrace callback handler for kprobes -- called under preepmt
> >> > disabed */
> >> > +void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
> >> > + struct ftrace_ops *ops, struct pt_regs *regs)
> >> > +{
> >> > + struct kprobe *p;
> >> > + struct kprobe_ctlblk *kcb;
> >> > +
> >> > + /* Preempt is disabled by ftrace */
> >> > + p = get_kprobe((kprobe_opcode_t *)ip);
> >> > + if (unlikely(!p) || kprobe_disabled(p))
> >> > + return;
> >> > +
> >> > + kcb = get_kprobe_ctlblk();
> >> > + if (kprobe_running()) {
> >> > + kprobes_inc_nmissed_count(p);
> >> > + } else {
> >> > + unsigned long orig_ip = instruction_pointer(regs);
> >> > + /* Kprobe handler expects regs->pc = pc + 4 as breakpoint hit */
> >> > + instruction_pointer_set(regs, ip + sizeof(kprobe_opcode_t));
> >>
> >> Just want to make sure that you've confirmed that this is what happens
> >> with a regular trap/brk based kprobe on ARM64. The reason for setting
> >> the instruction pointer here is to ensure that it is set to the same
> >> value as would be set if there was a trap/brk instruction at the ftrace
> >> location. This ensures that the kprobe pre handler sees the same value
> >> regardless.
> >
> > Due to the arm64's DYNAMIC_FTRACE_WITH_REGS implementation, the code itself
> > is correct. But this doesn't look like "there was a trap instruction at
> > the ftrace location".
> >
> > W/O KPROBE_ON_FTRACE:
> >
> > foo:
> > 00 insA
> > 04 insB
> > 08 insC
> >
> > kprobe's pre_handler() will see pc points to 00.
>
> In this case, the probe will be placed at foo+0x00, so pre_handler()
> seeing that address in pt_regs is correct behavior - as long as arm64
> 'brk' instruction causes an exception with the instruction pointer set
Yep, confirmed with regular trap/brk based kprobes, I do see PC set to
the "brk" instruction.
> *to* the 'brk' instruction. This is similar to how powerpc 'trap' works.
> However, x86 'int3' causes an exception *after* execution of the
> instruction.
Got it. I understand where's the comment "expects regs->pc = pc + 1" from.
>
> >
> > W/ KPROBE_ON_FTRACE:
> >
> > foo:
> > 00 lr saver
> > 04 nop // will be modified to ftrace call ins when KPROBE is armed
> > 08 insA
> > 0c insB
>
> In this case, if user asks for a probe to be placed at 'foo', we will
> choose foo+0x04 and from that point on, the behavior should reflect that
> a kprobe was placed at foo+0x04. In particular, the pre_handler() should
> see foo+0x04 in pt_regs. The post_handler() would then see foo+0x08.
>
> >
> > later, kprobe_ftrace_handler() will see pc points to 04, so pc + 4 will
> > point to 08 the same as the one w/o KPROBE_ON_FTRACE.
>
> I didn't mean to compare regular trap/brk based kprobes with
> KPROBES_ON_FTRACE. The only important aspect is that the handlers see
> consistent pt_regs in both cases, depending on where the kprobe was
> placed. Choosing a different address/offset to place a kprobe during its
> registration is an orthogonal aspect.
Indeed, previously, I want to let the PC point to the same instruction, it
seems I misunderstood the "consistent" meaning.
>
> >
> > It seems I need to fix the comment.
>
> Given your explanation above, I think you can simply drop the first
> adjustment to the instruction pointer before the pre handler invocation.
> The rest of the code looks fine.
>
>
Yep, thanks a lot. Will send out a new version soon.
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v3 05/10] KVM: arm64: Support stolen time reporting via shared structure
From: Jonathan Cameron @ 2019-08-22 10:39 UTC (permalink / raw)
To: Steven Price
Cc: Mark Rutland, kvm, Radim Krčmář, Marc Zyngier,
Suzuki K Pouloze, linux-doc, linux-kernel, Russell King,
James Morse, Julien Thierry, Catalin Marinas, Paolo Bonzini,
Will Deacon, kvmarm, linux-arm-kernel
In-Reply-To: <20190821153656.33429-6-steven.price@arm.com>
On Wed, 21 Aug 2019 16:36:51 +0100
Steven Price <steven.price@arm.com> wrote:
> Implement the service call for configuring a shared structure between a
> VCPU and the hypervisor in which the hypervisor can write the time
> stolen from the VCPU's execution time by other tasks on the host.
>
> The hypervisor allocates memory which is placed at an IPA chosen by user
> space. The hypervisor then updates the shared structure using
> kvm_put_guest() to ensure single copy atomicity of the 64-bit value
> reporting the stolen time in nanoseconds.
>
> Whenever stolen time is enabled by the guest, the stolen time counter is
> reset.
>
> The stolen time itself is retrieved from the sched_info structure
> maintained by the Linux scheduler code. We enable SCHEDSTATS when
> selecting KVM Kconfig to ensure this value is meaningful.
>
> Signed-off-by: Steven Price <steven.price@arm.com>
One totally trivial comment inline... Feel free to ignore :)
> ---
> arch/arm/include/asm/kvm_host.h | 20 +++++++++
> arch/arm64/include/asm/kvm_host.h | 25 +++++++++++-
> arch/arm64/kvm/Kconfig | 1 +
> include/linux/kvm_types.h | 2 +
> virt/kvm/arm/arm.c | 10 +++++
> virt/kvm/arm/hypercalls.c | 3 ++
> virt/kvm/arm/pvtime.c | 67 +++++++++++++++++++++++++++++++
> 7 files changed, 127 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h
> index 369b5d2d54bf..47d2ced99421 100644
> --- a/arch/arm/include/asm/kvm_host.h
> +++ b/arch/arm/include/asm/kvm_host.h
> @@ -39,6 +39,7 @@
> KVM_ARCH_REQ_FLAGS(0, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP)
> #define KVM_REQ_IRQ_PENDING KVM_ARCH_REQ(1)
> #define KVM_REQ_VCPU_RESET KVM_ARCH_REQ(2)
> +#define KVM_REQ_RECORD_STEAL KVM_ARCH_REQ(3)
>
> DECLARE_STATIC_KEY_FALSE(userspace_irqchip_in_use);
>
> @@ -329,6 +330,25 @@ static inline int kvm_hypercall_pv_features(struct kvm_vcpu *vcpu)
> return SMCCC_RET_NOT_SUPPORTED;
> }
>
> +static inline int kvm_hypercall_stolen_time(struct kvm_vcpu *vcpu)
> +{
> + return SMCCC_RET_NOT_SUPPORTED;
> +}
> +
> +static inline int kvm_update_stolen_time(struct kvm_vcpu *vcpu, bool init)
> +{
> + return -ENOTSUPP;
> +}
> +
> +static inline void kvm_pvtime_init_vm(struct kvm_arch *kvm_arch)
> +{
> +}
> +
> +static inline bool kvm_is_pvtime_enabled(struct kvm_arch *kvm_arch)
> +{
> + return false;
> +}
> +
> void kvm_mmu_wp_memory_region(struct kvm *kvm, int slot);
>
> struct kvm_vcpu *kvm_mpidr_to_vcpu(struct kvm *kvm, unsigned long mpidr);
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index 583b3639062a..b6fa7beffd8a 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -44,6 +44,7 @@
> KVM_ARCH_REQ_FLAGS(0, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP)
> #define KVM_REQ_IRQ_PENDING KVM_ARCH_REQ(1)
> #define KVM_REQ_VCPU_RESET KVM_ARCH_REQ(2)
> +#define KVM_REQ_RECORD_STEAL KVM_ARCH_REQ(3)
>
> DECLARE_STATIC_KEY_FALSE(userspace_irqchip_in_use);
>
> @@ -83,6 +84,11 @@ struct kvm_arch {
>
> /* Mandated version of PSCI */
> u32 psci_version;
> +
> + struct kvm_arch_pvtime {
> + gpa_t st_base;
> + u64 st_size;
> + } pvtime;
> };
>
> #define KVM_NR_MEM_OBJS 40
> @@ -338,8 +344,13 @@ struct kvm_vcpu_arch {
> /* True when deferrable sysregs are loaded on the physical CPU,
> * see kvm_vcpu_load_sysregs and kvm_vcpu_put_sysregs. */
> bool sysregs_loaded_on_cpu;
> -};
>
> + /* Guest PV state */
> + struct {
> + u64 steal;
> + u64 last_steal;
> + } steal;
> +};
> /* Pointer to the vcpu's SVE FFR for sve_{save,load}_state() */
> #define vcpu_sve_pffr(vcpu) ((void *)((char *)((vcpu)->arch.sve_state) + \
> sve_ffr_offset((vcpu)->arch.sve_max_vl)))
> @@ -479,6 +490,18 @@ int kvm_perf_init(void);
> int kvm_perf_teardown(void);
>
> int kvm_hypercall_pv_features(struct kvm_vcpu *vcpu);
> +int kvm_hypercall_stolen_time(struct kvm_vcpu *vcpu);
> +int kvm_update_stolen_time(struct kvm_vcpu *vcpu, bool init);
> +
> +static inline void kvm_pvtime_init_vm(struct kvm_arch *kvm_arch)
> +{
> + kvm_arch->pvtime.st_base = GPA_INVALID;
> +}
> +
> +static inline bool kvm_is_pvtime_enabled(struct kvm_arch *kvm_arch)
> +{
> + return (kvm_arch->pvtime.st_base != GPA_INVALID);
> +}
>
> void kvm_set_sei_esr(struct kvm_vcpu *vcpu, u64 syndrome);
>
> diff --git a/arch/arm64/kvm/Kconfig b/arch/arm64/kvm/Kconfig
> index a67121d419a2..d8b88e40d223 100644
> --- a/arch/arm64/kvm/Kconfig
> +++ b/arch/arm64/kvm/Kconfig
> @@ -39,6 +39,7 @@ config KVM
> select IRQ_BYPASS_MANAGER
> select HAVE_KVM_IRQ_BYPASS
> select HAVE_KVM_VCPU_RUN_PID_CHANGE
> + select SCHEDSTATS
> ---help---
> Support hosting virtualized guest machines.
> We don't support KVM with 16K page tables yet, due to the multiple
> diff --git a/include/linux/kvm_types.h b/include/linux/kvm_types.h
> index bde5374ae021..1c88e69db3d9 100644
> --- a/include/linux/kvm_types.h
> +++ b/include/linux/kvm_types.h
> @@ -35,6 +35,8 @@ typedef unsigned long gva_t;
> typedef u64 gpa_t;
> typedef u64 gfn_t;
>
> +#define GPA_INVALID (~(gpa_t)0)
> +
> typedef unsigned long hva_t;
> typedef u64 hpa_t;
> typedef u64 hfn_t;
> diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c
> index 35a069815baf..5e8343e2dd62 100644
> --- a/virt/kvm/arm/arm.c
> +++ b/virt/kvm/arm/arm.c
> @@ -40,6 +40,10 @@
> #include <asm/kvm_coproc.h>
> #include <asm/sections.h>
>
> +#include <kvm/arm_hypercalls.h>
> +#include <kvm/arm_pmu.h>
> +#include <kvm/arm_psci.h>
> +
> #ifdef REQUIRES_VIRT
> __asm__(".arch_extension virt");
> #endif
> @@ -135,6 +139,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
> kvm->arch.max_vcpus = vgic_present ?
> kvm_vgic_get_max_vcpus() : KVM_MAX_VCPUS;
>
> + kvm_pvtime_init_vm(&kvm->arch);
> return ret;
> out_free_stage2_pgd:
> kvm_free_stage2_pgd(kvm);
> @@ -379,6 +384,8 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
> kvm_vcpu_load_sysregs(vcpu);
> kvm_arch_vcpu_load_fp(vcpu);
> kvm_vcpu_pmu_restore_guest(vcpu);
> + if (kvm_is_pvtime_enabled(&vcpu->kvm->arch))
> + kvm_make_request(KVM_REQ_RECORD_STEAL, vcpu);
>
> if (single_task_running())
> vcpu_clear_wfe_traps(vcpu);
> @@ -644,6 +651,9 @@ static void check_vcpu_requests(struct kvm_vcpu *vcpu)
> * that a VCPU sees new virtual interrupts.
> */
> kvm_check_request(KVM_REQ_IRQ_PENDING, vcpu);
> +
> + if (kvm_check_request(KVM_REQ_RECORD_STEAL, vcpu))
> + kvm_update_stolen_time(vcpu, false);
> }
> }
>
> diff --git a/virt/kvm/arm/hypercalls.c b/virt/kvm/arm/hypercalls.c
> index 63ae629c466a..ac678eabf15f 100644
> --- a/virt/kvm/arm/hypercalls.c
> +++ b/virt/kvm/arm/hypercalls.c
> @@ -56,6 +56,9 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
> case ARM_SMCCC_HV_PV_FEATURES:
> val = kvm_hypercall_pv_features(vcpu);
> break;
> + case ARM_SMCCC_HV_PV_TIME_ST:
> + val = kvm_hypercall_stolen_time(vcpu);
> + break;
> default:
> return kvm_psci_call(vcpu);
> }
> diff --git a/virt/kvm/arm/pvtime.c b/virt/kvm/arm/pvtime.c
> index 6201d71cb1f8..28603689f6e0 100644
> --- a/virt/kvm/arm/pvtime.c
> +++ b/virt/kvm/arm/pvtime.c
> @@ -3,8 +3,51 @@
>
> #include <linux/arm-smccc.h>
>
> +#include <asm/pvclock-abi.h>
> +
> #include <kvm/arm_hypercalls.h>
>
> +int kvm_update_stolen_time(struct kvm_vcpu *vcpu, bool init)
> +{
> + struct kvm *kvm = vcpu->kvm;
> + struct kvm_arch_pvtime *pvtime = &kvm->arch.pvtime;
> + u64 steal;
> + u64 steal_le;
> + u64 offset;
> + int idx;
> + const int stride = sizeof(struct pvclock_vcpu_stolen_time);
> +
> + if (pvtime->st_base == GPA_INVALID)
> + return -ENOTSUPP;
> +
> + /* Let's do the local bookkeeping */
> + steal = vcpu->arch.steal.steal;
> + steal += current->sched_info.run_delay - vcpu->arch.steal.last_steal;
> + vcpu->arch.steal.last_steal = current->sched_info.run_delay;
> + vcpu->arch.steal.steal = steal;
> +
> + offset = stride * kvm_vcpu_get_idx(vcpu);
> +
> + if (unlikely(offset + stride > pvtime->st_size))
> + return -EINVAL;
> +
> + steal_le = cpu_to_le64(steal);
> + idx = srcu_read_lock(&kvm->srcu);
> + if (init) {
> + struct pvclock_vcpu_stolen_time init_values = {
> + .revision = 0,
> + .attributes = 0
> + };
> + kvm_write_guest(kvm, pvtime->st_base + offset, &init_values,
> + sizeof(init_values));
> + }
> + offset += offsetof(struct pvclock_vcpu_stolen_time, stolen_time);
> + kvm_put_guest(kvm, pvtime->st_base + offset, steal_le, u64);
> + srcu_read_unlock(&kvm->srcu, idx);
> +
> + return 0;
> +}
> +
> int kvm_hypercall_pv_features(struct kvm_vcpu *vcpu)
> {
> u32 feature = smccc_get_arg1(vcpu);
> @@ -12,6 +55,7 @@ int kvm_hypercall_pv_features(struct kvm_vcpu *vcpu)
>
> switch (feature) {
> case ARM_SMCCC_HV_PV_FEATURES:
> + case ARM_SMCCC_HV_PV_TIME_ST:
> val = SMCCC_RET_SUCCESS;
> break;
> }
> @@ -19,3 +63,26 @@ int kvm_hypercall_pv_features(struct kvm_vcpu *vcpu)
> return val;
> }
>
> +int kvm_hypercall_stolen_time(struct kvm_vcpu *vcpu)
> +{
> + u64 ret;
> + int err;
> +
> + /*
> + * Start counting stolen time from the time the guest requests
> + * the feature enabled.
> + */
> + vcpu->arch.steal.steal = 0;
> + vcpu->arch.steal.last_steal = current->sched_info.run_delay;
> +
> + err = kvm_update_stolen_time(vcpu, true);
> +
> + if (err)
> + ret = SMCCC_RET_NOT_SUPPORTED;
Trivial by why not
return SMCCC_RET_NOT_SUPPORTED;
return vcpu->kvm->arch.pvtime.st_base +
...
Drops the indentation a bit and puts the error handling out of
line which is slightly nicer to read (to my eyes).
> + else
> + ret = vcpu->kvm->arch.pvtime.st_base +
> + (sizeof(struct pvclock_vcpu_stolen_time) *
> + kvm_vcpu_get_idx(vcpu));
> +
> + return ret;
> +}
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v3 04/10] KVM: Implement kvm_put_guest()
From: Steven Price @ 2019-08-22 10:37 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Mark Rutland, Radim Krčmář, kvm, Suzuki K Pouloze,
Marc Zyngier, linux-doc, linux-kernel, Russell King, James Morse,
linux-arm-kernel, Catalin Marinas, Paolo Bonzini, Will Deacon,
kvmarm, Julien Thierry
In-Reply-To: <20190822112930.000052db@huawei.com>
On 22/08/2019 11:29, Jonathan Cameron wrote:
> On Wed, 21 Aug 2019 16:36:50 +0100
> Steven Price <steven.price@arm.com> wrote:
>
>> kvm_put_guest() is analogous to put_user() - it writes a single value to
>> the guest physical address. The implementation is built upon put_user()
>> and so it has the same single copy atomic properties.
>>
>> Signed-off-by: Steven Price <steven.price@arm.com>
>> ---
>> include/linux/kvm_host.h | 24 ++++++++++++++++++++++++
>> 1 file changed, 24 insertions(+)
>>
>> diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
>> index fcb46b3374c6..e154a1897e20 100644
>> --- a/include/linux/kvm_host.h
>> +++ b/include/linux/kvm_host.h
>> @@ -746,6 +746,30 @@ int kvm_write_guest_offset_cached(struct kvm *kvm, struct gfn_to_hva_cache *ghc,
>> unsigned long len);
>> int kvm_gfn_to_hva_cache_init(struct kvm *kvm, struct gfn_to_hva_cache *ghc,
>> gpa_t gpa, unsigned long len);
>> +
>> +#define __kvm_put_guest(kvm, gfn, offset, value, type) \
>> +({ \
>> + unsigned long __addr = gfn_to_hva(kvm, gfn); \
>> + type __user *__uaddr = (type __user *)(__addr + offset); \
>> + int __ret = 0; \
>
> Why initialize __ret?
Good question. Actually looking at this again if I reorder this to be
pessimistic I can make it shorter:
int __ret = -EFAULT;
if (!kvm_is_error_hva(__addr))
__ret = put_user(value, __uaddr);
if (!__ret)
mark_page_dirty(kvm, gfn);
__ret;
Thanks for taking a look.
Steve
>> + \
>> + if (kvm_is_error_hva(__addr)) \
>> + __ret = -EFAULT; \
>> + else \
>> + __ret = put_user(value, __uaddr); \
>> + if (!__ret) \
>> + mark_page_dirty(kvm, gfn); \
>> + __ret; \
>> +})
>> +
>> +#define kvm_put_guest(kvm, gpa, value, type) \
>> +({ \
>> + gpa_t __gpa = gpa; \
>> + struct kvm *__kvm = kvm; \
>> + __kvm_put_guest(__kvm, __gpa >> PAGE_SHIFT, \
>> + offset_in_page(__gpa), (value), type); \
>> +})
>> +
>> int kvm_clear_guest_page(struct kvm *kvm, gfn_t gfn, int offset, int len);
>> int kvm_clear_guest(struct kvm *kvm, gpa_t gpa, unsigned long len);
>> struct kvm_memory_slot *gfn_to_memslot(struct kvm *kvm, gfn_t gfn);
>
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v2 00/20] Initial support for Marvell MMP3 SoC
From: Marc Zyngier @ 2019-08-22 10:31 UTC (permalink / raw)
To: Lubomir Rintel, Olof Johansson
Cc: Mark Rutland, devicetree, Jason Cooper, Stephen Boyd,
linux-kernel, Michael Turquette, Russell King,
Kishon Vijay Abraham I, Rob Herring, Thomas Gleixner, linux-clk,
linux-arm-kernel
In-Reply-To: <20190822092643.593488-1-lkundrak@v3.sk>
On 22/08/2019 10:26, Lubomir Rintel wrote:
> Hi,
>
> this is a second spin of a patch set that adds support for the Marvell
> MMP3 processor. MMP3 is used in OLPC XO-4 laptops, Panasonic Toughpad
> FZ-A1 tablet and Dell Wyse 3020 Tx0D thin clients.
>
> Compared to v1, there's a handful of fixes in response to reviews. Patch
> 02/20 is new. Details in individual patches.
>
> Apart from the adjustments in mach-mmp/, the patch makes necessary
> changes to the irqchip driver and adds an USB2 PHY driver. The latter
> has a dependency on the mach-mmp/ changes, so it can't be submitted
> separately.
>
> The patch set has been tested to work on Wyse Tx0D and not ruin MMP2
> support on XO-1.75.
How do you want this series to be merged? I'm happy to take the irqchip
related patches as well as the corresponding DT change (once reviewed)
through my tree.
Thanks,
M.
--
Jazz is not dead, it just smells funny...
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v3 04/10] KVM: Implement kvm_put_guest()
From: Jonathan Cameron @ 2019-08-22 10:29 UTC (permalink / raw)
To: Steven Price
Cc: Mark Rutland, kvm, Radim Krčmář, Marc Zyngier,
Suzuki K Pouloze, linux-doc, linux-kernel, Russell King,
James Morse, Julien Thierry, Catalin Marinas, Paolo Bonzini,
Will Deacon, kvmarm, linux-arm-kernel
In-Reply-To: <20190821153656.33429-5-steven.price@arm.com>
On Wed, 21 Aug 2019 16:36:50 +0100
Steven Price <steven.price@arm.com> wrote:
> kvm_put_guest() is analogous to put_user() - it writes a single value to
> the guest physical address. The implementation is built upon put_user()
> and so it has the same single copy atomic properties.
>
> Signed-off-by: Steven Price <steven.price@arm.com>
> ---
> include/linux/kvm_host.h | 24 ++++++++++++++++++++++++
> 1 file changed, 24 insertions(+)
>
> diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
> index fcb46b3374c6..e154a1897e20 100644
> --- a/include/linux/kvm_host.h
> +++ b/include/linux/kvm_host.h
> @@ -746,6 +746,30 @@ int kvm_write_guest_offset_cached(struct kvm *kvm, struct gfn_to_hva_cache *ghc,
> unsigned long len);
> int kvm_gfn_to_hva_cache_init(struct kvm *kvm, struct gfn_to_hva_cache *ghc,
> gpa_t gpa, unsigned long len);
> +
> +#define __kvm_put_guest(kvm, gfn, offset, value, type) \
> +({ \
> + unsigned long __addr = gfn_to_hva(kvm, gfn); \
> + type __user *__uaddr = (type __user *)(__addr + offset); \
> + int __ret = 0; \
Why initialize __ret?
> + \
> + if (kvm_is_error_hva(__addr)) \
> + __ret = -EFAULT; \
> + else \
> + __ret = put_user(value, __uaddr); \
> + if (!__ret) \
> + mark_page_dirty(kvm, gfn); \
> + __ret; \
> +})
> +
> +#define kvm_put_guest(kvm, gpa, value, type) \
> +({ \
> + gpa_t __gpa = gpa; \
> + struct kvm *__kvm = kvm; \
> + __kvm_put_guest(__kvm, __gpa >> PAGE_SHIFT, \
> + offset_in_page(__gpa), (value), type); \
> +})
> +
> int kvm_clear_guest_page(struct kvm *kvm, gfn_t gfn, int offset, int len);
> int kvm_clear_guest(struct kvm *kvm, gpa_t gpa, unsigned long len);
> struct kvm_memory_slot *gfn_to_memslot(struct kvm *kvm, gfn_t gfn);
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v4] arm64: implement KPROBES_ON_FTRACE
From: Naveen N. Rao @ 2019-08-22 10:22 UTC (permalink / raw)
To: Jisheng Zhang
Cc: Mark Rutland, linux-doc@vger.kernel.org, Peter Zijlstra,
Catalin Marinas, Jonathan Corbet, linux-kernel@vger.kernel.org,
Steven Rostedt, Ingo Molnar, Masami Hiramatsu, Thomas Gleixner,
Will Deacon, linux-arm-kernel@lists.infradead.org
In-Reply-To: <20190822173558.63de3fc4@xhacker.debian>
Jisheng Zhang wrote:
> Hi,
>
> On Thu, 22 Aug 2019 12:23:58 +0530
> "Naveen N. Rao" <naveen.n.rao@linux.vnet.ibm.com> wrote:
>> Jisheng Zhang wrote:
...
>> > +/* Ftrace callback handler for kprobes -- called under preepmt
>> > disabed */
>> > +void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
>> > + struct ftrace_ops *ops, struct pt_regs *regs)
>> > +{
>> > + struct kprobe *p;
>> > + struct kprobe_ctlblk *kcb;
>> > +
>> > + /* Preempt is disabled by ftrace */
>> > + p = get_kprobe((kprobe_opcode_t *)ip);
>> > + if (unlikely(!p) || kprobe_disabled(p))
>> > + return;
>> > +
>> > + kcb = get_kprobe_ctlblk();
>> > + if (kprobe_running()) {
>> > + kprobes_inc_nmissed_count(p);
>> > + } else {
>> > + unsigned long orig_ip = instruction_pointer(regs);
>> > + /* Kprobe handler expects regs->pc = pc + 4 as breakpoint hit */
>> > + instruction_pointer_set(regs, ip + sizeof(kprobe_opcode_t));
>>
>> Just want to make sure that you've confirmed that this is what happens
>> with a regular trap/brk based kprobe on ARM64. The reason for setting
>> the instruction pointer here is to ensure that it is set to the same
>> value as would be set if there was a trap/brk instruction at the ftrace
>> location. This ensures that the kprobe pre handler sees the same value
>> regardless.
>
> Due to the arm64's DYNAMIC_FTRACE_WITH_REGS implementation, the code itself
> is correct. But this doesn't look like "there was a trap instruction at
> the ftrace location".
>
> W/O KPROBE_ON_FTRACE:
>
> foo:
> 00 insA
> 04 insB
> 08 insC
>
> kprobe's pre_handler() will see pc points to 00.
In this case, the probe will be placed at foo+0x00, so pre_handler()
seeing that address in pt_regs is correct behavior - as long as arm64
'brk' instruction causes an exception with the instruction pointer set
*to* the 'brk' instruction. This is similar to how powerpc 'trap' works.
However, x86 'int3' causes an exception *after* execution of the
instruction.
>
> W/ KPROBE_ON_FTRACE:
>
> foo:
> 00 lr saver
> 04 nop // will be modified to ftrace call ins when KPROBE is armed
> 08 insA
> 0c insB
In this case, if user asks for a probe to be placed at 'foo', we will
choose foo+0x04 and from that point on, the behavior should reflect that
a kprobe was placed at foo+0x04. In particular, the pre_handler() should
see foo+0x04 in pt_regs. The post_handler() would then see foo+0x08.
>
> later, kprobe_ftrace_handler() will see pc points to 04, so pc + 4 will
> point to 08 the same as the one w/o KPROBE_ON_FTRACE.
I didn't mean to compare regular trap/brk based kprobes with
KPROBES_ON_FTRACE. The only important aspect is that the handlers see
consistent pt_regs in both cases, depending on where the kprobe was
placed. Choosing a different address/offset to place a kprobe during its
registration is an orthogonal aspect.
>
> It seems I need to fix the comment.
Given your explanation above, I think you can simply drop the first
adjustment to the instruction pointer before the pre handler invocation.
The rest of the code looks fine.
- Naveen
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v10 09/23] iommu/io-pgtable-arm-v7s: Extend to support PA[33:32] for MediaTek
From: Will Deacon @ 2019-08-22 10:17 UTC (permalink / raw)
To: Robin Murphy
Cc: youlin.pei, devicetree, Nicolas Boichat, cui.zhang,
srv_heupstream, Tomasz Figa, Joerg Roedel, linux-kernel,
Evan Green, chao.hao, iommu, Rob Herring, linux-mediatek,
linux-arm-kernel, Matthias Brugger, ming-fan.chen, anan.sun,
Matthias Kaehlcke, Yong Wu
In-Reply-To: <10d5122d-3375-161b-9356-2ddfc1c835bd@arm.com>
On Thu, Aug 22, 2019 at 11:08:58AM +0100, Robin Murphy wrote:
> On 2019-08-22 9:56 am, Yong Wu wrote:
> > On Wed, 2019-08-21 at 16:24 +0100, Will Deacon wrote:
> > > On Wed, Aug 21, 2019 at 09:53:12PM +0800, Yong Wu wrote:
> > > > MediaTek extend the arm v7s descriptor to support up to 34 bits PA where
> > > > the bit32 and bit33 are encoded in the bit9 and bit4 of the PTE
> > > > respectively. Meanwhile the iova still is 32bits.
> > > >
> > > > Regarding whether the pagetable address could be over 4GB, the mt8183
> > > > support it while the previous mt8173 don't, thus keep it as is.
> > > >
> > > > Signed-off-by: Yong Wu <yong.wu@mediatek.com>
> > > > ---
> > > > drivers/iommu/io-pgtable-arm-v7s.c | 32 +++++++++++++++++++++++++-------
> > > > include/linux/io-pgtable.h | 7 +++----
> > > > 2 files changed, 28 insertions(+), 11 deletions(-)
> > >
> > > [...]
> > >
> > > > @@ -731,7 +747,9 @@ static struct io_pgtable *arm_v7s_alloc_pgtable(struct io_pgtable_cfg *cfg,
> > > > {
> > > > struct arm_v7s_io_pgtable *data;
> > > > - if (cfg->ias > ARM_V7S_ADDR_BITS || cfg->oas > ARM_V7S_ADDR_BITS)
> > > > + if (cfg->ias > ARM_V7S_ADDR_BITS ||
> > > > + (cfg->oas > ARM_V7S_ADDR_BITS &&
> > > > + !(cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_EXT)))
> > >
> > > Please can you instead change arm_v7s_alloc_pgtable() so that it allows an
> > > ias of up to 34 when the IO_PGTABLE_QUIRK_ARM_MTK_EXT is set?
> >
> > Here I only simply skip the oas checking for our case. then which way do
> > your prefer? something like you commented before:?
> >
> >
> > if (cfg->ias > ARM_V7S_ADDR_BITS)
> > return NULL;
> >
> > if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_EXT) {
> > if (!IS_ENABLED(CONFIG_PHYS_ADDR_T_64BIT))
> > cfg->oas = min(cfg->oas, ARM_V7S_ADDR_BITS);
> > else if (cfg->oas > 34)
> > return NULL;
> > } else if (cfg->oas > ARM_V7S_ADDR_BITS) {
> > return NULL;
> > }
>
> All it should take is something like:
>
> if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_EXT)
> max_oas = 34;
> else
> max_oas = 32;
> if (cfg->oas > max_oas)
> return NULL;
>
> or even just:
>
> if (cfg->oas > 32 ||
> (cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_EXT && cfg->oas > 34))
> return NULL;
>
> (and if we prefer the latter style, perhaps we could introduce some kind of
> "is_mtk_4gb()" helper to save on verbosity)
I wondered the same thing, but another place we'd want the check is in
iopte_to_paddr() which probably needs the PHYS_ADDR_T check to avoid GCC
warnings, although I didn't try it.
So if we did:
static bool cfg_mtk_ext_enabled(struct io_pgtable_cfg *cfg)
{
return IS_ENABLED(CONFIG_PHYS_ADDR_T_64BIT) &&
cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_EXT;
}
Then I suppose we could do this in _alloc():
if (cfg->oas > cfg_mtk_ext_enabled(cfg) ? 34 : ARM_V7S_ADDR_BITS)
return NULL;
and then this in iopte_to_paddr():
[...]
paddr = pte & mask;
if (!cfg_mtk_ext_enabled(cfg))
return paddr;
if (pte & ARM_V7S_ATTR_MTK_PA_BIT32)
paddr |= ...
[...]
What do you reckon?
Will
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v3 1/2] PM / devfreq: Add dev_pm_qos support
From: Chanwoo Choi @ 2019-08-22 10:14 UTC (permalink / raw)
To: Leonard Crestez, MyungJoo Ham, Kyungmin Park
Cc: Artur Świgoń, Jacky Bai, Saravana Kannan,
linux-pm@vger.kernel.org, Viresh Kumar, Krzysztof Kozlowski,
Alexandre Bailon, cpgs (cpgs@samsung.com), Georgi Djakov,
linux-arm-kernel@lists.infradead.org
In-Reply-To: <VI1PR04MB7023E05C5683C4392CEA5915EEAA0@VI1PR04MB7023.eurprd04.prod.outlook.com>
On 19. 8. 21. 오후 10:00, Leonard Crestez wrote:
> On 21.08.2019 04:40, Chanwoo Choi wrote:
>
>> On 19. 8. 21. 오전 12:24, Leonard Crestez wrote:
>>> Add dev_pm_qos notifies to devfreq core in order to support frequency
>>> limits via the dev_pm_qos_add_request.
>>>
>>> +static unsigned long get_effective_min_freq(struct devfreq *devfreq)
>>
>> I'm not sure that 'effective' expression is correct.
>> From this function, the devfreq can get the final target frequency.
>>
>> I think that we need to use the more correct expression
>> to give the meaning of function as following:
>>
>> get_min_freq
>> get_max_freq
>
> OK, will rename to get_min_freq and get_max_freq
>
>>> @@ -636,21 +688,40 @@ struct devfreq *devfreq_add_device(struct device *dev,
>>> err = -ENOMEM;
>>> goto err_out;
>>> }
>>>
>>> mutex_init(&devfreq->lock);
>>> - mutex_lock(&devfreq->lock);
>>
>> Basically, I think that it is safe to lock when touch
>> the variable of the devfreq.
>>
>> it is not proper way for the dev_pm_qos because
>> it breaks the existing locking reason of devfreq's variables.
>
> I don't understand what you mean. I'm initializing some stuff outside
> the lock to avoid circular lock warning between:
>
> (&devfreq->lock){+.+.}, at: devfreq_qos_min_notifier_call+0x24/0x48
> (&(n)->rwsem){++++}, at: blocking_notifier_call_chain+0x3c/0x78
>
> In general you don't need to lock an object while initializing except
> after it becomes accessible from the outside so devfreq_add_device
> doesn't need to take the lock so early.
>
> The QOS notifiers are registered on the parent device so in theory it's
> possible for somebody to add QOS requests while devfreq_add_device is
> executing. Maybe notifier registration should be moved at the end after
> unlock?
I think that it is more clear to add notifier
after mutex_unlock(&devfreq->lock) if there are no any issue.
>
> --
> Regards,
> Leonard
>
--
Best Regards,
Chanwoo Choi
Samsung Electronics
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v10 09/23] iommu/io-pgtable-arm-v7s: Extend to support PA[33:32] for MediaTek
From: Robin Murphy @ 2019-08-22 10:08 UTC (permalink / raw)
To: Yong Wu, Will Deacon
Cc: youlin.pei, devicetree, Nicolas Boichat, cui.zhang,
srv_heupstream, Tomasz Figa, Joerg Roedel, linux-kernel,
Evan Green, chao.hao, iommu, Rob Herring, linux-mediatek,
Matthias Brugger, ming-fan.chen, anan.sun, Matthias Kaehlcke,
linux-arm-kernel
In-Reply-To: <1566464186.11621.7.camel@mhfsdcap03>
On 2019-08-22 9:56 am, Yong Wu wrote:
> On Wed, 2019-08-21 at 16:24 +0100, Will Deacon wrote:
>> On Wed, Aug 21, 2019 at 09:53:12PM +0800, Yong Wu wrote:
>>> MediaTek extend the arm v7s descriptor to support up to 34 bits PA where
>>> the bit32 and bit33 are encoded in the bit9 and bit4 of the PTE
>>> respectively. Meanwhile the iova still is 32bits.
>>>
>>> Regarding whether the pagetable address could be over 4GB, the mt8183
>>> support it while the previous mt8173 don't, thus keep it as is.
>>>
>>> Signed-off-by: Yong Wu <yong.wu@mediatek.com>
>>> ---
>>> drivers/iommu/io-pgtable-arm-v7s.c | 32 +++++++++++++++++++++++++-------
>>> include/linux/io-pgtable.h | 7 +++----
>>> 2 files changed, 28 insertions(+), 11 deletions(-)
>>
>> [...]
>>
>>> @@ -731,7 +747,9 @@ static struct io_pgtable *arm_v7s_alloc_pgtable(struct io_pgtable_cfg *cfg,
>>> {
>>> struct arm_v7s_io_pgtable *data;
>>>
>>> - if (cfg->ias > ARM_V7S_ADDR_BITS || cfg->oas > ARM_V7S_ADDR_BITS)
>>> + if (cfg->ias > ARM_V7S_ADDR_BITS ||
>>> + (cfg->oas > ARM_V7S_ADDR_BITS &&
>>> + !(cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_EXT)))
>>
>> Please can you instead change arm_v7s_alloc_pgtable() so that it allows an
>> ias of up to 34 when the IO_PGTABLE_QUIRK_ARM_MTK_EXT is set?
>
> Here I only simply skip the oas checking for our case. then which way do
> your prefer? something like you commented before:?
>
>
> if (cfg->ias > ARM_V7S_ADDR_BITS)
> return NULL;
>
> if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_EXT) {
> if (!IS_ENABLED(CONFIG_PHYS_ADDR_T_64BIT))
> cfg->oas = min(cfg->oas, ARM_V7S_ADDR_BITS);
> else if (cfg->oas > 34)
> return NULL;
> } else if (cfg->oas > ARM_V7S_ADDR_BITS) {
> return NULL;
> }
All it should take is something like:
if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_EXT)
max_oas = 34;
else
max_oas = 32;
if (cfg->oas > max_oas)
return NULL;
or even just:
if (cfg->oas > 32 ||
(cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_EXT && cfg->oas > 34))
return NULL;
(and if we prefer the latter style, perhaps we could introduce some kind
of "is_mtk_4gb()" helper to save on verbosity)
We shouldn't need to care about the size of phys_addr_t either way - the
fact is that the MTK format can still encode up to 34 bits of PA
regardless of whether callers can actually pass addresses that large.
Robin.
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v10 09/23] iommu/io-pgtable-arm-v7s: Extend to support PA[33:32] for MediaTek
From: Will Deacon @ 2019-08-22 10:08 UTC (permalink / raw)
To: Yong Wu
Cc: youlin.pei, devicetree, Nicolas Boichat, cui.zhang,
srv_heupstream, Tomasz Figa, Joerg Roedel, linux-kernel,
Evan Green, chao.hao, iommu, Rob Herring, linux-mediatek,
Matthias Brugger, ming-fan.chen, anan.sun, Robin Murphy,
Matthias Kaehlcke, linux-arm-kernel
In-Reply-To: <1566464186.11621.7.camel@mhfsdcap03>
On Thu, Aug 22, 2019 at 04:56:26PM +0800, Yong Wu wrote:
> On Wed, 2019-08-21 at 16:24 +0100, Will Deacon wrote:
> > On Wed, Aug 21, 2019 at 09:53:12PM +0800, Yong Wu wrote:
> > > MediaTek extend the arm v7s descriptor to support up to 34 bits PA where
> > > the bit32 and bit33 are encoded in the bit9 and bit4 of the PTE
> > > respectively. Meanwhile the iova still is 32bits.
> > >
> > > Regarding whether the pagetable address could be over 4GB, the mt8183
> > > support it while the previous mt8173 don't, thus keep it as is.
> > >
> > > Signed-off-by: Yong Wu <yong.wu@mediatek.com>
> > > ---
> > > drivers/iommu/io-pgtable-arm-v7s.c | 32 +++++++++++++++++++++++++-------
> > > include/linux/io-pgtable.h | 7 +++----
> > > 2 files changed, 28 insertions(+), 11 deletions(-)
> >
> > [...]
> >
> > > @@ -731,7 +747,9 @@ static struct io_pgtable *arm_v7s_alloc_pgtable(struct io_pgtable_cfg *cfg,
> > > {
> > > struct arm_v7s_io_pgtable *data;
> > >
> > > - if (cfg->ias > ARM_V7S_ADDR_BITS || cfg->oas > ARM_V7S_ADDR_BITS)
> > > + if (cfg->ias > ARM_V7S_ADDR_BITS ||
> > > + (cfg->oas > ARM_V7S_ADDR_BITS &&
> > > + !(cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_EXT)))
> >
> > Please can you instead change arm_v7s_alloc_pgtable() so that it allows an
> > ias of up to 34 when the IO_PGTABLE_QUIRK_ARM_MTK_EXT is set?
>
> Here I only simply skip the oas checking for our case. then which way do
> your prefer? something like you commented before:?
>
>
> if (cfg->ias > ARM_V7S_ADDR_BITS)
> return NULL;
>
> if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_EXT) {
> if (!IS_ENABLED(CONFIG_PHYS_ADDR_T_64BIT))
> cfg->oas = min(cfg->oas, ARM_V7S_ADDR_BITS);
Isn't this always 32 for your IOMMUs?
> else if (cfg->oas > 34)
> return NULL;
> } else if (cfg->oas > ARM_V7S_ADDR_BITS) {
> return NULL;
> }
How about:
unsigned int oas_max = ARM_V7S_ADDR_BITS;
if (cfg->ias > ARM_V7S_ADDR_BITS)
return NULL;
if (IS_ENABLED(CONFIG_PHYS_ADDR_T_64BIT) &&
cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_EXT)
oas_max = 34;
if (cfg->oas > oas_max)
return NULL;
Will
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v2 1/3] arm64: imx8mq: add imx8mq iomux-gpr field defines
From: Philipp Zabel @ 2019-08-22 10:03 UTC (permalink / raw)
To: Guido Günther, Arnd Bergmann
Cc: Mark Rutland, DTML, Jernej Skrabec, Pengutronix Kernel Team,
Sam Ravnborg, Neil Armstrong, David Airlie, Shawn Guo,
Sascha Hauer, Jonas Karlman, Linux Kernel Mailing List, dri-devel,
Andrzej Hajda, Rob Herring, NXP Linux Team, Daniel Vetter,
Robert Chiras, Lee Jones, Fabio Estevam, Linux ARM,
Laurent Pinchart
In-Reply-To: <20190821174208.GA9486@bogon.m.sigxcpu.org>
On Wed, 2019-08-21 at 19:42 +0200, Guido Günther wrote:
> Hi,
> On Tue, Aug 13, 2019 at 01:07:52PM +0200, Arnd Bergmann wrote:
> > On Tue, Aug 13, 2019 at 12:10 PM Guido Günther <agx@sigxcpu.org> wrote:
> > > On Tue, Aug 13, 2019 at 10:08:44AM +0200, Arnd Bergmann wrote:
> > > > On Fri, Aug 9, 2019 at 6:24 PM Guido Günther <agx@sigxcpu.org> wrote:
> > > > >
> > > > > This adds all the gpr registers and the define needed for selecting
> > > > > the input source in the imx-nwl drm bridge.
> > > > >
> > > > > Signed-off-by: Guido Günther <agx@sigxcpu.org>
> > > > > +
> > > > > +#define IOMUXC_GPR0 0x00
> > > > > +#define IOMUXC_GPR1 0x04
> > > > > +#define IOMUXC_GPR2 0x08
> > > > > +#define IOMUXC_GPR3 0x0c
> > > > > +#define IOMUXC_GPR4 0x10
> > > > > +#define IOMUXC_GPR5 0x14
> > > > > +#define IOMUXC_GPR6 0x18
> > > > > +#define IOMUXC_GPR7 0x1c
> > > >
> > > > (more of the same)
> > > >
> > > > huh?
> > >
> > > These are the names from the imx8MQ reference manual (general purpose
> > > registers, they lump together all sorts of things), it's the same on
> > > imx6/imx7):
> > >
> > > https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h
> > > https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/mfd/syscon/imx7-iomuxc-gpr.h
> > >
> > > > > +/* i.MX8Mq iomux gpr register field defines */
> > > > > +#define IMX8MQ_GPR13_MIPI_MUX_SEL BIT(2)
> > > >
> > > > I think this define should probably be local to the pinctrl driver, to
> > > > ensure that no other drivers fiddle with the registers manually.
> > >
> > > The purpose of these bits is for a driver to fiddle with them to select
> > > the input source. Similar on imx7 it's already used for e.g. the phy
> > > refclk in the pci controller:
> > >
> > > https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/pci/controller/dwc/pci-imx6.c#n638
> >
> > That one should likely use either the clk interface or the phy
> > interface instead.
> >
> > > The GPRs are not about pad configuration but gather all sorts of things
> > > (section 8.2.4 of the imx8mq reference manual): pcie setup, dsi related
> > > bits so I don't think this should be done via a pinctrl
> > > driver. Should we handle that differently than on imx6/7?
> >
> > It would be nice to fix the existing code as well, but for the moment,
> > I only think we should not add more of that.
> >
> > Generally speaking, we can use syscon to do random things that don't
> > have a subsystem of their own, but we should not use it to do things
> > that have an existing driver framework like pinctrl, clock, reset, phy
> > etc.
>
> Since it's not an external pin i opted to use MUX_MMIO instead which
> seems like a good fit here. Does that make sense?
Yes, I agree. The i.MX6 display subsystem predates the mux framework,
otherwise I would have used it for the LVDS and HDMI muxes in the first
place. We should probably switch imx-drm over as well, in a backwards
compatible fashion. The &mux definitions are already there in
arch/arm/boot/dts/imx6q.dtsi.
regards
Philipp
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v3 2/2] PM / devfreq: Use dev_pm_qos for sysfs min/max_freq
From: Chanwoo Choi @ 2019-08-22 10:07 UTC (permalink / raw)
To: Leonard Crestez, MyungJoo Ham, Kyungmin Park
Cc: Artur Świgoń, Jacky Bai, Saravana Kannan,
linux-pm@vger.kernel.org, Viresh Kumar, Krzysztof Kozlowski,
Alexandre Bailon, cpgs (cpgs@samsung.com), Georgi Djakov,
linux-arm-kernel@lists.infradead.org
In-Reply-To: <VI1PR04MB7023A7AC7DDE349BF6D2D2C9EEAA0@VI1PR04MB7023.eurprd04.prod.outlook.com>
On 19. 8. 21. 오후 10:03, Leonard Crestez wrote:
> On 21.08.2019 05:02, Chanwoo Choi wrote:
>> On 19. 8. 21. 오전 12:24, Leonard Crestez wrote:
>>> Now that devfreq supports dev_pm_qos requests we can use them to handle
>>> the min/max_freq values set by userspace in sysfs, similar to cpufreq.
>>>
>>> Since dev_pm_qos handles frequencies as kHz this change reduces the
>>> precision of min_freq and max_freq. This shouldn't introduce problems
>>> because frequencies which are not an integer number of kHz are likely
>>> not an integer number of Hz either.
>>>
>>> Try to ensure compatibilitity by rounding min values down and rounding
>>> max values up.
>>>
>>> Simplify the {min,max}_freq_store code by setting "null" values of 0 and
>>> MAX_S32 respectively instead of clamping to what freq tables are
>>> actually supported. Values are already automatically clamped on
>>> readback.
>>>
>>> Also simplify by droping the limitation that userspace min_freq must be
>>> lower than userspace max_freq, it is already documented that max_freq
>>> takes precedence.
>>>
>>> @@ -1358,33 +1371,20 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr,
>>>
>>> ret = sscanf(buf, "%lu", &value);
>>> if (ret != 1)
>>> return -EINVAL;
>>>
>>> - mutex_lock(&df->lock);
>>> -
>>> - if (value) {
>>> - if (value > df->max_freq) {
>>> - ret = -EINVAL;
>>> - goto unlock;
>>> - }
>>
>> Actually, the user can input the value they want.
>> So, the above code is not necessary because the devfreq->scaling_max_freq
>> is never overflow from supported maximum frequency. The devfreq->scaling_max_freq
>> is based on OPP entries from DT.
>>
>> But, if we replace the existing request way of devfreq-cooling.c
>> with dev_pm_qos, devfreq->scaling_max_freq doesn't guarantee
>> the supported maximum frequency. >
>> We need to keep the supported min_freq/max_freq value without dev_pm_qos
>> requirement because the dev_pm_qos requirement might have the overflow value.
>> the dev_pm_qos doesn't know the supported minimum and maximum frequency
>> of devfreq device.
>
> I'm not sure I understand what you mean. My patch allows user to set
> entirely arbitrary min/max rates and this is good because we already
> have a well-defined way to handle this: max overrides min.
>
> The scaling_min_freq and scaling_max_freq variables can just be kept
> around indefinitely no matter what happens to thermal. They're just a
> cache for dev_pm_opp_find_freq_ceil and dev_pm_opp_find_freq_floor.
This patch doesn't check the range of input value
with the supported frequencies of devfreq device.
For example,
The devfreq0 device has the following available frequencies
100000000 200000000 300000000
and then user enters the 500000000 as following:
echo 500000000 > /sys/class/devfreq/devfreq0/min_freq
In result, get_effective_min_freq() will return the 500000000.
It is wrong value. it show the unsupported frequency through
min_freq sysfs path.
- devfreq->scaling_min_freq is 100000000,
- 1000 * (unsigned long)dev_pm_qos_read_value(devfreq->dev.parent,
DEV_PM_QOS_MIN_FREQUENCY)); is 500000000
>
> BTW: I noticed that scaling_min_freq and scaling_max_freq are updated in
> devfreq_notifier_call but devfreq->nb is not registered by default, only
> when a driver requests it explicitly. Is there a reason for this?
Initial version of devfreq has not forced to use the OPP interface
as the mandatory. So, it just provides the external function
devfreq_register_opp_notifier.
But,
We can call 'devfreq_register_opp_notifier' during devfreq_add_device()
because the OPP interface is mandatory for devfreq.
> Because I'd call dev_pm_opp_register_notifier inside devfreq_add_device
> and remove all the devfreq_register/unregister_notifier APIs.
OK.
>
> --
> Regards,
> Leonard
>
--
Best Regards,
Chanwoo Choi
Samsung Electronics
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v4] arm64: implement KPROBES_ON_FTRACE
From: Jisheng Zhang @ 2019-08-22 9:47 UTC (permalink / raw)
To: Naveen N. Rao
Cc: Mark Rutland, linux-doc@vger.kernel.org, Peter Zijlstra,
Catalin Marinas, Jonathan Corbet, linux-kernel@vger.kernel.org,
Steven Rostedt, Ingo Molnar, Masami Hiramatsu, Thomas Gleixner,
Will Deacon, linux-arm-kernel@lists.infradead.org
In-Reply-To: <1566456155.27ojwy97ss.naveen@linux.ibm.com>
Hi,
On Thu, 22 Aug 2019 12:23:58 +0530
"Naveen N. Rao" <naveen.n.rao@linux.vnet.ibm.com> wrote:
>
>
> Jisheng Zhang wrote:
> > KPROBES_ON_FTRACE avoids much of the overhead with regular kprobes as it
> > eliminates the need for a trap, as well as the need to emulate or
> > single-step instructions.
> >
> > Tested on berlin arm64 platform.
> >
> > ~ # mount -t debugfs debugfs /sys/kernel/debug/
> > ~ # cd /sys/kernel/debug/
> > /sys/kernel/debug # echo 'p _do_fork' > tracing/kprobe_events
> >
> > before the patch:
> >
> > /sys/kernel/debug # cat kprobes/list
> > ffffff801009fe28 k _do_fork+0x0 [DISABLED]
> >
> > after the patch:
> >
> > /sys/kernel/debug # cat kprobes/list
> > ffffff801009ff54 k _do_fork+0x4 [DISABLED][FTRACE]
> >
> > Signed-off-by: Jisheng Zhang <Jisheng.Zhang@synaptics.com>
>
> This looks good to me. Except for a small confirmation below:
> Reviewed-by: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
>
>
<...>
> > +/* Ftrace callback handler for kprobes -- called under preepmt disabed */
> > +void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
> > + struct ftrace_ops *ops, struct pt_regs *regs)
> > +{
> > + struct kprobe *p;
> > + struct kprobe_ctlblk *kcb;
> > +
> > + /* Preempt is disabled by ftrace */
> > + p = get_kprobe((kprobe_opcode_t *)ip);
> > + if (unlikely(!p) || kprobe_disabled(p))
> > + return;
> > +
> > + kcb = get_kprobe_ctlblk();
> > + if (kprobe_running()) {
> > + kprobes_inc_nmissed_count(p);
> > + } else {
> > + unsigned long orig_ip = instruction_pointer(regs);
> > + /* Kprobe handler expects regs->pc = pc + 4 as breakpoint hit */
> > + instruction_pointer_set(regs, ip + sizeof(kprobe_opcode_t));
>
> Just want to make sure that you've confirmed that this is what happens
> with a regular trap/brk based kprobe on ARM64. The reason for setting
> the instruction pointer here is to ensure that it is set to the same
> value as would be set if there was a trap/brk instruction at the ftrace
> location. This ensures that the kprobe pre handler sees the same value
> regardless.
Due to the arm64's DYNAMIC_FTRACE_WITH_REGS implementation, the code itself
is correct. But this doesn't look like "there was a trap instruction at
the ftrace location".
W/O KPROBE_ON_FTRACE:
foo:
00 insA
04 insB
08 insC
kprobe's pre_handler() will see pc points to 00.
W/ KPROBE_ON_FTRACE:
foo:
00 lr saver
04 nop // will be modified to ftrace call ins when KPROBE is armed
08 insA
0c insB
later, kprobe_ftrace_handler() will see pc points to 04, so pc + 4 will
point to 08 the same as the one w/o KPROBE_ON_FTRACE.
It seems I need to fix the comment.
>
> Further changes to the instruction pointer are to achieve the same
> effect for kprobe post handlers.
>
>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH v2] i2c: mediatek: disable zero-length transfers for mt8183
From: Hsin-Yi Wang @ 2019-08-22 9:45 UTC (permalink / raw)
To: Wolfram Sang, Yingjoe Chen
Cc: linux-arm-kernel, Nicolas Boichat, Alexandru M Stan, Jun Gao,
linux-kernel, linux-mediatek, linux-i2c, Matthias Brugger,
Thomas Gleixner, Qii Wang
When doing i2cdetect quick write mode, we would get transfer
error ENOMEM, and i2cdetect shows there's no device at the address.
Quoting from mt8183 datasheet, the number of transfers to be
transferred in one transaction should be set to bigger than 1,
so we should forbid zero-length transfer and update functionality.
Incorrect return:
localhost ~ # i2cdetect -q -y 0
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
After this patch:
localhost ~ # i2cdetect -q -y 0
Error: Can't use SMBus Quick Write command on this bus
localhost ~ # i2cdetect -y 0
Warning: Can't use SMBus Quick Write command, will skip some addresses
0 1 2 3 4 5 6 7 8 9 a b c d e f
00:
10:
20:
30: -- -- -- -- -- -- -- --
40:
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60:
70:
Reported-by: Alexandru M Stan <amstan@chromium.org>
Signed-off-by: Hsin-Yi Wang <hsinyi@chromium.org>
---
Change from v1:
* restore the order of algo and quirks
---
drivers/i2c/busses/i2c-mt65xx.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c
index 252edb433fdf..29eae1bf4f86 100644
--- a/drivers/i2c/busses/i2c-mt65xx.c
+++ b/drivers/i2c/busses/i2c-mt65xx.c
@@ -234,6 +234,10 @@ static const struct i2c_adapter_quirks mt7622_i2c_quirks = {
.max_num_msgs = 255,
};
+static const struct i2c_adapter_quirks mt8183_i2c_quirks = {
+ .flags = I2C_AQ_NO_ZERO_LEN,
+};
+
static const struct mtk_i2c_compatible mt2712_compat = {
.regs = mt_i2c_regs_v1,
.pmic_i2c = 0,
@@ -298,6 +302,7 @@ static const struct mtk_i2c_compatible mt8173_compat = {
};
static const struct mtk_i2c_compatible mt8183_compat = {
+ .quirks = &mt8183_i2c_quirks,
.regs = mt_i2c_regs_v2,
.pmic_i2c = 0,
.dcm = 0,
@@ -870,7 +875,11 @@ static irqreturn_t mtk_i2c_irq(int irqno, void *dev_id)
static u32 mtk_i2c_functionality(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+ if (adap->quirks->flags & I2C_AQ_NO_ZERO_LEN)
+ return I2C_FUNC_I2C |
+ (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+ else
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
static const struct i2c_algorithm mtk_i2c_algorithm = {
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox