Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 04/12] arm64: Initialise high_memory global variable earlier
From: Steve Capper @ 2017-12-12 10:56 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171211120022.ujdc37dp3iawzrem@armageddon.cambridge.arm.com>

On Mon, Dec 11, 2017 at 12:00:22PM +0000, Catalin Marinas wrote:
> On Mon, Dec 04, 2017 at 02:13:05PM +0000, Steve Capper wrote:
> > The high_memory global variable is used by
> > cma_declare_contiguous(.) before it is defined.
> > 
> > We don't notice this as we compute __pa(high_memory - 1), and it looks
> > like we're processing a VA from the direct linear map.
> > 
> > This problem becomes apparent when we flip the kernel virtual address
> > space and the linear map is moved to the bottom of the kernel VA space.
> > 
> > This patch moves the initialisation of high_memory before it used.
> > 
> > Signed-off-by: Steve Capper <steve.capper@arm.com>
> 
> It looks like we've had this bug since 3.18 (f7426b983a6a, "mm: cma:
> adjust address limit to avoid hitting low/high memory boundary"). It may
> be worth adding a cc stable on this patch.

Thanks Catalin,
Will add a fixes and cc stable.

Cheers,
-- 
Steve

> 
> -- 
> Catalin

^ permalink raw reply

* [PATCH] s5p-mfc: Fix encoder menu controls initialization
From: Sylwester Nawrocki @ 2017-12-12 11:02 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CGME20171212110322epcas2p11e3f36ba3de73a03f062b7877d797d2a@epcas2p1.samsung.com>

This patch fixes the menu_skip_mask field initialization
and addresses a following issue found by the SVACE static
analysis:

* NO_EFFECT.SELF: assignment to self in expression 'cfg.menu_skip_mask = cfg.menu_skip_mask'
  No effect at drivers/media/platform/s5p-mfc/s5p_mfc_enc.c:2083

Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
---
 drivers/media/platform/s5p-mfc/s5p_mfc_enc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
index 2a5fd7c42cd5..0d5d465561be 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
@@ -2080,7 +2080,7 @@ int s5p_mfc_enc_ctrls_setup(struct s5p_mfc_ctx *ctx)
 
 			if (cfg.type == V4L2_CTRL_TYPE_MENU) {
 				cfg.step = 0;
-				cfg.menu_skip_mask = cfg.menu_skip_mask;
+				cfg.menu_skip_mask = controls[i].menu_skip_mask;
 				cfg.qmenu = mfc51_get_menu(cfg.id);
 			} else {
 				cfg.step = controls[i].step;
-- 
2.14.2

^ permalink raw reply related

* [PATCH 1/6] ARM: stm32: prepare stm32 family to welcome armv7 architecture
From: afzal mohammed @ 2017-12-12 11:03 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAK8P3a2rrpFuwET8r1H0YWVABbCZr5c2ySrKCgA4mfoZPfWp6Q@mail.gmail.com>

Hi,

On Mon, Dec 11, 2017 at 02:40:43PM +0100, Arnd Bergmann wrote:
> On Mon, Dec 11, 2017 at 11:25 AM, Linus Walleij

> >> This patch prepares the STM32 machine for the integration of Cortex-A
> >> based microprocessor (MPU), on top of the existing Cortex-M
> >> microcontroller family (MCU). Since both MCUs and MPUs are sharing
> >> common hardware blocks we can keep using ARCH_STM32 flag for most of
> >> them. If a hardware block is specific to one family we can use either
> >> ARCH_STM32_MCU or ARCH_STM32_MPU flag.

> To what degree do we need to treat them as separate families
> at all then? I wonder if the MCU/MPU distinction is always that
> clear along the Cortex-M/Cortex-A separation,

> What
> exactly would we miss if we do away with the ARCH_STM32_MCU
> symbol here?

Based on this patch series, the only difference seems to be w.r.t ARM
components, not peripherals outside ARM subystem. Vybrid VF610 is a
similar case, though not identical (it can have both instead of
either), deals w/o extra symbols,

8064887e02fd6 (ARM: vf610: enable Cortex-M4 configuration on Vybrid SoC)

> especially if
> we ever get to a chip that has both types of cores.

Your wish fulfilled, Vybrid VF610 has both A5 & M4F and mainline Linux
boots on both (simultaneously as well), and the second Linux support,
i.e. on M4 went thr' your keyboard, see above commit :)

There are quite a few others as well, TI's AM335x (A8 + M3), AM437x
(A9 + M3), AM57x (A15 + M4), but of these Cortex M's, the one in AM57x
only can be Linux'able. On others they are meant for PM with limited
resources.

> > So yesterdays application processors are todays MCU processors.
> >
> > I said this on a lecture for control systems a while back and
> > stated it as a reason I think RTOSes are not really seeing a bright
> > future compared to Linux.

> I think there is still lots of room for smaller RTOS in the long run,

Me being an electrical engineer & worked to some extent in motor
control on RTOS/no OS (the value of my opinion is questionable
though), the thought of handling the same in Linux (even RT) sends
shivers down my spine. Here, case being considered is the type of
motor (like permanent magnet ones) where each phase of the motor has
to be properly excited during every PWM period (say every 100us,
depending on the feedback, algorithm, other synchronization) w/o which
the motor that has been told to run might try to fly. This is
different from stepper motor where if control misbehaves/stops nothing
harmful normally happens.

But my opinion is a kind of knee-jerk reaction and based on prevalent
atitude in that field, hmm.., probably i should attempt it first.

Regards
afzal

^ permalink raw reply

* [PATCH 07/12] arm64: mm: Place kImage at bottom of VA space
From: Steve Capper @ 2017-12-12 11:03 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171204181213.or2t24rdh6uwj3iu@capper-debian.cambridge.arm.com>

On Mon, Dec 04, 2017 at 06:12:13PM +0000, Steve Capper wrote:
> On Mon, Dec 04, 2017 at 05:27:10PM +0000, Ard Biesheuvel wrote:
> > On 4 December 2017 at 17:18, Steve Capper <steve.capper@arm.com> wrote:
> > > Hi Ard,
> > >
> > > On Mon, Dec 04, 2017 at 04:25:18PM +0000, Ard Biesheuvel wrote:
> > >> On 4 December 2017 at 14:13, Steve Capper <steve.capper@arm.com> wrote:
> > >> > Re-arrange the kernel memory map s.t. the kernel image resides in the
> > >> > bottom 514MB of memory.
> > >>
> > >> I guess this breaks KASLR entirely, no? Given that it adds an offset
> > >> in the range [0 ... sizeof(VMALLOC_SPACE) /4 ].
> > >
> > > Yes, yes it does. Sorry about this. I had very carefully tested KASLR
> > > with custom offsets... on my early page table code. I will have a think
> > > about this.
> > >
> > > From a KASLR side, my (renewed) understanding is that a virtual address
> > > as low as possible is desired for the kimage start as that affords the
> > > most wiggle room?
> > >
> > 
> > Well, the nice thing about the current arrangement is that the default
> > is adjacent to the vmalloc space so any non-zero [bounded] offset
> > produces a valid placement. Addition with subtraction is easy, so
> > which side the default placement happens to be at does not really
> > matter. Having to implement additional bounds checking in the early
> > KASLR init code to stay clear of the PCI I/O or fixmap regions sounds
> > a bit more cumbersome.
> > 
> 
> I *think* I can fix KASAN_SHADOW_END to be 0xFFFF200000000000 on both 48-bit
> and 52-bit VA configurations. Thus I may be able to enable 52-bit VA with
> minimal disruption to the layout of the VA space (i.e. no need to change
> the layout) if I also depend on CONFIG_RELOCATABLE.
> 

Unfortunately, having KASAN_SHADOW_END at 0xFFFF2000000000000 doesn't
work with 52-bit VAs as this would place it in the direct linear map
area.

So I think we need to flip the two halves of the kernel address space in
order to accommodate inline KASAN that operates under multiple VA space
sizes (I couldn't figure out any way to patch the inline KASAN instrumentation).

Cheers,
-- 
Steve

^ permalink raw reply

* [PATCH v2] ARM: dts: exynos: Enable Mixer node for Exynos5800 Peach Pi machine
From: Guillaume Tucker @ 2017-12-12 11:07 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1bbedec6-6250-f02a-bf9a-4b9849833de2@samsung.com>

On 12/12/17 10:55, Marek Szyprowski wrote:
> Hi Guillaume,
> 
> On 2017-12-12 11:43, Guillaume Tucker wrote:
>> On 12/12/17 10:17, Marek Szyprowski wrote:
>>> Hi Krzysztof,
>>>
>>> On 2017-12-12 11:09, Krzysztof Kozlowski wrote:
>>>> On Tue, Dec 12, 2017 at 10:55 AM, Krzysztof Kozlowski <krzk@kernel.org> wrote:
>>>>> On Tue, Dec 12, 2017 at 8:42 AM, Javier Martinez Canillas
>>>>> <javierm@redhat.com> wrote:
>>>>>> Commit 1cb686c08d12 ("ARM: dts: exynos: Add status property to Exynos 542x
>>>>>> Mixer nodes") disabled the Mixer node by default in the DTSI and enabled
>>>>>> for each Exynos 542x DTS. But unfortunately it missed to enable it for the
>>>>>> Exynos5800 Peach Pi machine, since the 5800 is also an 542x SoC variant.
>>>>>>
>>>>>> Fixes: 1cb686c08d12 ("ARM: dts: exynos: Add status property to Exynos 542x Mixer nodes")
>>>>>> Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
>>>>>> Acked-by: Marek Szyprowski <m.szyprowski@samsung.com>
>>>>>>
>>>>>> ---
>>>>>>
>>>>>> Changes in v2:
>>>>>> - Remove RFT tag.
>>>>> Thanks guys! However I still would like to see a tested-by for this on
>>>>> Peach Pi (AFAIU, Marek's only acked the code/solution).
>>>> On the other hand I could just apply it for my for-next branch and
>>>> we'll see if it fixes kernel-ci boot tests... Not a nice way of
>>>> testing but apparently no one has Peach Pi.
>>>
>>> Frankly, I don't expect that this will solve the boot hang issue on PeachPi.
>>> However it should at least hide the unbalanced regulator issue.
>>
>> We have a peach-pi in our LAVA lab so I've tested it and
>> actually, it does fix the hang on v4.15-rc3:
>>
>> ? https://lava.collabora.co.uk/scheduler/job/1019877
>> ? https://lava.collabora.co.uk/scheduler/job/1019878
>>
>> I ran it twice and it booted both times.? I also ran the same
>> boot tests with the same kernel but the dtb from v4.15-rc3
>> without the fix to double check and these failed:
>>
>> ? https://lava.collabora.co.uk/scheduler/job/1019879
>> ? https://lava.collabora.co.uk/scheduler/job/1019880
>>
>>
>> Tested-by: Guillaume Tucker <guillaume.tucker@collabora.com>
>>
>>
>> Thanks for the fix!
> 
> Well, thanks for the test! It proves that there the boot failure is
> caused by an issue somewhere in the error path of Exynos DRM, Analogix
> DP, Simple Panel or other drivers.
> 
> This patch simply hides it by fixing the source issue of the Exynos
> DRM initialization failure. :-)
> 
> I hope Javier will be able to investigate the discussed hang issue
> later, as fixing it is also imho important.

Sure.  This device tree change is needed to get HDMI to work so
it's still a fix for that.  Also it's good to know that nothing
else breaks when the driver issue is "hidden".  Might be worth
testing on -next as well as it might help spot any new issues
that haven't been merged in mainline yet, or in general give
another data point.

Guillaume

^ permalink raw reply

* [PATCH 4/4] [v4] pinctrl: qcom: qdf2xxx: add support for new ACPI HID QCOM8002
From: Andy Shevchenko @ 2017-12-12 11:07 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CACRpkdYer_Q5R4XhWTX3=jwshcKZRSDY9=gOvSeUPsQHbUw6vw@mail.gmail.com>

On Tue, 2017-12-12 at 11:42 +0100, Linus Walleij wrote:
> On Sat, Dec 2, 2017 at 12:28 AM, Timur Tabi <timur@codeaurora.org>
> wrote:

> > +               /* The number of GPIOs in the approved list */
> > +               ret = device_property_read_u16_array(&pdev->dev,
> > "gpios",
> > +                                                    NULL, 0);
> > +               if (ret < 0) {
> > +                       dev_err(&pdev->dev, "missing 'gpios'
> > property\n");
> > +                       return ret;
> > +               }
> 
> This is in direct conflict with the existing "gpios" binding in device
> tree.
> 
> Where is this name coming from? ACPI standards?

Not ACPI standards as of my knowledge. ACPI standard defines a common
scheme how to define properties, it doesn't tell anything about property
names or any mappings between names to values or names to "OS
subsystem").

As for GPIO we just follow *de facto* what DT has right now, i.e. "xxx-
gpio" or "xxx-gpios" pattern is used to map ACPI standard resource to a
GPIO name. That's how GPIO ACPI lib is being developed.

> If device tree and ACPI start defining things which are in direct
> conflict
> we can just shut down this device_property() business altogether,
> it will never work that way.

This is fully understandable. Also it works in other direction, i.e. if
DT will break the established thing it will break also ACPI and built-in 
device properties.

We are keeping an eye on this not to happen as much as we can in any
direction.

So, summarize above, I don't see any impediments (except maybe very
broken ARM64 firmware that is already on devices on market) to make it
properly from the beginning.

-- 
Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Intel Finland Oy

^ permalink raw reply

* [PATCH v5 15/30] arm64/sve: Signal handling support
From: Dave Martin @ 2017-12-12 11:11 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171212104030.GG28301@arm.com>

On Tue, Dec 12, 2017 at 10:40:30AM +0000, Will Deacon wrote:
> On Mon, Dec 11, 2017 at 11:23:09AM -0800, Kees Cook wrote:
> > On Mon, Dec 11, 2017 at 6:07 AM, Will Deacon <will.deacon@arm.com> wrote:
> > > On Thu, Dec 07, 2017 at 10:50:38AM -0800, Kees Cook wrote:
> > >> My question is mainly: why not just use copy_*() everywhere instead?
> > >> Having these things so spread out makes it fragile, and there's very
> > >> little performance benefit from using __copy_*() over copy_*().
> > >
> > > I think that's more of a general question. Why not just remove the __
> > > versions from the kernel entirely if they're not worth the perf?
> > 
> > That has been something Linus has strongly suggested in the past, so
> > I've kind of been looking for easy places to drop the __copy_*
> > versions. :)
> 
> Tell you what then: I'll Ack the arm64 patch if it's part of a series
> removing the thing entirely :p
> 
> I guess we'd still want to the validation of the whole sigframe though,
> so we don't end up pushing half a signal stack before running into an
> access_ok failure?

That's an interesting question.  In many cases access_ok() might become
redundant, but for syscalls that you don't want to have side-effects
on user memory on failure it's still relevant.

In the signal case we'd still an encompassing access_ok() to prevent
stack guard overruns, because the signal frame can be large and isn't
written or read contiguously or in a well-defined order.

Cheers
---Dave

^ permalink raw reply

* [RFC] KVM API extensions for SVE
From: Dave Martin @ 2017-12-12 11:12 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171211192432.GL910@cbox>

On Mon, Dec 11, 2017 at 08:24:32PM +0100, Christoffer Dall wrote:
> On Mon, Dec 11, 2017 at 02:51:36PM +0000, Dave Martin wrote:
> > On Fri, Nov 24, 2017 at 03:45:38PM +0100, Christoffer Dall wrote:

[...]

> > > So you're saying even if we try the "expose full width and read back
> > > hidden values" approach, those hidden values may be changed when
> > > executing the guest, due to the KVM implementation or the way hardware
> > > works, is that the point?
> > 
> > Basically yes.
> > 
> > > I think the KVM interface should be designed similarly to being able to
> > > probe a hardware CPU's register state at various stages of execution.
> > > 
> > > So, for example, if you write content to hidden bits in the SVE
> > > registers from EL2 on real hardware and limit the length using ZCR_EL2,
> > > and then run a bunch of code in EL1/0, and then come back to EL2 and
> > > examine the registers again, then we should model that behavior in
> > > software.
> > > 
> > > In other words, I think we have to model this more closely to what
> > > guarantees ZCR_EL2 gives us, and not ZCR_EL1, and choose something
> > > architecturally compliant which is reasonable to implement.
> > 
> > So, we imagine that provided the vcpu is not run in the meantime,
> > all accesses to SVE regs via the KVM reg API act like they are executed
> > at EL2?
> 
> Yes, userspace probing virtual EL1 state should be like EL2 probing EL1
> state on hardware.
> 
> > 
> > That doesn't seem unreasonable, and it removes any ordering requirement
> > between ZCR_EL1 and the SVE regs providing that the vcpu isn't set
> > running in the meantime.  There is no userspace access to ZCR_EL2 at
> > all, if we go with the model of configuring that via attributes that
> > must be configured before vcpu startup -- in which case there is no
> > ordering requirement there.
> > 
> > The extra bits beyond ZCR_EL1.LEN may disappear as soon as the vcpu
> > is run, but that is architecturally consistent behaviour at least.
> > 
> 
> Yes, I think we agree here.  It will all be interesting with nested
> virtualization where we have to start exposing ZCR_EL2, but that's not
> for today.

OK, that sounds reasonable.  There are a couple of options open for the
nested virt case, but we don't need to worry about it now in any case.

Cheers
---Dave

^ permalink raw reply

* [RFC PATCH][resend] pciehp: fix a race between pciehp and removing operations by sysfs
From: Xiongfeng Wang @ 2017-12-12 11:30 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1513067384-10914-1-git-send-email-wangxiongfeng2@huawei.com>

This patch seems to introduce another issue. pciehp_power_thread() use
'container_of' to get the 'slot' according to 'work_struct'.
If the 'slot' has been freed before that, there will be an issue.

On 2017/12/12 16:29, Xiongfeng Wang wrote:
> When the Attention button on a PCIE slot is pressed, 5 seconds later,
> pciehp_power_thread() will be scheduled on slot->wq. This function will
> get a global mutex lock 'pci_rescan_remove_lock' in
> pciehp_unconfigure_device().
> 
> At the same time, we remove the pcie port by sysfs, which results in
> pci_stop_and_remove_bus_device_locked() called. This function will get
> the global mutex lock 'pci_rescan_remove_lock', and then release the
> struct 'ctrl', which will wait until the work_struct on slot->wq is
> finished.
> 
> If pci_stop_and_remove_bus_device_locked() got the mutex lock, and
> before it drains workqueue slot->wq, pciehp_power_thread() is scheduled
> on slot->wq and tries to get the mutex lock. Then
> pci_stop_and_remove_bus_device_locked() tries to drain workqueue
> slot->wq and wait until work struct 'pciehp_power_thread()' is finished.
> Then a hung_task happens.
> 
> This patch solve this problem by schedule 'pciehp_power_thread()' on a
> system workqueue instead of slot->wq.
> 
> The Call Trace we got is as following.
> 
>  INFO: task kworker/0:2:4413 blocked for more than 120 seconds.
>        Tainted: P        W  O    4.12.0-rc1 #1
>  "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
>  kworker/0:2     D    0  4413      2 0x00000000
>  Workqueue: pciehp-0 pciehp_power_thread
>  Call trace:
>  [<ffff0000080861d4>] __switch_to+0x94/0xa8
>  [<ffff000008bea9c0>] __schedule+0x1b0/0x708
>  [<ffff000008beaf58>] schedule+0x40/0xa4
>  [<ffff000008beb33c>] schedule_preempt_disabled+0x28/0x40
>  [<ffff000008bec1dc>] __mutex_lock.isra.8+0x148/0x50c
>  [<ffff000008bec5c4>] __mutex_lock_slowpath+0x24/0x30
>  [<ffff000008bec618>] mutex_lock+0x48/0x54
>  [<ffff0000084d8188>] pci_lock_rescan_remove+0x20/0x28
>  [<ffff0000084f87c0>] pciehp_unconfigure_device+0x54/0x1cc
>  [<ffff0000084f8260>] pciehp_disable_slot+0x4c/0xbc
>  [<ffff0000084f8370>] pciehp_power_thread+0xa0/0xb8
>  [<ffff0000080e9ce8>] process_one_work+0x13c/0x3f8
>  [<ffff0000080ea004>] worker_thread+0x60/0x3e4
>  [<ffff0000080f0814>] kthread+0x10c/0x138
>  [<ffff0000080836c0>] ret_from_fork+0x10/0x50
>  INFO: task bash:31732 blocked for more than 120 seconds.
>        Tainted: P        W  O    4.12.0-rc1 #1
>  "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
>  bash            D    0 31732      1 0x00000009
>  Call trace:
>  [<ffff0000080861d4>] __switch_to+0x94/0xa8
>  [<ffff000008bea9c0>] __schedule+0x1b0/0x708
>  [<ffff000008beaf58>] schedule+0x40/0xa4
>  [<ffff000008bee7b4>] schedule_timeout+0x1a0/0x340
>  [<ffff000008bebb88>] wait_for_common+0x108/0x1bc
>  [<ffff000008bebc64>] wait_for_completion+0x28/0x34
>  [<ffff0000080e7594>] flush_workqueue+0x130/0x488
>  [<ffff0000080e79b0>] drain_workqueue+0xc4/0x164
>  [<ffff0000080ec3cc>] destroy_workqueue+0x28/0x1f4
>  [<ffff0000084fa094>] pciehp_release_ctrl+0x34/0xe0
>  [<ffff0000084f75b0>] pciehp_remove+0x30/0x3c
>  [<ffff0000084f24d8>] pcie_port_remove_service+0x3c/0x54
>  [<ffff00000876b1e4>] device_release_driver_internal+0x150/0x1d0
>  [<ffff00000876b28c>] device_release_driver+0x28/0x34
>  [<ffff00000876a018>] bus_remove_device+0xe0/0x11c
>  [<ffff000008766348>] device_del+0x200/0x304
>  [<ffff00000876646c>] device_unregister+0x20/0x38
>  [<ffff0000084f2560>] remove_iter+0x44/0x54
>  [<ffff000008765230>] device_for_each_child+0x4c/0x90
>  [<ffff0000084f2c98>] pcie_port_device_remove+0x2c/0x48
>  [<ffff0000084f2f48>] pcie_portdrv_remove+0x60/0x6c
>  [<ffff0000084e3de4>] pci_device_remove+0x48/0x110
>  [<ffff00000876b1e4>] device_release_driver_internal+0x150/0x1d0
>  [<ffff00000876b28c>] device_release_driver+0x28/0x34
>  [<ffff0000084db028>] pci_stop_bus_device+0x9c/0xac
>  [<ffff0000084db190>] pci_stop_and_remove_bus_device_locked+0x24/0x3c
>  [<ffff0000084e5eb0>] remove_store+0x74/0x80
>  [<ffff000008764680>] dev_attr_store+0x44/0x5c
>  [<ffff0000082e7e1c>] sysfs_kf_write+0x5c/0x74
>  [<ffff0000082e7014>] kernfs_fop_write+0xcc/0x1dc
>  [<ffff0000082602e0>] __vfs_write+0x48/0x13c
>  [<ffff00000826174c>] vfs_write+0xa8/0x198
>  [<ffff000008262ce8>] SyS_write+0x54/0xb0
>  [<ffff000008083730>] el0_svc_naked+0x24/0x28
> 
> Signed-off-by: Xiongfeng Wang <wangxiongfeng2@huawei.com>
> ---
>  drivers/pci/hotplug/pciehp_ctrl.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c
> index 83f3d4a..9d39d85 100644
> --- a/drivers/pci/hotplug/pciehp_ctrl.c
> +++ b/drivers/pci/hotplug/pciehp_ctrl.c
> @@ -221,7 +221,7 @@ static void pciehp_queue_power_work(struct slot *p_slot, int req)
>  	info->p_slot = p_slot;
>  	INIT_WORK(&info->work, pciehp_power_thread);
>  	info->req = req;
> -	queue_work(p_slot->wq, &info->work);
> +	schedule_work(&info->work);
>  }
>  
>  void pciehp_queue_pushbutton_work(struct work_struct *work)
> 

^ permalink raw reply

* mainline/master boot bisection: v4.15-rc3 on peach-pi #3228-staging
From: Marek Szyprowski @ 2017-12-12 11:38 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CABxcv=mUHgTxvYpOYstE7rQn8fKS3QGiTT=FzBUc8DREvVuTeQ@mail.gmail.com>

Hi All,

On 2017-12-11 23:28, Javier Martinez Canillas wrote:
> [adding Marek and Shuah to cc list]
>
> On Mon, Dec 11, 2017 at 6:05 PM, Daniel Vetter <daniel.vetter@ffwll.ch> wrote:
>> On Mon, Dec 11, 2017 at 11:30 AM, Guillaume Tucker
>> <guillaume.tucker@collabora.com> wrote:
>>> Hi Daniel,
>>>
>>> Please see below, I've had several bisection results pointing at
>>> that commit over the week-end on mainline but also on linux-next
>>> and net-next.  While the peach-pi is a bit flaky at the moment
>>> and is likely to have more than one issue, it does seem like this
>>> commit is causing some well reproducible kernel hang.
>>>
>>> Here's a re-run with v4.15-rc3 showing the issue:
>>>
>>>    https://lava.collabora.co.uk/scheduler/job/1018478
>>>
>>> and here's another one with the change mentioned below reverted:
>>>
>>>    https://lava.collabora.co.uk/scheduler/job/1018479
>>>
>>> They both show a warning about "unbalanced disables for lcd_vdd",
>>> I don't know if this is related as I haven't investigated any
>>> further.  It does appear to reliably hang with v4.15-rc3 and
>>> boot most of the time with the commit reverted though.
>>>
>>> The automated kernelci.org bisection is still an experimental
>>> tool and it may well be a false positive, so please take this
>>> result with a pinch of salt...
>> The patch just very minimal moves the connector cleanup around (so
>> timing change), but except when you unload a driver (or maybe that
>> funny EPROBE_DEFER stuff) it shouldn't matter. So if you don't have
>> more info than "seems to hang a bit more" I have no idea what's wrong.
>> The patch itself should work, at least it survived quite some serious
>> testing we do on everything.
>> -Daniel
>>
> Marek was pointing to a different culprit [0] in this [1] thread. I
> see that both commits made it to v4.15-rc3, which is the first version
> where boot fails. So maybe is a combination of both? Or rather
> reverting one patch masks the error in the other.
>
> I've access to the machine but unfortunately not a lot of time to dig
> on this, I could try to do it in the weekend though.

After a recent discussion on the Javier's patch:
https://patchwork.kernel.org/patch/10106417/
I've managed to reproduce this issue also on Exynos5250 based Samsung
Snow Chromebook and investigate a bit.

It is caused by a deadlock in the main kernel workqueue. Here are details:

1. Exynos DRM fails to initialize due to missing regulators and gets moved
to deferred probe device list

2. Deferred probe is triggered and kernel "events" workqueue calls
deferred_probe_work_func()

3. exynos_drm_bind() is called, component_bind_all() fails due to missing
Exynos Mixer device

4. error handling path is executed in exynos_drm_bind(), which calls
drm_mode_config_cleanup()

5. drm_mode_config_cleanup() calls flush_scheduled_work(), what causes
deadlock.

Do You have idea how to fix this issue properly?

Taking a look at git blame, this indeed shows that the issue has been
introduced by the commit a703c55004e1 ("drm: safely free connectors from
connector_ite"), which added a call to flush_scheduled_work() in
drm_mode_config_cleanup().

drm_mode_config_cleanup() should avoid calling flush_scheduled_work() if
called from the workqueue, but I don't have idea how to check that. The
other way of fixing it would be to resurrect separate workqueue for DRM
related events.

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland

^ permalink raw reply

* [PATCH] arm64: mm: Fix false positives in set_pte_at access/dirty race detection
From: Will Deacon @ 2017-12-12 11:43 UTC (permalink / raw)
  To: linux-arm-kernel

Jiankang reports that our race detection in set_pte_at is firing when
copying the page tables in dup_mmap as a result of a fork(). In this
situation, the page table isn't actually live and so there is no way
that we can race with a concurrent update from the hardware page table
walker.

This patch reworks the race detection so that we require either the
mm to match the current active_mm (i.e. currently installed in our TTBR0)
or the mm_users count to be greater than 1, implying that the page table
could be live in another CPU. The mm_users check might still be racy,
but we'll avoid false positives and it's not realistic to validate that
all the necessary locks are held as part of this assertion.

Cc: Yisheng Xie <xieyisheng1@huawei.com>
Reported-by: Jiankang Chen <chenjiankang1@huawei.com>
Tested-by: Jiankang Chen <chenjiankang1@huawei.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
---
 arch/arm64/include/asm/pgtable.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index 3ff03a755c32..bdcc7f1c9d06 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -42,6 +42,8 @@
 #include <asm/cmpxchg.h>
 #include <asm/fixmap.h>
 #include <linux/mmdebug.h>
+#include <linux/mm_types.h>
+#include <linux/sched.h>
 
 extern void __pte_error(const char *file, int line, unsigned long val);
 extern void __pmd_error(const char *file, int line, unsigned long val);
@@ -215,9 +217,6 @@ static inline void set_pte(pte_t *ptep, pte_t pte)
 	}
 }
 
-struct mm_struct;
-struct vm_area_struct;
-
 extern void __sync_icache_dcache(pte_t pteval, unsigned long addr);
 
 /*
@@ -246,7 +245,8 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
 	 * hardware updates of the pte (ptep_set_access_flags safely changes
 	 * valid ptes without going through an invalid entry).
 	 */
-	if (pte_valid(*ptep) && pte_valid(pte)) {
+	if (IS_ENABLED(CONFIG_DEBUG_VM) && pte_valid(*ptep) && pte_valid(pte) &&
+	   (mm == current->active_mm || atomic_read(&mm->mm_users) > 1)) {
 		VM_WARN_ONCE(!pte_young(pte),
 			     "%s: racy access flag clearing: 0x%016llx -> 0x%016llx",
 			     __func__, pte_val(*ptep), pte_val(pte));
-- 
2.1.4

^ permalink raw reply related

* [PATCH 1/2] net: dsa: allow XAUI phy interface mode
From: Andrew Lunn @ 2017-12-12 11:44 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <E1eOgsk-0001AU-5z@rmk-PC.armlinux.org.uk>

On Tue, Dec 12, 2017 at 09:29:46AM +0000, Russell King wrote:
> XGMII is a 32-bit bus plus two clock signals per direction.  XAUI is
> four serial lanes per direction.  The 88e6190 supports XAUI but not
> XGMII as it doesn't have enough pins.  The same is true of 88e6176.
> 
> Match on PHY_INTERFACE_MODE_XAUI for the XAUI port type, but keep
> accepting XGMII for backwards compatibility.
> 
> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>

Reviewed-by: Andrew Lunn <andrew@lunn.ch>

    Andrew

^ permalink raw reply

* [PATCH 2/2] ARM: dts: vf610-zii-dev: use XAUI for DSA link ports
From: Andrew Lunn @ 2017-12-12 11:44 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <E1eOgsp-0001Ab-9x@rmk-PC.armlinux.org.uk>

On Tue, Dec 12, 2017 at 09:29:51AM +0000, Russell King wrote:
> Use XAUI rather than XGMII for DSA link ports, as this is the interface
> mode that the switches actually use. XAUI is the 4 lane bus with clock
> per direction, whereas XGMII is a 32 bit bus with clock.
> 
> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>

Reviewed-by: Andrew Lunn <andrew@lunn.ch>

    Andrew

^ permalink raw reply

* [PATCH v10 1/3] ACPI/IORT: Add msi address regions reservation helper
From: Shameerali Kolothum Thodi @ 2017-12-12 11:51 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171211144504.GA4123@red-moon>



> -----Original Message-----
> From: Lorenzo Pieralisi [mailto:lorenzo.pieralisi at arm.com]
> Sent: Monday, December 11, 2017 2:45 PM
> To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>
> Cc: will.deacon at arm.com; robin.murphy at arm.com; marc.zyngier at arm.com;
> joro at 8bytes.org; John Garry <john.garry@huawei.com>; xuwei (O)
> <xuwei5@hisilicon.com>; Guohanjun (Hanjun Guo) <guohanjun@huawei.com>;
> iommu at lists.linux-foundation.org; linux-arm-kernel at lists.infradead.org; linux-
> acpi at vger.kernel.org; devicetree at vger.kernel.org; Linuxarm
> <linuxarm@huawei.com>
> Subject: Re: [PATCH v10 1/3] ACPI/IORT: Add msi address regions reservation
> helper
> 
> On Wed, Nov 29, 2017 at 02:14:47PM +0000, Shameer Kolothum wrote:
> > On some platforms msi parent address regions have to be excluded from
> > normal IOVA allocation in that they are detected and decoded in a HW
> > specific way by system components and so they cannot be considered normal
> > IOVA address space.
> >
> > Add a helper function that retrieves ITS address regions - the msi
> > parent - through IORT device <-> ITS mappings and reserves it so that
> > these regions will not be translated by IOMMU and will be excluded from
> > IOVA allocations. The function checks for the smmu model number and
> > only applies the msi reservation if the platform requires it.
> >
> > Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> > ---
> >  drivers/acpi/arm64/iort.c        | 133
> ++++++++++++++++++++++++++++++++++++++-
> >  drivers/irqchip/irq-gic-v3-its.c |   3 +-
> >  include/linux/acpi_iort.h        |   7 ++-
> >  3 files changed, 138 insertions(+), 5 deletions(-)
> >
> > diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
> > index 95255ec..1c5fc36 100644
> > --- a/drivers/acpi/arm64/iort.c
> > +++ b/drivers/acpi/arm64/iort.c
> > @@ -39,6 +39,7 @@
> >  struct iort_its_msi_chip {
> >  	struct list_head	list;
> >  	struct fwnode_handle	*fw_node;
> > +	phys_addr_t		base_addr;
> >  	u32			translation_id;
> >  };
> >
> > @@ -161,14 +162,16 @@ typedef acpi_status (*iort_find_node_callback)
> >  static DEFINE_SPINLOCK(iort_msi_chip_lock);
> >
> >  /**
> > - * iort_register_domain_token() - register domain token and related ITS ID
> > - * to the list from where we can get it back later on.
> > + * iort_register_domain_token() - register domain token along with related
> > + * ITS ID and base address to the list from where we can get it back later on.
> >   * @trans_id: ITS ID.
> > + * @base: ITS base address.
> >   * @fw_node: Domain token.
> >   *
> >   * Returns: 0 on success, -ENOMEM if no memory when allocating list
> element
> >   */
> > -int iort_register_domain_token(int trans_id, struct fwnode_handle
> *fw_node)
> > +int iort_register_domain_token(int trans_id, phys_addr_t base,
> > +			       struct fwnode_handle *fw_node)
> >  {
> >  	struct iort_its_msi_chip *its_msi_chip;
> >
> > @@ -178,6 +181,7 @@ int iort_register_domain_token(int trans_id, struct
> fwnode_handle *fw_node)
> >
> >  	its_msi_chip->fw_node = fw_node;
> >  	its_msi_chip->translation_id = trans_id;
> > +	its_msi_chip->base_addr = base;
> >
> >  	spin_lock(&iort_msi_chip_lock);
> >  	list_add(&its_msi_chip->list, &iort_msi_chip_list);
> > @@ -581,6 +585,24 @@ int iort_pmsi_get_dev_id(struct device *dev, u32
> *dev_id)
> >  	return -ENODEV;
> >  }
> >
> > +static int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base)
> > +{
> > +	struct iort_its_msi_chip *its_msi_chip;
> > +	bool match = false;
> > +
> > +	spin_lock(&iort_msi_chip_lock);
> > +	list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) {
> > +		if (its_msi_chip->translation_id == its_id) {
> > +			*base = its_msi_chip->base_addr;
> > +			match = true;
> > +			break;
> > +		}
> > +	}
> > +	spin_unlock(&iort_msi_chip_lock);
> > +
> > +	return match ? 0 : -ENODEV;
> 
> Nit: if you need to return an int, use it as the "match" variable,
> there is no point in using a bool.
> 
> int ret = -ENODEV;
> 
> if (its_msi_chip->translation_id == its_id) {
> 	...
> 	ret = 0;
> 	break;
> }
> 
> return ret;
> 
> > +}
> > +
> >  /**
> >   * iort_dev_find_its_id() - Find the ITS identifier for a device
> >   * @dev: The device.
> > @@ -740,6 +762,38 @@ static int __maybe_unused __get_pci_rid(struct
> pci_dev *pdev, u16 alias,
> >  	return 0;
> >  }
> >
> > +static bool __maybe_unused iort_hw_msi_resv_enable(struct device *dev,
> > +					struct acpi_iort_node *node)
> > +{
> > +	struct iort_fwnode *curr;
> > +	struct acpi_iort_node *iommu = NULL;
> > +	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
> > +
> > +	if (WARN_ON(!fwspec || !fwspec->iommu_fwnode))
> 
> Remove WARN_ON() here, it seems excessive.
> 
> > +		return false;
> > +
> > +	spin_lock(&iort_fwnode_lock);
> > +	list_for_each_entry(curr, &iort_fwnode_list, list) {
> > +		if (curr->fwnode == fwspec->iommu_fwnode) {
> > +			iommu = curr->iort_node;
> > +			break;
> > +		}
> > +	}
> > +	spin_unlock(&iort_fwnode_lock);
> 
> iort_get_iort_node() ?
> 
> > +	if (iommu && (iommu->type == ACPI_IORT_NODE_SMMU_V3)) {
> > +		struct acpi_iort_smmu_v3 *smmu;
> > +
> > +		smmu = (struct acpi_iort_smmu_v3 *)iommu->node_data;
> > +		if (smmu->model ==
> ACPI_IORT_SMMU_V3_HISILICON_HI161X) {
> > +			dev_notice(dev, "Enabling HiSilicon erratum
> 161010801\n");
> > +			return true;
> > +		}
> > +	}
> > +
> > +	return false;
> > +}
> > +
> >  static int arm_smmu_iort_xlate(struct device *dev, u32 streamid,
> >  			       struct fwnode_handle *fwnode,
> >  			       const struct iommu_ops *ops)
> > @@ -782,6 +836,77 @@ static inline int iort_add_device_replay(const struct
> iommu_ops *ops,
> >
> >  	return err;
> >  }
> > +
> > +/**
> > + * iort_iommu_msi_get_resv_regions - Reserved region driver helper
> > + * @dev: Device from iommu_get_resv_regions()
> > + * @head: Reserved region list from iommu_get_resv_regions()
> > + *
> > + * Returns: Number of msi reserved regions on success (0 if platform
> > + *          doesn't require the reservation or no associated msi regions),
> > + *          appropriate error value otherwise. The ITS interrupt translation
> > + *          space (ITS_base + 0x010000) associated with the device are the
> > + *          msi reserved regions.
> > + */
> > +int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head
> *head)
> > +{
> > +	struct acpi_iort_its_group *its;
> > +	struct acpi_iort_node *node, *its_node = NULL;
> > +	int i, resv = 0;
> > +
> > +	node = iort_find_dev_node(dev);
> > +	if (!node)
> > +		return -ENODEV;
> 
> You may not need node, see below.
> 
> > +	if (!iort_hw_msi_resv_enable(dev, node))
> 
> node is passed but effectively not used by the function.
> 
> > +		return 0;
> > +
> > +	/*
> > +	 * Current logic to reserve ITS regions relies on HW topologies
> > +	 * where a given PCI or named component maps its IDs to only one
> > +	 * ITS group; if a PCI or named component can map its IDs to
> > +	 * different ITS groups through IORT mappings this function has
> > +	 * to be reworked to ensure we reserve regions for all ITS groups
> > +	 * a given PCI or named component may map IDs to.
> > +	 */
> > +	if (dev_is_pci(dev)) {
> > +		u32 rid;
> > +
> > +		pci_for_each_dma_alias(to_pci_dev(dev), __get_pci_rid, &rid);
> > +		its_node = iort_node_map_id(node, rid, NULL,
> IORT_MSI_TYPE);
> > +	} else {
> > +		for (i = 0; i < node->mapping_count; i++) {
> > +			its_node = iort_node_map_platform_id(node, NULL,
> > +							 IORT_MSI_TYPE, i);
> > +			if (its_node)
> > +				break;
> > +		}
> > +	}
> 
> Ok. So, how about doing:
> 
> for (i = 0; i < dev->iommu_fwspec->num_ids; i++) {
> 	its_node = iort_node_map_id(iommu_iort_node, dev->iommu_fwspec-
> >ids[i],
> 				    NULL, IORT_MSI_TYPE);
> 	if (its_node)
> 		break;
> }
> 
> which removes the need for dev_is_pci() check and simplifies a bit, is
> there any issue with that ?
> 
> iommu_iort_node can be returned by iort_hw_msi_resv_enable(), that
> you can rename accordingly.

Thanks Lorenzo. I will address the comments and send the revised patch
after testing.

Much appreciated,
Shameer

^ permalink raw reply

* [PATCH 01/12] KVM: arm/arm64: vgic: Remove spurious call to kern_hyp_va
From: Steve Capper @ 2017-12-12 11:53 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <d8a26134-52e2-0800-542f-603c6e8347c7@arm.com>

On Mon, Dec 04, 2017 at 02:30:28PM +0000, Suzuki K Poulose wrote:
> On 04/12/17 14:13, Steve Capper wrote:
> > In save_elrsr(.), we use the following technique to ascertain the
> > address of the vgic global state:
> > 	(kern_hyp_va(&kvm_vgic_global_state))->nr_lr
> > 
> > For arm, kern_hyp_va(va) == va, and this call effectively compiles out.
> > 
> > For arm64, this call can be spurious as the address of kvm_vgic_global_state
> > will usually be determined by relative page/absolute page offset relocation
> > at link time. As the function is idempotent, having the call for arm64 does
> > not cause any problems.
> > 
> > Unfortunately, this is about to change for arm64 as we need to change
> > the logic of kern_hyp_va to allow for kernel addresses that are outside
> > the direct linear map.
> > 
> > This patch removes the call to kern_hyp_va, and ensures that correct
> > HYP addresses are computed via relative page offset addressing on arm64.
> > This is achieved by a custom accessor, hyp_address(.), which on arm is a
> > simple reference operator.
> 
> minor nit: I somehow feel that there word "symbol" should be part of the name of
> the macro, to make it implicit that it can only be used on a symbol and not any
> generic variable.
> 
> > diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h
> > index 08d3bb66c8b7..34a4ae906a97 100644
> > --- a/arch/arm64/include/asm/kvm_hyp.h
> > +++ b/arch/arm64/include/asm/kvm_hyp.h
> > @@ -25,6 +25,16 @@
> >   #define __hyp_text __section(.hyp.text) notrace
> > +#define hyp_address(symbol)				\
> > +({							\
> > +	typeof(&symbol) __ret;				\
> > +	asm volatile(					\
> > +	"adrp %[ptr], " #symbol	"\n"			\
> > +	"add %[ptr], %[ptr], :lo12:" #symbol "\n"	\
> > +	: [ptr] "=r"(__ret));				\
> > +	__ret;						\
> > +})
> > +
> 
> > -	addr  = kern_hyp_va((kern_hyp_va(&kvm_vgic_global_state))->vcpu_base_va);
> > +	addr  = kern_hyp_va(hyp_address(kvm_vgic_global_state)->vcpu_base_va);
> 
> e.g, Like here, why do we use hyp_address only for the kvm_vgic_global_state and not
> the dereferenced value. Having a name, say, hyp_symbol_address() makes it clear.
> 
> Otherwise, looks good to me.
> 

Thanks Suzuki,
Marc Zyngier has a similar patch in his series:
KVM/arm64: Randomise EL2 mappings

I'll refactor my series to apply on top of Marc's
(and take advantage of the simiplified HYP logic)

Cheers,
-- 
Steve

^ permalink raw reply

* [PATCH 02/41] drm/rockchip: Get rid of some unnecessary code
From: Heiko Stuebner @ 2017-12-12 12:25 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170310043305.17216-3-seanpaul@chromium.org>

Am Donnerstag, 9. M?rz 2017, 23:32:17 CET schrieb Sean Paul:
> From: Tomasz Figa <tfiga@chromium.org>
> 
> Current code implements prepare_fb and cleanup_fb callbacks only to
> grab/release fb references, which is already done by atomic framework
> when creating/destryoing plane state. Also there are some unused fields
> vop and vop_win structs. Let's remove these unused bits.
> 
> Signed-off-by: Tomasz Figa <tfiga@chromium.org>
> Signed-off-by: Sean Paul <seanpaul@chromium.org>

There seems to be a slight discrepancy between patch message and code.
The commit message talks about *_fb-callbacks, while the patch removes
a seemingly unsused mutex, which I guess falls under the "some unused fields"
part.

In any case, I didn't see any ill effects when testing this patch, so
Tested-by: Heiko Stuebner <heiko@sntech.de>

> ---
>  drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 5 -----
>  1 file changed, 5 deletions(-)
> 
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> index 2151e1cee4b4..0539afb6b7c8 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> @@ -108,9 +108,6 @@ struct vop {
>  	struct drm_device *drm_dev;
>  	bool is_enabled;
>  
> -	/* mutex vsync_ work */
> -	struct mutex vsync_mutex;
> -	bool vsync_work_pending;
>  	struct completion dsp_hold_completion;
>  
>  	/* protected by dev->event_lock */
> @@ -1592,8 +1589,6 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
>  	spin_lock_init(&vop->reg_lock);
>  	spin_lock_init(&vop->irq_lock);
>  
> -	mutex_init(&vop->vsync_mutex);
> -
>  	ret = devm_request_irq(dev, vop->irq, vop_isr,
>  			       IRQF_SHARED, dev_name(dev), vop);
>  	if (ret)
> 

^ permalink raw reply

* [PATCH 00/10] clk: qcom: CPU clock driver for msm8996
From: Ilia Lin @ 2017-12-12 12:31 UTC (permalink / raw)
  To: linux-arm-kernel

This series adds support for the CPU clocks on msm8996 devices.
A previous post of RFC can be found here
https://patchwork.kernel.org/patch/9662917/

Ilia Lin (5):
  soc: qcom: Separate kryo l2 accessors from PMU driver
  clk: qcom: clk-cpu-8996: Prepare PLLs on probe
  clk: qcom: Add ACD path to CPU clock driver for msm8996
  DT: QCOM: Add cpufreq-dt to msm8996
  DT: QCOM: Add thermal mitigation to msm8996

Rajendra Nayak (4):
  clk: qcom: Make clk_alpha_pll_configure available to modules
  clk: qcom: Add CPU clock driver for msm8996
  clk: qcom: cpu-8996: Add support to switch to alternate PLL
  clk: qcom: cpu-8996: Add support to switch below 600Mhz

Taniya Das (1):
  clk: qcom: Fix .set_rate to handle alpha PLLs w/wo dynamic update

 .../devicetree/bindings/clock/qcom,kryocc.txt      |  17 +
 arch/arm64/Kconfig.platforms                       |   7 +
 arch/arm64/boot/dts/qcom/apq8096-db820c.dts        |   2 +-
 arch/arm64/boot/dts/qcom/msm8996.dtsi              | 282 ++++++++++-
 drivers/clk/qcom/Kconfig                           |   8 +
 drivers/clk/qcom/Makefile                          |   1 +
 drivers/clk/qcom/clk-alpha-pll.c                   |  72 ++-
 drivers/clk/qcom/clk-alpha-pll.h                   |   5 +
 drivers/clk/qcom/clk-cpu-8996.c                    | 542 +++++++++++++++++++++
 drivers/cpufreq/cpufreq-dt-platdev.c               |   3 +
 drivers/perf/qcom_l2_pmu.c                         |  48 +-
 drivers/soc/qcom/Makefile                          |   1 +
 drivers/soc/qcom/kryo-l2-accessors.c               |  64 +++
 include/soc/qcom/kryo-l2-accessors.h               |  27 +
 14 files changed, 1009 insertions(+), 70 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/clock/qcom,kryocc.txt
 create mode 100644 drivers/clk/qcom/clk-cpu-8996.c
 create mode 100644 drivers/soc/qcom/kryo-l2-accessors.c
 create mode 100644 include/soc/qcom/kryo-l2-accessors.h

-- 
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation

-- 
1.9.1

^ permalink raw reply

* [PATCH 01/10] soc: qcom: Separate kryo l2 accessors from PMU driver
From: Ilia Lin @ 2017-12-12 12:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1513081897-31612-1-git-send-email-ilialin@codeaurora.org>

The driver provides kernel level API for other drivers
to access the MSM8996 L2 cache registers.
Separating the L2 access code from the PMU driver and
making it public to allow other drivers use it.
The accesses must be separated with a single spinlock,
maintained in this driver.

Signed-off-by: Ilia Lin <ilialin@codeaurora.org>
---
 arch/arm64/Kconfig.platforms         |  7 ++++
 drivers/perf/qcom_l2_pmu.c           | 48 +--------------------------
 drivers/soc/qcom/Makefile            |  1 +
 drivers/soc/qcom/kryo-l2-accessors.c | 64 ++++++++++++++++++++++++++++++++++++
 include/soc/qcom/kryo-l2-accessors.h | 27 +++++++++++++++
 5 files changed, 100 insertions(+), 47 deletions(-)
 create mode 100644 drivers/soc/qcom/kryo-l2-accessors.c
 create mode 100644 include/soc/qcom/kryo-l2-accessors.h

diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms
index 2401373..a6a6504 100644
--- a/arch/arm64/Kconfig.platforms
+++ b/arch/arm64/Kconfig.platforms
@@ -144,6 +144,13 @@ config ARCH_REALTEK
 	  This enables support for the ARMv8 based Realtek chipsets,
 	  like the RTD1295.
 
+config ARCH_MSM8996
+    bool "Enable Support for Qualcomm Technologies, Inc. MSM8996"
+	depends on ARCH_QCOM
+	help
+	This enables support for the MSM8996 chipset. If you do not
+	wish to build a kernel that runs on this chipset, say 'N' here.
+
 config ARCH_ROCKCHIP
 	bool "Rockchip Platforms"
 	select ARCH_HAS_RESET_CONTROLLER
diff --git a/drivers/perf/qcom_l2_pmu.c b/drivers/perf/qcom_l2_pmu.c
index 4fdc848..ba5f241 100644
--- a/drivers/perf/qcom_l2_pmu.c
+++ b/drivers/perf/qcom_l2_pmu.c
@@ -30,7 +30,7 @@
 
 #include <asm/barrier.h>
 #include <asm/local64.h>
-#include <asm/sysreg.h>
+#include <soc/qcom/kryo-l2-accessors.h>
 
 #define MAX_L2_CTRS             9
 
@@ -87,9 +87,6 @@
 #define L2_COUNTER_RELOAD       BIT_ULL(31)
 #define L2_CYCLE_COUNTER_RELOAD BIT_ULL(63)
 
-#define L2CPUSRSELR_EL1         sys_reg(3, 3, 15, 0, 6)
-#define L2CPUSRDR_EL1           sys_reg(3, 3, 15, 0, 7)
-
 #define reg_idx(reg, i)         (((i) * IA_L2_REG_OFFSET) + reg##_BASE)
 
 /*
@@ -107,49 +104,6 @@
 #define L2_EVENT_STREX                     0x421
 #define L2_EVENT_CLREX                     0x422
 
-static DEFINE_RAW_SPINLOCK(l2_access_lock);
-
-/**
- * set_l2_indirect_reg: write value to an L2 register
- * @reg: Address of L2 register.
- * @value: Value to be written to register.
- *
- * Use architecturally required barriers for ordering between system register
- * accesses
- */
-static void set_l2_indirect_reg(u64 reg, u64 val)
-{
-	unsigned long flags;
-
-	raw_spin_lock_irqsave(&l2_access_lock, flags);
-	write_sysreg_s(reg, L2CPUSRSELR_EL1);
-	isb();
-	write_sysreg_s(val, L2CPUSRDR_EL1);
-	isb();
-	raw_spin_unlock_irqrestore(&l2_access_lock, flags);
-}
-
-/**
- * get_l2_indirect_reg: read an L2 register value
- * @reg: Address of L2 register.
- *
- * Use architecturally required barriers for ordering between system register
- * accesses
- */
-static u64 get_l2_indirect_reg(u64 reg)
-{
-	u64 val;
-	unsigned long flags;
-
-	raw_spin_lock_irqsave(&l2_access_lock, flags);
-	write_sysreg_s(reg, L2CPUSRSELR_EL1);
-	isb();
-	val = read_sysreg_s(L2CPUSRDR_EL1);
-	raw_spin_unlock_irqrestore(&l2_access_lock, flags);
-
-	return val;
-}
-
 struct cluster_pmu;
 
 /*
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 40c56f6..2bf8d93 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o
 obj-$(CONFIG_QCOM_SMP2P)	+= smp2p.o
 obj-$(CONFIG_QCOM_SMSM)	+= smsm.o
 obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o
+obj-$(CONFIG_ARCH_MSM8996) +=	kryo-l2-accessors.o
diff --git a/drivers/soc/qcom/kryo-l2-accessors.c b/drivers/soc/qcom/kryo-l2-accessors.c
new file mode 100644
index 0000000..6be57e6
--- /dev/null
+++ b/drivers/soc/qcom/kryo-l2-accessors.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2014-2015, 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/spinlock.h>
+#include <asm/sysreg.h>
+#include <soc/qcom/kryo-l2-accessors.h>
+
+#define L2CPUSRSELR_EL1         sys_reg(3, 3, 15, 0, 6)
+#define L2CPUSRDR_EL1           sys_reg(3, 3, 15, 0, 7)
+
+static DEFINE_RAW_SPINLOCK(l2_access_lock);
+
+/**
+ * set_l2_indirect_reg: write value to an L2 register
+ * @reg: Address of L2 register.
+ * @value: Value to be written to register.
+ *
+ * Use architecturally required barriers for ordering between system register
+ * accesses, and system registers with respect to device memory
+ */
+void set_l2_indirect_reg(u64 reg, u64 val)
+{
+	unsigned long flags;
+	mb();
+	raw_spin_lock_irqsave(&l2_access_lock, flags);
+	write_sysreg_s(reg, L2CPUSRSELR_EL1);
+	isb();
+	write_sysreg_s(val, L2CPUSRDR_EL1);
+	isb();
+	raw_spin_unlock_irqrestore(&l2_access_lock, flags);
+}
+EXPORT_SYMBOL(set_l2_indirect_reg);
+
+/**
+ * get_l2_indirect_reg: read an L2 register value
+ * @reg: Address of L2 register.
+ *
+ * Use architecturally required barriers for ordering between system register
+ * accesses, and system registers with respect to device memory
+ */
+u64 get_l2_indirect_reg(u64 reg)
+{
+	u64 val;
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&l2_access_lock, flags);
+	write_sysreg_s(reg, L2CPUSRSELR_EL1);
+	isb();
+	val = read_sysreg_s(L2CPUSRDR_EL1);
+	raw_spin_unlock_irqrestore(&l2_access_lock, flags);
+
+	return val;
+}
+EXPORT_SYMBOL(get_l2_indirect_reg);
diff --git a/include/soc/qcom/kryo-l2-accessors.h b/include/soc/qcom/kryo-l2-accessors.h
new file mode 100644
index 0000000..2bebce1
--- /dev/null
+++ b/include/soc/qcom/kryo-l2-accessors.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ASM_ARCH_MSM_MSM_KRYO_L2_ACCESSORS_H
+#define __ASM_ARCH_MSM_MSM_KRYO_L2_ACCESSORS_H
+
+#ifdef CONFIG_ARCH_QCOM
+void set_l2_indirect_reg(u64 reg_addr, u64 val);
+u64 get_l2_indirect_reg(u64 reg_addr);
+#else
+static inline void set_l2_indirect_reg(u32 reg_addr, u32 val) {}
+static inline u32 get_l2_indirect_reg(u32 reg_addr)
+{
+	return 0;
+}
+#endif
+#endif
-- 
1.9.1

^ permalink raw reply related

* [PATCH 02/10] clk: qcom: Fix .set_rate to handle alpha PLLs w/wo dynamic update
From: Ilia Lin @ 2017-12-12 12:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1513081897-31612-1-git-send-email-ilialin@codeaurora.org>

From: Taniya Das <tdas@codeaurora.org>

From: Taniya Das <tdas@codeaurora.org>

Alpha PLLs which do not support dynamic update feature
need to be explicitly disabled before a rate change.
The ones which do support dynamic update do so within a
single vco range, so add a min/max freq check for such
PLLs so they fall in the vco range.

Signed-off-by: Taniya Das <tdas@codeaurora.org>
Signed-off-by: Rajendra Nayak <rnayak@codeaurora.org>
Signed-off-by: Ilia Lin <ilialin@codeaurora.org>
---
 drivers/clk/qcom/clk-alpha-pll.c | 71 +++++++++++++++++++++++++++++++++-------
 drivers/clk/qcom/clk-alpha-pll.h |  5 +++
 2 files changed, 65 insertions(+), 11 deletions(-)

diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c
index 47a1da3..ecb9e7f 100644
--- a/drivers/clk/qcom/clk-alpha-pll.c
+++ b/drivers/clk/qcom/clk-alpha-pll.c
@@ -376,19 +376,46 @@ static unsigned long alpha_pll_calc_rate(u64 prate, u32 l, u32 a)
 	return alpha_pll_calc_rate(prate, l, a);
 }
 
-static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
-				  unsigned long prate)
+static int alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+			      unsigned long prate,
+			      int (*enable)(struct clk_hw *hw),
+			      void (*disable)(struct clk_hw *hw))
 {
+	bool enabled;
 	struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
 	const struct pll_vco *vco;
 	u32 l, off = pll->offset;
 	u64 a;
 
 	rate = alpha_pll_round_rate(rate, prate, &l, &a);
-	vco = alpha_pll_find_vco(pll, rate);
-	if (!vco) {
-		pr_err("alpha pll not in a valid vco range\n");
-		return -EINVAL;
+	enabled = clk_hw_is_enabled(hw);
+
+	if (pll->flags & SUPPORTS_DYNAMIC_UPDATE) {
+		/*
+		 * PLLs which support dynamic updates support one single
+		 * vco range, between min_rate and max_rate supported
+		 */
+		if (rate < pll->min_rate || rate > pll->max_rate) {
+			pr_err("alpha pll rate outside supported min/max range\n");
+			return -EINVAL;
+		}
+	} else {
+		/*
+		 * All alpha PLLs which do not support dynamic update,
+		 * should be disabled before a vco update.
+		 */
+		if (enabled)
+			disable(hw);
+
+		vco = alpha_pll_find_vco(pll, rate);
+		if (!vco) {
+			pr_err("alpha pll not in a valid vco range\n");
+			return -EINVAL;
+		}
+
+		regmap_update_bits(pll->clkr.regmap, off + PLL_USER_CTL,
+				   PLL_VCO_MASK << PLL_VCO_SHIFT,
+				   vco->val << PLL_VCO_SHIFT);
 	}
 
 	regmap_write(pll->clkr.regmap, off + PLL_L_VAL, l);
@@ -401,16 +428,29 @@ static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
 		regmap_write(pll->clkr.regmap, off + PLL_ALPHA_VAL_U, a >> 32);
 	}
 
-	regmap_update_bits(pll->clkr.regmap, off + PLL_USER_CTL,
-			   PLL_VCO_MASK << PLL_VCO_SHIFT,
-			   vco->val << PLL_VCO_SHIFT);
-
 	regmap_update_bits(pll->clkr.regmap, off + PLL_USER_CTL, PLL_ALPHA_EN,
 			   PLL_ALPHA_EN);
 
+	if (!(pll->flags & SUPPORTS_DYNAMIC_UPDATE) && enabled)
+		enable(hw);
+
 	return 0;
 }
 
+static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+				  unsigned long prate)
+{
+	return alpha_pll_set_rate(hw, rate, prate, clk_alpha_pll_enable,
+				  clk_alpha_pll_disable);
+}
+
+static int clk_alpha_pll_hwfsm_set_rate(struct clk_hw *hw, unsigned long rate,
+					unsigned long prate)
+{
+	return alpha_pll_set_rate(hw, rate, prate, clk_alpha_pll_hwfsm_enable,
+				  clk_alpha_pll_hwfsm_disable);
+}
+
 static long clk_alpha_pll_round_rate(struct clk_hw *hw, unsigned long rate,
 				     unsigned long *prate)
 {
@@ -420,6 +460,15 @@ static long clk_alpha_pll_round_rate(struct clk_hw *hw, unsigned long rate,
 	unsigned long min_freq, max_freq;
 
 	rate = alpha_pll_round_rate(rate, *prate, &l, &a);
+
+	if (pll->flags & SUPPORTS_DYNAMIC_UPDATE) {
+		if (rate < pll->min_rate)
+			rate = pll->min_rate;
+		else if (rate > pll->max_rate)
+			rate = pll->max_rate;
+		return rate;
+	}
+
 	if (alpha_pll_find_vco(pll, rate))
 		return rate;
 
@@ -445,7 +494,7 @@ static long clk_alpha_pll_round_rate(struct clk_hw *hw, unsigned long rate,
 	.is_enabled = clk_alpha_pll_hwfsm_is_enabled,
 	.recalc_rate = clk_alpha_pll_recalc_rate,
 	.round_rate = clk_alpha_pll_round_rate,
-	.set_rate = clk_alpha_pll_set_rate,
+	.set_rate = clk_alpha_pll_hwfsm_set_rate,
 };
 EXPORT_SYMBOL_GPL(clk_alpha_pll_hwfsm_ops);
 
diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h
index d6e1ee2..7aaa11c 100644
--- a/drivers/clk/qcom/clk-alpha-pll.h
+++ b/drivers/clk/qcom/clk-alpha-pll.h
@@ -27,6 +27,8 @@ struct pll_vco {
  * struct clk_alpha_pll - phase locked loop (PLL)
  * @offset: base address of registers
  * @vco_table: array of VCO settings
+ * @min_rate: Minimim rate for PLLs with single VCO range
+ * @max_rate: Maximun rate for PLLs with single VCO range
  * @clkr: regmap clock handle
  */
 struct clk_alpha_pll {
@@ -37,8 +39,11 @@ struct clk_alpha_pll {
 #define SUPPORTS_OFFLINE_REQ	BIT(0)
 #define SUPPORTS_16BIT_ALPHA	BIT(1)
 #define SUPPORTS_FSM_MODE	BIT(2)
+#define SUPPORTS_DYNAMIC_UPDATE	BIT(3)
 	u8 flags;
 
+	unsigned long min_rate;
+	unsigned long max_rate;
 	struct clk_regmap clkr;
 };
 
-- 
1.9.1

^ permalink raw reply related

* [PATCH 03/10] clk: qcom: Make clk_alpha_pll_configure available to modules
From: Ilia Lin @ 2017-12-12 12:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1513081897-31612-1-git-send-email-ilialin@codeaurora.org>

From: Rajendra Nayak <rnayak@codeaurora.org>

Allow clk_alpha_pll_configure to be called from loadable
kernel modules.

Signed-off-by: Rajendra Nayak <rnayak@codeaurora.org>
Signed-off-by: Ilia Lin <ilialin@codeaurora.org>
---
 drivers/clk/qcom/clk-alpha-pll.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c
index ecb9e7f..7b06e21 100644
--- a/drivers/clk/qcom/clk-alpha-pll.c
+++ b/drivers/clk/qcom/clk-alpha-pll.c
@@ -141,6 +141,7 @@ void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
 	if (pll->flags & SUPPORTS_FSM_MODE)
 		qcom_pll_set_fsm_mode(regmap, off + PLL_MODE, 6, 0);
 }
+EXPORT_SYMBOL_GPL(clk_alpha_pll_configure);
 
 static int clk_alpha_pll_hwfsm_enable(struct clk_hw *hw)
 {
-- 
1.9.1

^ permalink raw reply related

* [PATCH 04/10] clk: qcom: Add CPU clock driver for msm8996
From: Ilia Lin @ 2017-12-12 12:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1513081897-31612-1-git-send-email-ilialin@codeaurora.org>

From: Rajendra Nayak <rnayak@codeaurora.org>

Each of the CPU clusters (Power and Perf) on msm8996 are
clocked via 2 PLLs, a primary and alternate. There are also
2 Mux'es, a primary and secondary all connected together
as shown below

                             +-------+
              XO             |       |
          +------------------>0      |
                             |       |
                   PLL/2     | SMUX  +----+
                     +------->1      |    |
                     |       |       |    |
                     |       +-------+    |    +-------+
                     |                    +---->0      |
                     |                         |       |
+---------------+    |             +----------->1      | CPU clk
|Primary PLL    +----+ PLL_EARLY   |           |       +------>
|               +------+-----------+    +------>2 PMUX |
+---------------+      |                |      |       |
                       |   +------+     |   +-->3      |
                       +--^+  ACD +-----+   |  +-------+
+---------------+          +------+         |
|Alt PLL        |                           |
|               +---------------------------+
+---------------+         PLL_EARLY

The primary PLL is what drives the CPU clk, except for times
when we are reprogramming the PLL itself (for rate changes) when
we temporarily switch to an alternate PLL. A subsequent patch adds
support to switch between primary and alternate PLL during rate
changes.

The primary PLL operates on a single VCO range, between 600Mhz
and 3Ghz. However the CPUs do support OPPs with frequencies
between 300Mhz and 600Mhz. In order to support running the CPUs
at those frequencies we end up having to lock the PLL at twice
the rate and drive the CPU clk via the PLL/2 output and SMUX.

So for frequencies above 600Mhz we follow the following path
 Primary PLL --> PLL_EARLY --> PMUX(1) --> CPU clk
and for frequencies between 300Mhz and 600Mhz we follow
 Primary PLL --> PLL/2 --> SMUX(1) --> PMUX(0) --> CPU clk
Support for this is added in a subsequent patch as well.

ACD stands for Adaptive Clock Distribution and is used to
detect voltage droops. We do not add support for ACD as yet.
This can be added at a later point as needed.

Signed-off-by: Rajendra Nayak <rnayak@codeaurora.org>
Signed-off-by: Ilia Lin <ilialin@codeaurora.org>
---
 .../devicetree/bindings/clock/qcom,kryocc.txt      |  17 +
 drivers/clk/qcom/Kconfig                           |   8 +
 drivers/clk/qcom/Makefile                          |   1 +
 drivers/clk/qcom/clk-cpu-8996.c                    | 388 +++++++++++++++++++++
 4 files changed, 414 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/qcom,kryocc.txt
 create mode 100644 drivers/clk/qcom/clk-cpu-8996.c

diff --git a/Documentation/devicetree/bindings/clock/qcom,kryocc.txt b/Documentation/devicetree/bindings/clock/qcom,kryocc.txt
new file mode 100644
index 0000000..c45de03
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/qcom,kryocc.txt
@@ -0,0 +1,17 @@
+Qualcomm CPUSS clock controller for Kryo CPUs
+----------------------------------------------------
+
+Required properties :
+- compatible : shall contain only one of the following:
+
+			"qcom,apcc-msm8996"
+
+- reg : shall contain base register location and length
+- #clock-cells : shall contain 1
+
+Example:
+	kryocc: clock-controller at 6400000 {
+		compatible = "qcom,apcc-msm8996";
+		reg = <0x6400000 0x90000>;
+		#clock-cells = <1>;
+	};
diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
index 9f6c278..124fb9a4 100644
--- a/drivers/clk/qcom/Kconfig
+++ b/drivers/clk/qcom/Kconfig
@@ -196,3 +196,11 @@ config MSM_MMCC_8996
 	  Support for the multimedia clock controller on msm8996 devices.
 	  Say Y if you want to support multimedia devices such as display,
 	  graphics, video encode/decode, camera, etc.
+
+config MSM_APCC_8996
+	tristate "MSM8996 CPU Clock Controller"
+	depends on COMMON_CLK_QCOM
+	help
+	  Support for the CPU clock controller on msm8996 devices.
+	  Say Y if you want to support CPU clock scaling using CPUfreq
+	  drivers for dyanmic power management.
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 26410d3..b964f43 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -32,5 +32,6 @@ obj-$(CONFIG_MSM_LCC_8960) += lcc-msm8960.o
 obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
 obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
 obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o
+obj-$(CONFIG_MSM_APCC_8996) += clk-cpu-8996.o
 obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o
 obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
diff --git a/drivers/clk/qcom/clk-cpu-8996.c b/drivers/clk/qcom/clk-cpu-8996.c
new file mode 100644
index 0000000..bc60111
--- /dev/null
+++ b/drivers/clk/qcom/clk-cpu-8996.c
@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "clk-alpha-pll.h"
+
+#define VCO(a, b, c) { \
+	.val = a,\
+	.min_freq = b,\
+	.max_freq = c,\
+}
+
+#define DIV_2_INDEX		0
+#define PLL_INDEX		1
+#define ACD_INDEX		2
+#define ALT_INDEX		3
+
+/* PLLs */
+
+static const struct alpha_pll_config hfpll_config = {
+	.l = 60,
+	.config_ctl_val = 0x200d4828,
+	.config_ctl_hi_val = 0x006,
+	.pre_div_mask = BIT(12),
+	.post_div_mask = 0x3 << 8,
+	.main_output_mask = BIT(0),
+	.early_output_mask = BIT(3),
+};
+
+static struct clk_alpha_pll perfcl_pll = {
+	.offset = 0x80000,
+	.min_rate = 600000000,
+	.max_rate = 3000000000,
+	.flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_16BIT_ALPHA
+			| SUPPORTS_FSM_MODE,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "perfcl_pll",
+		.parent_names = (const char *[]){ "xo" },
+		.num_parents = 1,
+		.ops = &clk_alpha_pll_hwfsm_ops,
+	},
+};
+
+static struct clk_alpha_pll pwrcl_pll = {
+	.offset = 0x0,
+	.min_rate = 600000000,
+	.max_rate = 3000000000,
+	.flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_16BIT_ALPHA
+			| SUPPORTS_FSM_MODE,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "pwrcl_pll",
+		.parent_names = (const char *[]){ "xo" },
+		.num_parents = 1,
+		.ops = &clk_alpha_pll_hwfsm_ops,
+	},
+};
+
+static const struct pll_vco alt_pll_vco_modes[] = {
+	VCO(3,  250000000,  500000000),
+	VCO(2,  500000000,  750000000),
+	VCO(1,  750000000, 1000000000),
+	VCO(0, 1000000000, 2150400000),
+};
+
+static const struct alpha_pll_config altpll_config = {
+	.l = 16,
+	.vco_val = 0x3 << 20,
+	.vco_mask = 0x3 << 20,
+	.config_ctl_val = 0x4001051b,
+	.post_div_mask = 0x3 << 8,
+	.post_div_val = 0x1,
+	.main_output_mask = BIT(0),
+	.early_output_mask = BIT(3),
+};
+
+static struct clk_alpha_pll perfcl_alt_pll = {
+	.offset = 0x80100,
+	.vco_table = alt_pll_vco_modes,
+	.num_vco = ARRAY_SIZE(alt_pll_vco_modes),
+	.flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE,
+	.clkr.hw.init = &(struct clk_init_data) {
+		.name = "perfcl_alt_pll",
+		.parent_names = (const char *[]){ "xo" },
+		.num_parents = 1,
+		.ops = &clk_alpha_pll_hwfsm_ops,
+	},
+};
+
+static struct clk_alpha_pll pwrcl_alt_pll = {
+	.offset = 0x100,
+	.vco_table = alt_pll_vco_modes,
+	.num_vco = ARRAY_SIZE(alt_pll_vco_modes),
+	.flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE,
+	.clkr.hw.init = &(struct clk_init_data) {
+		.name = "pwrcl_alt_pll",
+		.parent_names = (const char *[]){ "xo" },
+		.num_parents = 1,
+		.ops = &clk_alpha_pll_hwfsm_ops,
+	},
+};
+
+/* Mux'es */
+
+struct clk_cpu_8996_mux {
+	u32	reg;
+	u32	shift;
+	u32	width;
+	struct clk_hw	*pll;
+	struct clk_regmap clkr;
+};
+
+static inline
+struct clk_cpu_8996_mux *to_clk_cpu_8996_mux_hw(struct clk_hw *hw)
+{
+	return container_of(to_clk_regmap(hw), struct clk_cpu_8996_mux, clkr);
+}
+
+static u8 clk_cpu_8996_mux_get_parent(struct clk_hw *hw)
+{
+	unsigned int val;
+	struct clk_regmap *clkr = to_clk_regmap(hw);
+	struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw);
+	unsigned int mask = GENMASK(cpuclk->width - 1, 0);
+
+	regmap_read(clkr->regmap, cpuclk->reg, &val);
+
+	val >>= cpuclk->shift;
+	val &= mask;
+
+	return val;
+}
+
+static int clk_cpu_8996_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+	unsigned int val;
+	struct clk_regmap *clkr = to_clk_regmap(hw);
+	struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw);
+	unsigned int mask = GENMASK(cpuclk->width + cpuclk->shift - 1,
+				    cpuclk->shift);
+
+	val = index;
+	val = cpuclk->shift;
+
+	return regmap_update_bits(clkr->regmap, cpuclk->reg, mask, val);
+}
+
+static int
+clk_cpu_8996_mux_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
+{
+	struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw);
+	struct clk_hw *parent = cpuclk->pll;
+
+	if (!cpuclk->pll)
+		return -EINVAL;
+
+	req->best_parent_rate = clk_hw_round_rate(parent, req->rate);
+	req->best_parent_hw = parent;
+
+	return 0;
+}
+
+const struct clk_ops clk_cpu_8996_mux_ops = {
+	.set_parent = clk_cpu_8996_mux_set_parent,
+	.get_parent = clk_cpu_8996_mux_get_parent,
+	.determine_rate = clk_cpu_8996_mux_determine_rate,
+};
+
+static struct clk_cpu_8996_mux pwrcl_smux = {
+	.reg = 0x40,
+	.shift = 2,
+	.width = 2,
+	.clkr.hw.init = &(struct clk_init_data) {
+		.name = "pwrcl_smux",
+		.parent_names = (const char *[]){
+			"xo",
+			"pwrcl_pll_main",
+		},
+		.num_parents = 2,
+		.ops = &clk_cpu_8996_mux_ops,
+		.flags = CLK_SET_RATE_PARENT,
+	},
+};
+
+static struct clk_cpu_8996_mux perfcl_smux = {
+	.reg = 0x80040,
+	.shift = 2,
+	.width = 2,
+	.clkr.hw.init = &(struct clk_init_data) {
+		.name = "perfcl_smux",
+		.parent_names = (const char *[]){
+			"xo",
+			"perfcl_pll_main",
+		},
+		.num_parents = 2,
+		.ops = &clk_cpu_8996_mux_ops,
+		.flags = CLK_SET_RATE_PARENT,
+	},
+};
+
+static struct clk_cpu_8996_mux pwrcl_pmux = {
+	.reg = 0x40,
+	.shift = 0,
+	.width = 2,
+	.pll = &pwrcl_pll.clkr.hw,
+	.clkr.hw.init = &(struct clk_init_data) {
+		.name = "pwrcl_pmux",
+		.parent_names = (const char *[]){
+			"pwrcl_smux",
+			"pwrcl_pll",
+			"pwrcl_pll_acd",
+			"pwrcl_alt_pll",
+		},
+		.num_parents = 4,
+		.ops = &clk_cpu_8996_mux_ops,
+		.flags = CLK_SET_RATE_PARENT,
+	},
+};
+
+static struct clk_cpu_8996_mux perfcl_pmux = {
+	.reg = 0x80040,
+	.shift = 0,
+	.width = 2,
+	.pll = &perfcl_pll.clkr.hw,
+	.clkr.hw.init = &(struct clk_init_data) {
+		.name = "perfcl_pmux",
+		.parent_names = (const char *[]){
+			"perfcl_smux",
+			"perfcl_pll",
+			"pwrcl_pll_acd",
+			"perfcl_alt_pll",
+		},
+		.num_parents = 4,
+		.ops = &clk_cpu_8996_mux_ops,
+		.flags = CLK_SET_RATE_PARENT,
+	},
+};
+
+static const struct regmap_config cpu_msm8996_regmap_config = {
+	.reg_bits		= 32,
+	.reg_stride		= 4,
+	.val_bits		= 32,
+	.max_register		= 0x80210,
+	.fast_io		= true,
+	.val_format_endian	= REGMAP_ENDIAN_LITTLE,
+};
+
+static const struct of_device_id match_table[] = {
+	{ .compatible = "qcom,apcc-msm8996" },
+	{}
+};
+
+struct clk_regmap *clks[] = {
+	/* PLLs */
+	&perfcl_pll.clkr,
+	&pwrcl_pll.clkr,
+	&perfcl_alt_pll.clkr,
+	&pwrcl_alt_pll.clkr,
+	/* MUXs */
+	&perfcl_smux.clkr,
+	&pwrcl_smux.clkr,
+	&perfcl_pmux.clkr,
+	&pwrcl_pmux.clkr,
+};
+
+struct clk_hw_clks {
+	unsigned int num;
+	struct clk_hw *hws[];
+};
+
+static int
+qcom_cpu_clk_msm8996_register_clks(struct device *dev, struct clk_hw_clks *hws,
+				   struct regmap *regmap)
+{
+	int i, ret;
+
+	hws->hws[0] = clk_hw_register_fixed_factor(dev, "perfcl_pll_main",
+						   "perfcl_pll",
+						   CLK_SET_RATE_PARENT, 1, 2);
+	perfcl_smux.pll = hws->hws[0];
+
+	hws->hws[1] = clk_hw_register_fixed_factor(dev, "pwrcl_pll_main",
+						   "pwrcl_pll",
+						   CLK_SET_RATE_PARENT, 1, 2);
+	pwrcl_smux.pll = hws->hws[1];
+
+	hws->num = 2;
+
+	for (i = 0; i < ARRAY_SIZE(clks); i++) {
+		ret = devm_clk_register_regmap(dev, clks[i]);
+		if (ret)
+			return ret;
+	}
+
+	clk_alpha_pll_configure(&perfcl_pll, regmap, &hfpll_config);
+	clk_alpha_pll_configure(&pwrcl_pll, regmap, &hfpll_config);
+	clk_alpha_pll_configure(&perfcl_alt_pll, regmap, &altpll_config);
+	clk_alpha_pll_configure(&pwrcl_alt_pll, regmap, &altpll_config);
+
+	return ret;
+}
+
+static int qcom_cpu_clk_msm8996_driver_probe(struct platform_device *pdev)
+{
+	int ret;
+	void __iomem *base;
+	struct resource *res;
+	struct regmap *regmap_cpu;
+	struct clk_hw_clks *hws;
+	struct clk_hw_onecell_data *data;
+	struct device *dev = &pdev->dev;
+	struct device_node *node = dev->of_node;
+
+	data = devm_kzalloc(dev, sizeof(*data) + 2 * sizeof(struct clk_hw *),
+			    GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	hws = devm_kzalloc(dev, sizeof(*hws) + 2 * sizeof(struct clk_hw *),
+			   GFP_KERNEL);
+	if (!hws)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	regmap_cpu = devm_regmap_init_mmio(dev, base,
+					   &cpu_msm8996_regmap_config);
+	if (IS_ERR(regmap_cpu))
+		return PTR_ERR(regmap_cpu);
+
+	ret = qcom_cpu_clk_msm8996_register_clks(dev, hws, regmap_cpu);
+	if (ret)
+		return ret;
+
+	data->hws[0] = &pwrcl_pmux.clkr.hw;
+	data->hws[1] = &perfcl_pmux.clkr.hw;
+
+	data->num = 2;
+
+	platform_set_drvdata(pdev, hws);
+
+	return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, data);
+}
+
+static int qcom_cpu_clk_msm8996_driver_remove(struct platform_device *pdev)
+{
+	int i;
+	struct device *dev = &pdev->dev;
+	struct clk_hw_clks *hws = platform_get_drvdata(pdev);
+
+	for (i = 0; i < hws->num; i++)
+		clk_hw_unregister_fixed_rate(hws->hws[i]);
+
+	of_clk_del_provider(dev->of_node);
+
+	return 0;
+}
+
+static struct platform_driver qcom_cpu_clk_msm8996_driver = {
+	.probe = qcom_cpu_clk_msm8996_driver_probe,
+	.remove = qcom_cpu_clk_msm8996_driver_remove,
+	.driver = {
+		.name = "qcom-apcc-msm8996",
+		.of_match_table = match_table,
+	},
+};
+
+module_platform_driver(qcom_cpu_clk_msm8996_driver);
+
+MODULE_ALIAS("platform:apcc-msm8996");
+MODULE_DESCRIPTION("QCOM MSM8996 CPU clock Driver");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

^ permalink raw reply related

* [PATCH 05/10] clk: qcom: cpu-8996: Add support to switch to alternate PLL
From: Ilia Lin @ 2017-12-12 12:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1513081897-31612-1-git-send-email-ilialin@codeaurora.org>

From: Rajendra Nayak <rnayak@codeaurora.org>

Each of the CPU clusters on msm8996 and powered via a primary
PLL and a secondary PLL. The primary PLL is what drivers the
CPU clk, except for times when we are reprogramming the PLL
itself, when we temporarily switch to an alternate PLL.
Use clock rate change notifiers to support this.

Signed-off-by: Rajendra Nayak <rnayak@codeaurora.org>
Signed-off-by: Ilia Lin <ilialin@codeaurora.org>
---
 drivers/clk/qcom/clk-cpu-8996.c | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/drivers/clk/qcom/clk-cpu-8996.c b/drivers/clk/qcom/clk-cpu-8996.c
index bc60111..9bb25be 100644
--- a/drivers/clk/qcom/clk-cpu-8996.c
+++ b/drivers/clk/qcom/clk-cpu-8996.c
@@ -119,10 +119,14 @@ struct clk_cpu_8996_mux {
 	u32	reg;
 	u32	shift;
 	u32	width;
+	struct notifier_block nb;
 	struct clk_hw	*pll;
 	struct clk_regmap clkr;
 };
 
+#define to_clk_cpu_8996_mux_nb(_nb) \
+	container_of(_nb, struct clk_cpu_8996_mux, nb)
+
 static inline
 struct clk_cpu_8996_mux *to_clk_cpu_8996_mux_hw(struct clk_hw *hw)
 {
@@ -173,6 +177,27 @@ static int clk_cpu_8996_mux_set_parent(struct clk_hw *hw, u8 index)
 	return 0;
 }
 
+int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event,
+			void *data)
+{
+	int ret;
+	struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_nb(nb);
+
+	switch (event) {
+	case PRE_RATE_CHANGE:
+		ret = clk_cpu_8996_mux_set_parent(&cpuclk->clkr.hw, ALT_INDEX);
+		break;
+	case POST_RATE_CHANGE:
+		ret = clk_cpu_8996_mux_set_parent(&cpuclk->clkr.hw, PLL_INDEX);
+		break;
+	default:
+		ret = 0;
+		break;
+	}
+
+	return notifier_from_errno(ret);
+};
+
 const struct clk_ops clk_cpu_8996_mux_ops = {
 	.set_parent = clk_cpu_8996_mux_set_parent,
 	.get_parent = clk_cpu_8996_mux_get_parent,
@@ -216,6 +241,7 @@ static int clk_cpu_8996_mux_set_parent(struct clk_hw *hw, u8 index)
 	.shift = 0,
 	.width = 2,
 	.pll = &pwrcl_pll.clkr.hw,
+	.nb.notifier_call = cpu_clk_notifier_cb,
 	.clkr.hw.init = &(struct clk_init_data) {
 		.name = "pwrcl_pmux",
 		.parent_names = (const char *[]){
@@ -235,6 +261,7 @@ static int clk_cpu_8996_mux_set_parent(struct clk_hw *hw, u8 index)
 	.shift = 0,
 	.width = 2,
 	.pll = &perfcl_pll.clkr.hw,
+	.nb.notifier_call = cpu_clk_notifier_cb,
 	.clkr.hw.init = &(struct clk_init_data) {
 		.name = "perfcl_pmux",
 		.parent_names = (const char *[]){
@@ -310,6 +337,14 @@ struct clk_hw_clks {
 	clk_alpha_pll_configure(&perfcl_alt_pll, regmap, &altpll_config);
 	clk_alpha_pll_configure(&pwrcl_alt_pll, regmap, &altpll_config);
 
+	ret = clk_notifier_register(pwrcl_pmux.clkr.hw.clk, &pwrcl_pmux.nb);
+	if (ret)
+		return ret;
+
+	ret = clk_notifier_register(perfcl_pmux.clkr.hw.clk, &perfcl_pmux.nb);
+	if (ret)
+		return ret;
+
 	return ret;
 }
 
-- 
1.9.1

^ permalink raw reply related

* [PATCH 06/10] clk: qcom: cpu-8996: Add support to switch below 600Mhz
From: Ilia Lin @ 2017-12-12 12:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1513081897-31612-1-git-send-email-ilialin@codeaurora.org>

From: Rajendra Nayak <rnayak@codeaurora.org>

The CPU clock controllers primary PLL operates on a single VCO range,
between 600Mhz and 3Ghz. However the CPUs do support OPPs with
frequencies between 300Mhz and 600Mhz. In order to support running the
CPUs at those frequencies we end up having to lock the PLL at twice the
rate and drive the CPU clk via the PLL/2 output and SMUX.

So for frequencies above 600Mhz we follow the following path
 Primary PLL --> PLL_EARLY --> PMUX(1) --> CPU clk
and for frequencies between 300Mhz and 600Mhz we follow
 Primary PLL --> PLL/2 --> SMUX(1) --> PMUX(0) --> CPU clk

Signed-off-by: Rajendra Nayak <rnayak@codeaurora.org>
Signed-off-by: Ilia Lin <ilialin@codeaurora.org>
---
 drivers/clk/qcom/clk-cpu-8996.c | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/qcom/clk-cpu-8996.c b/drivers/clk/qcom/clk-cpu-8996.c
index 9bb25be..79db4e8 100644
--- a/drivers/clk/qcom/clk-cpu-8996.c
+++ b/drivers/clk/qcom/clk-cpu-8996.c
@@ -28,6 +28,7 @@
 #define PLL_INDEX		1
 #define ACD_INDEX		2
 #define ALT_INDEX		3
+#define DIV_2_THRESHOLD		600000000
 
 /* PLLs */
 
@@ -121,6 +122,7 @@ struct clk_cpu_8996_mux {
 	u32	width;
 	struct notifier_block nb;
 	struct clk_hw	*pll;
+	struct clk_hw	*pll_div_2;
 	struct clk_regmap clkr;
 };
 
@@ -171,6 +173,13 @@ static int clk_cpu_8996_mux_set_parent(struct clk_hw *hw, u8 index)
 	if (!cpuclk->pll)
 		return -EINVAL;
 
+	if (cpuclk->pll_div_2 && req->rate < DIV_2_THRESHOLD) {
+		if (req->rate < (DIV_2_THRESHOLD / 2))
+			return -EINVAL;
+
+		parent = cpuclk->pll_div_2;
+	}
+
 	req->best_parent_rate = clk_hw_round_rate(parent, req->rate);
 	req->best_parent_hw = parent;
 
@@ -182,13 +191,19 @@ int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event,
 {
 	int ret;
 	struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_nb(nb);
+	struct clk_notifier_data *cnd = data;
 
 	switch (event) {
 	case PRE_RATE_CHANGE:
 		ret = clk_cpu_8996_mux_set_parent(&cpuclk->clkr.hw, ALT_INDEX);
 		break;
 	case POST_RATE_CHANGE:
-		ret = clk_cpu_8996_mux_set_parent(&cpuclk->clkr.hw, PLL_INDEX);
+		if (cnd->new_rate < DIV_2_THRESHOLD)
+			ret = clk_cpu_8996_mux_set_parent(&cpuclk->clkr.hw,
+							  DIV_2_INDEX);
+		else
+			ret = clk_cpu_8996_mux_set_parent(&cpuclk->clkr.hw,
+							  PLL_INDEX);
 		break;
 	default:
 		ret = 0;
@@ -241,6 +256,7 @@ int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event,
 	.shift = 0,
 	.width = 2,
 	.pll = &pwrcl_pll.clkr.hw,
+	.pll_div_2 = &pwrcl_smux.clkr.hw,
 	.nb.notifier_call = cpu_clk_notifier_cb,
 	.clkr.hw.init = &(struct clk_init_data) {
 		.name = "pwrcl_pmux",
@@ -261,6 +277,7 @@ int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event,
 	.shift = 0,
 	.width = 2,
 	.pll = &perfcl_pll.clkr.hw,
+	.pll_div_2 = &perfcl_smux.clkr.hw,
 	.nb.notifier_call = cpu_clk_notifier_cb,
 	.clkr.hw.init = &(struct clk_init_data) {
 		.name = "perfcl_pmux",
-- 
1.9.1

^ permalink raw reply related

* [PATCH 07/10] clk: qcom: clk-cpu-8996: Prepare PLLs on probe
From: Ilia Lin @ 2017-12-12 12:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1513081897-31612-1-git-send-email-ilialin@codeaurora.org>

The PLLs must be prepared enabled during the probe to be
accessible by the OPPs. Otherwise an OPP may switch
to non-enabled clock.

Signed-off-by: Ilia Lin <ilialin@codeaurora.org>
---
 drivers/clk/qcom/clk-cpu-8996.c | 22 +++++++++++++++++-----
 1 file changed, 17 insertions(+), 5 deletions(-)

diff --git a/drivers/clk/qcom/clk-cpu-8996.c b/drivers/clk/qcom/clk-cpu-8996.c
index 79db4e8..0d41fa9 100644
--- a/drivers/clk/qcom/clk-cpu-8996.c
+++ b/drivers/clk/qcom/clk-cpu-8996.c
@@ -15,7 +15,7 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
-
+#include <linux/clk-provider.h>
 #include "clk-alpha-pll.h"
 
 #define VCO(a, b, c) { \
@@ -159,7 +159,7 @@ static int clk_cpu_8996_mux_set_parent(struct clk_hw *hw, u8 index)
 				    cpuclk->shift);
 
 	val = index;
-	val = cpuclk->shift;
+	val <<= cpuclk->shift;
 
 	return regmap_update_bits(clkr->regmap, cpuclk->reg, mask, val);
 }
@@ -268,7 +268,7 @@ int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event,
 		},
 		.num_parents = 4,
 		.ops = &clk_cpu_8996_mux_ops,
-		.flags = CLK_SET_RATE_PARENT,
+		.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
 	},
 };
 
@@ -284,12 +284,12 @@ int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event,
 		.parent_names = (const char *[]){
 			"perfcl_smux",
 			"perfcl_pll",
-			"pwrcl_pll_acd",
+			"perfcl_pll_acd",
 			"perfcl_alt_pll",
 		},
 		.num_parents = 4,
 		.ops = &clk_cpu_8996_mux_ops,
-		.flags = CLK_SET_RATE_PARENT,
+		.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
 	},
 };
 
@@ -354,6 +354,18 @@ struct clk_hw_clks {
 	clk_alpha_pll_configure(&perfcl_alt_pll, regmap, &altpll_config);
 	clk_alpha_pll_configure(&pwrcl_alt_pll, regmap, &altpll_config);
 
+	/* Enable all PLLs and alt PLLs */
+	clk_prepare_enable(pwrcl_alt_pll.clkr.hw.clk);
+	clk_prepare_enable(perfcl_alt_pll.clkr.hw.clk);
+	clk_prepare_enable(pwrcl_pll.clkr.hw.clk);
+	clk_prepare_enable(perfcl_pll.clkr.hw.clk);
+
+	/* Set initial boot frequencies for power/perf PLLs */
+	clk_set_rate(pwrcl_alt_pll.clkr.hw.clk, 652800000);
+	clk_set_rate(perfcl_alt_pll.clkr.hw.clk, 652800000);
+	clk_set_rate(pwrcl_pll.clkr.hw.clk, 652800000);
+	clk_set_rate(perfcl_pll.clkr.hw.clk, 652800000);
+
 	ret = clk_notifier_register(pwrcl_pmux.clkr.hw.clk, &pwrcl_pmux.nb);
 	if (ret)
 		return ret;
-- 
1.9.1

^ permalink raw reply related

* [PATCH 08/10] clk: qcom: Add ACD path to CPU clock driver for msm8996
From: Ilia Lin @ 2017-12-12 12:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1513081897-31612-1-git-send-email-ilialin@codeaurora.org>

The PMUX for each duplex allows for selection of ACD clock source.
The DVM (Dynamic Variation Monitor) will flag an error
when a voltage droop event is detected. This flagged error
enables ACD to provide a div-by-2 clock, sourced from the primary PLL.
The duplex will be provided the divided clock
until a pre-programmed delay has expired.

This change configures ACD during the probe and switches
the PMUXes to the ACD clock source.

Signed-off-by: Ilia Lin <ilialin@codeaurora.org>
---
 drivers/clk/qcom/clk-cpu-8996.c | 122 ++++++++++++++++++++++++++++++++++------
 1 file changed, 106 insertions(+), 16 deletions(-)

diff --git a/drivers/clk/qcom/clk-cpu-8996.c b/drivers/clk/qcom/clk-cpu-8996.c
index 0d41fa9..6f89471 100644
--- a/drivers/clk/qcom/clk-cpu-8996.c
+++ b/drivers/clk/qcom/clk-cpu-8996.c
@@ -17,6 +17,7 @@
 #include <linux/regmap.h>
 #include <linux/clk-provider.h>
 #include "clk-alpha-pll.h"
+#include <soc/qcom/kryo-l2-accessors.h>
 
 #define VCO(a, b, c) { \
 	.val = a,\
@@ -29,6 +30,27 @@
 #define ACD_INDEX		2
 #define ALT_INDEX		3
 #define DIV_2_THRESHOLD		600000000
+#define PWRCL_REG_OFFSET 0x0
+#define PERFCL_REG_OFFSET 0x80000
+#define MUX_OFFSET	0x40
+#define ALT_PLL_OFFSET	0x100
+#define SSSCTL_OFFSET 0x160
+/*
+APCy_QLL_SSSCTL value:
+SACDRCLEN=1
+SSWEN=1
+SSTRTEN=1
+SSTPAPMSWEN=1
+*/
+#define SSSCTL_VAL 0xF
+
+enum {
+	APC_BASE,
+	EFUSE_BASE,
+	NUM_BASES
+};
+
+static void __iomem *vbases[NUM_BASES];
 
 /* PLLs */
 
@@ -43,7 +65,7 @@
 };
 
 static struct clk_alpha_pll perfcl_pll = {
-	.offset = 0x80000,
+	.offset = PERFCL_REG_OFFSET,
 	.min_rate = 600000000,
 	.max_rate = 3000000000,
 	.flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_16BIT_ALPHA
@@ -57,7 +79,7 @@
 };
 
 static struct clk_alpha_pll pwrcl_pll = {
-	.offset = 0x0,
+	.offset = PWRCL_REG_OFFSET,
 	.min_rate = 600000000,
 	.max_rate = 3000000000,
 	.flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_16BIT_ALPHA
@@ -89,7 +111,7 @@
 };
 
 static struct clk_alpha_pll perfcl_alt_pll = {
-	.offset = 0x80100,
+	.offset = PERFCL_REG_OFFSET + ALT_PLL_OFFSET,
 	.vco_table = alt_pll_vco_modes,
 	.num_vco = ARRAY_SIZE(alt_pll_vco_modes),
 	.flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE,
@@ -102,7 +124,7 @@
 };
 
 static struct clk_alpha_pll pwrcl_alt_pll = {
-	.offset = 0x100,
+	.offset = PWRCL_REG_OFFSET + ALT_PLL_OFFSET,
 	.vco_table = alt_pll_vco_modes,
 	.num_vco = ARRAY_SIZE(alt_pll_vco_modes),
 	.flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE,
@@ -113,6 +135,7 @@
 		.ops = &clk_alpha_pll_hwfsm_ops,
 	},
 };
+static void qcom_cpu_clk_msm8996_acd_init(void);
 
 /* Mux'es */
 
@@ -196,6 +219,7 @@ int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event,
 	switch (event) {
 	case PRE_RATE_CHANGE:
 		ret = clk_cpu_8996_mux_set_parent(&cpuclk->clkr.hw, ALT_INDEX);
+		qcom_cpu_clk_msm8996_acd_init();
 		break;
 	case POST_RATE_CHANGE:
 		if (cnd->new_rate < DIV_2_THRESHOLD)
@@ -203,7 +227,7 @@ int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event,
 							  DIV_2_INDEX);
 		else
 			ret = clk_cpu_8996_mux_set_parent(&cpuclk->clkr.hw,
-							  PLL_INDEX);
+							  ACD_INDEX);
 		break;
 	default:
 		ret = 0;
@@ -220,7 +244,7 @@ int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event,
 };
 
 static struct clk_cpu_8996_mux pwrcl_smux = {
-	.reg = 0x40,
+	.reg = PWRCL_REG_OFFSET + MUX_OFFSET,
 	.shift = 2,
 	.width = 2,
 	.clkr.hw.init = &(struct clk_init_data) {
@@ -236,7 +260,7 @@ int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event,
 };
 
 static struct clk_cpu_8996_mux perfcl_smux = {
-	.reg = 0x80040,
+	.reg = PERFCL_REG_OFFSET + MUX_OFFSET,
 	.shift = 2,
 	.width = 2,
 	.clkr.hw.init = &(struct clk_init_data) {
@@ -252,7 +276,7 @@ int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event,
 };
 
 static struct clk_cpu_8996_mux pwrcl_pmux = {
-	.reg = 0x40,
+	.reg = PWRCL_REG_OFFSET + MUX_OFFSET,
 	.shift = 0,
 	.width = 2,
 	.pll = &pwrcl_pll.clkr.hw,
@@ -273,7 +297,7 @@ int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event,
 };
 
 static struct clk_cpu_8996_mux perfcl_pmux = {
-	.reg = 0x80040,
+	.reg = PERFCL_REG_OFFSET + MUX_OFFSET,
 	.shift = 0,
 	.width = 2,
 	.pll = &perfcl_pll.clkr.hw,
@@ -341,7 +365,17 @@ struct clk_hw_clks {
 						   CLK_SET_RATE_PARENT, 1, 2);
 	pwrcl_smux.pll = hws->hws[1];
 
-	hws->num = 2;
+	hws->hws[2] = clk_hw_register_fixed_factor(dev, "perfcl_pll_acd",
+						   "perfcl_pll",
+						   CLK_SET_RATE_PARENT, 1, 1);
+	perfcl_pmux.pll = hws->hws[2];
+
+	hws->hws[3] = clk_hw_register_fixed_factor(dev, "pwrcl_pll_acd",
+						   "pwrcl_pll",
+						   CLK_SET_RATE_PARENT, 1, 1);
+	pwrcl_pmux.pll = hws->hws[3];
+
+	hws->num = 4;
 
 	for (i = 0; i < ARRAY_SIZE(clks); i++) {
 		ret = devm_clk_register_regmap(dev, clks[i]);
@@ -377,10 +411,65 @@ struct clk_hw_clks {
 	return ret;
 }
 
+#define CPU_AFINITY_MASK 0xFFF
+#define PWRCL_CPU_REG_MASK 0x3
+#define PERFCL_CPU_REG_MASK 0x103
+
+/* ACD static settings (HMSS HPG 7.2.2) */
+#define L2ACDCR_REG 0x580ULL
+#define L2ACDTD_REG 0x581ULL
+#define L2ACDDVMRC_REG 0x584ULL
+#define L2ACDSSCR_REG 0x589ULL
+#define ACDTD_VAL 0x00006A11
+#define ACDCR_VAL 0x002C5FFD
+#define ACDSSCR_VAL 0x00000601
+#define ACDDVMRC_VAL 0x000E0F0F
+
+static DEFINE_SPINLOCK(acd_lock);
+
+static void qcom_cpu_clk_msm8996_acd_init(void)
+{
+	u64 hwid;
+	unsigned long flags;
+
+	spin_lock_irqsave(&acd_lock, flags);
+
+	hwid = read_cpuid_mpidr() & CPU_AFINITY_MASK;
+
+	/* Program ACD Tunable-Length Delay (TLD) */
+	set_l2_indirect_reg(L2ACDTD_REG, ACDTD_VAL);
+	/* Initial ACD for *this* cluster */
+	set_l2_indirect_reg(L2ACDDVMRC_REG, ACDDVMRC_VAL);
+	/* Program ACD soft start control bits. */
+	set_l2_indirect_reg(L2ACDSSCR_REG, ACDSSCR_VAL);
+
+	if (PWRCL_CPU_REG_MASK == (hwid | PWRCL_CPU_REG_MASK)) {
+		/* Enable Soft Stop/Start */
+		if (vbases[APC_BASE])
+			writel_relaxed(SSSCTL_VAL, vbases[APC_BASE] +
+					PWRCL_REG_OFFSET + SSSCTL_OFFSET);
+		/* Ensure SSSCTL config goes through before enabling ACD. */
+		mb();
+		/* Program ACD control bits */
+		set_l2_indirect_reg(L2ACDCR_REG, ACDCR_VAL);
+	}
+	if (PERFCL_CPU_REG_MASK == (hwid | PERFCL_CPU_REG_MASK)) { //else {
+		/* Program ACD control bits */
+		set_l2_indirect_reg(L2ACDCR_REG, ACDCR_VAL);
+		/* Enable Soft Stop/Start */
+		if (vbases[APC_BASE])
+			writel_relaxed(SSSCTL_VAL, vbases[APC_BASE] +
+					PERFCL_REG_OFFSET + SSSCTL_OFFSET);
+		/* Ensure SSSCTL config goes through before enabling ACD. */
+		mb();
+	}
+
+	spin_unlock_irqrestore(&acd_lock, flags);
+}
+
 static int qcom_cpu_clk_msm8996_driver_probe(struct platform_device *pdev)
 {
 	int ret;
-	void __iomem *base;
 	struct resource *res;
 	struct regmap *regmap_cpu;
 	struct clk_hw_clks *hws;
@@ -393,17 +482,17 @@ static int qcom_cpu_clk_msm8996_driver_probe(struct platform_device *pdev)
 	if (!data)
 		return -ENOMEM;
 
-	hws = devm_kzalloc(dev, sizeof(*hws) + 2 * sizeof(struct clk_hw *),
+	hws = devm_kzalloc(dev, sizeof(*hws) + 4 * sizeof(struct clk_hw *),
 			   GFP_KERNEL);
 	if (!hws)
 		return -ENOMEM;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	base = devm_ioremap_resource(dev, res);
-	if (IS_ERR(base))
-		return PTR_ERR(base);
+	vbases[APC_BASE] = devm_ioremap_resource(dev, res);
+	if (IS_ERR(vbases[APC_BASE]))
+		return PTR_ERR(vbases[APC_BASE]);
 
-	regmap_cpu = devm_regmap_init_mmio(dev, base,
+	regmap_cpu = devm_regmap_init_mmio(dev, vbases[APC_BASE],
 					   &cpu_msm8996_regmap_config);
 	if (IS_ERR(regmap_cpu))
 		return PTR_ERR(regmap_cpu);
@@ -411,6 +500,7 @@ static int qcom_cpu_clk_msm8996_driver_probe(struct platform_device *pdev)
 	ret = qcom_cpu_clk_msm8996_register_clks(dev, hws, regmap_cpu);
 	if (ret)
 		return ret;
+	qcom_cpu_clk_msm8996_acd_init();
 
 	data->hws[0] = &pwrcl_pmux.clkr.hw;
 	data->hws[1] = &perfcl_pmux.clkr.hw;
-- 
1.9.1

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox