* [linux-sunxi] [PATCH RESEND 1/2] dt: bindings: add allwinner,otg-routed property for phy-sun4i-usb
From: Hans de Goede @ 2016-10-28 18:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <86c3fad4-e0c1-9aaf-76c5-b9428110464f@redhat.com>
HI,
On 26-10-16 12:14, Hans de Goede wrote:
> Hi,
>
> On 26-10-16 10:52, Icenowy Zheng wrote:
>>
>>
>> 26.10.2016, 16:28, "Hans de Goede" <hdegoede@redhat.com>:
>>> Hi,
>>>
>>> On 25-10-16 06:11, Icenowy Zheng wrote:
>>>> On some newer Allwinner SoCs (H3 or A64), the PHY0 can be either routed to
>>>> the MUSB controller (which is an OTG controller) or the OHCI/EHCI pair
>>>> (which is a Host-only controller, but more stable and easy to implement).
>>>>
>>>> This property marks whether on a certain board which controller should be
>>>> attached to the PHY.
>>>>
>>>> Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
>>>
>>> Icenowy, I appreciate your work on this, but we really need full otg
>>> support with dynamic switching rather then hardwiring the routing, so
>>> this cannot go in as is.
>>
>> Now I have both PHY0 controllers' drivers.
>>
>> In the tree of https://github.com/Icenowy/linux/tree/ice-a64-v6.1 , I have already
>> enabled MUSB controller.
>>
>> And this patchset is for those prefer a stable USB host implement to dual-role
>> implementation. MUSB is a good UDC, but not a good host controller. My USB
>> sound card cannot work on MUSB on A33. Even connecting a R8's MUSB (Serial
>> Gadget) to an A33's MUSB cannot work.
>
> The idea is for dual-role setups to used the MUSB in gadget mode and the EHCI/OHCI
> pair when in host mode. So for otg setups you would runtime change the mux
> from one controller to the other based on the id pin value.
>
> Take a look at drivers/phy/phy-sun4i-usb.c, around line 512:
>
> if (id_det != data->id_det) {
> ...
> }
>
> This deals with id_det changes (including the initial id_det "change"
> for hardwired host-only ports). This currently assumes that the musb
> will be used for host mode too, we will want to change this to
> something like this:
>
> if (id_det != data->id_det) {
> if (data->cfg->separate_phy0_host_controller) {
> if (id_det) {
> /* Change to gadget mode (id_det == 1), switch phy mux to musb */
> actual code to switch phy mux to musb...
> } else {
> /* Change to host mode (id_det == 0), switch phy mux to ehci/ohci */
> actual code to switch phy mux to ehci/ohci...
> }
> }
> /* old code */
> }
>
> Note this will then still rely on the musb code to actually turn
> the regulator on, so you do need to have the musb driver build and
> loaded. This can be fixed but lets start with the above.
>
> If you combine this with dr_mode = "host"; in the dts, then
> sun4i_usb_phy0_get_id_det() will return 0 so on its first run
> sun4i_usb_phy0_id_vbus_det_scan() will throw the mux to the ehci/ohci
> and everything should work as you want without needing the custom
> "allwinner,otg-routed" property, and we should be more or less
> ready to support full otg on other boards.
I've just found further proof that the musb on the H3 at least
only is intended for gadget mode and that we must dynamically
switch for host-mode. If you look at:
drivers/usb/sunxi_usb/include/sunxi_udc.h
In the h3 sdk then you will see that for the H3 a different fifo
endpoint table is used, as the total fifo space is only 4k where
as previous SoCs had 8k. This means that we need to have 2
different ep tables in drivers/usb/musb/sunxi.c and select by
compatible.
Regards,
Hans
^ permalink raw reply
* [PATCH v3 1/2] clk: imx: fix integer overflow in AV PLL round rate
From: Stephen Boyd @ 2016-10-28 18:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAOMZO5Co32XUpNFz4Hd2DEmNnMBvD5fKmsyjcmsgawea4dJR9g@mail.gmail.com>
On 10/28, Fabio Estevam wrote:
> On Thu, Oct 27, 2016 at 11:41 PM, Stephen Boyd <sboyd@codeaurora.org> wrote:
> > On 10/12, Emil Lundmark wrote:
> >> Since 'parent_rate * mfn' may overflow 32 bits, the result should be
> >> stored using 64 bits.
> >>
> >> The problem was discovered when trying to set the rate of the audio PLL
> >> (pll4_post_div) on an i.MX6Q. The desired rate was 196.608 MHz, but
> >> the actual rate returned was 192.000570 MHz. The round rate function should
> >> have been able to return 196.608 MHz, i.e., the desired rate.
> >>
> >> Fixes: ba7f4f557eb6 ("clk: imx: correct AV PLL rate formula")
> >> Cc: Anson Huang <b20788@freescale.com>
> >> Signed-off-by: Emil Lundmark <emil@limesaudio.com>
> >> ---
> >
> > Applied to clk-next
>
> This one fixes a regression caused by ba7f4f557eb6 ("clk: imx: correct
> AV PLL rate formula").
>
> So it should go to clk-fixes instead with the stable tag:
>
> Cc: <stable@vger.kernel.org> # 4.8.x
>
The clk-fixes branch is for patches that fix problems in code
merged during the merge window as well as small one-liners and
things that are causing great pain for people on the latest
release. Given that this fixes a regression in v4.8 and we're
trying to stabilize v4.9 it looked like it could wait until
v4.10.
So is there something going on here where the pain is too much to
wait for the next merge window? If so the commit text should
mention something about what's causing that pain. Perhaps by
referencing the commit that merged outside of clk tree that
caused problems.
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply
* [PATCH v2] phy: sun4i: check PMU presence when poking unknown bit of pmu
From: Hans de Goede @ 2016-10-28 18:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161028162701.4531-1-icenowy@aosc.xyz>
Hi,
On 28-10-16 18:27, Icenowy Zheng wrote:
> Allwinner SoC's PHY 0, when used as OTG controller, have no pmu part.
> The code that poke some unknown bit of PMU for H3/A64 didn't check
> the PHY, and will cause kernel oops when PHY 0 is used.
>
> This patch will check whether the pmu is not NULL before poking.
>
> Fixes: b3e0d141ca9f (phy: sun4i: add support for A64 usb phy)
>
> Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
> Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Patch LGTM too:
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Regards,
Hans
> ---
> drivers/phy/phy-sun4i-usb.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
> index b9342a2..fec34f5 100644
> --- a/drivers/phy/phy-sun4i-usb.c
> +++ b/drivers/phy/phy-sun4i-usb.c
> @@ -264,7 +264,7 @@ static int sun4i_usb_phy_init(struct phy *_phy)
> return ret;
> }
>
> - if (data->cfg->enable_pmu_unk1) {
> + if (phy->pmu && data->cfg->enable_pmu_unk1) {
> val = readl(phy->pmu + REG_PMU_UNK1);
> writel(val & ~2, phy->pmu + REG_PMU_UNK1);
> }
>
^ permalink raw reply
* [PATCH v2] spi: spi-fsl-dspi: Add DMA support for Vybrid
From: Mark Brown @ 2016-10-28 18:16 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161017062308.GA3478@Sanchayan-Arch.localdomain>
On Mon, Oct 17, 2016 at 11:53:08AM +0530, maitysanchayan at gmail.com wrote:
> Hello,
>
> Ping?
Please don't send content free pings and please allow a reasonable time
for review. People get busy, go on holiday, attend conferences and so
on so unless there is some reason for urgency (like critical bug fixes)
please allow at least a couple of weeks for review. If there have been
review comments then people may be waiting for those to be addressed.
Sending content free pings just adds to the mail volume (if they are
seen at all) and if something has gone wrong you'll have to resend the
patches anyway.
Please don't top post, reply in line with needed context. This allows
readers to readily follow the flow of conversation and understand what
you are talking about and also helps ensure that everything in the
discussion is being addressed.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 455 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161028/2a48a59a/attachment.sig>
^ permalink raw reply
* [PATCH v6] soc: qcom: add l2 cache perf events driver
From: Leeder, Neil @ 2016-10-28 18:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161028160213.GH1076@arm.com>
On 10/28/2016 12:02 PM, Will Deacon wrote:
> On Tue, Oct 04, 2016 at 12:25:54PM -0400, Neil Leeder wrote:
>> Thanks Mark. I'll move it, rebase on 4.9-rc1 and run perf fuzzer.
>
> Did the fuzzer explode, or do you have a new version you can post?
>
> Will
Hi Will,
I was delayed by some logistical problems, but the fuzzer ran fine and I
will post the updated patch shortly - sorry for the delay.
Neil
--
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm
Technologies Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project.
^ permalink raw reply
* [PATCH v12 RESEND 0/4] generic TEE subsystem
From: Mark Brown @ 2016-10-28 18:19 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1e532aeb-4944-62e4-c8c4-1e23438b92cd@ti.com>
On Fri, Oct 28, 2016 at 10:43:24AM -0500, Andrew F. Davis wrote:
> Do we see this as a chicken and egg situation, or is there any harm
> beyond the pains of supporting an out-of-tree driver for a while, to
> wait until we have at least one other TEE to add to this subsystem
> before merging?
We haven't been overburneded with TEE vendors wanting to get their
driver code into mainline - do we have any reasonable prospect of other
TEE vendors with an interest in mainline turning up in any kind of
reasonable timeframe?
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 455 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161028/3adb9948/attachment.sig>
^ permalink raw reply
* [PATCH] fpga zynq: Check the bitstream for validity
From: Moritz Fischer @ 2016-10-28 18:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161028165555.GA17998@obsidianresearch.com>
On Fri, Oct 28, 2016 at 10:55:55AM -0600, Jason Gunthorpe wrote:
> On Fri, Oct 28, 2016 at 06:36:06PM +0200, Matthias Brugger wrote:
>
> > Sure but we are checking here that the bitstream passed to the kernel is
> > correct.
>
> The intent to check if it *possible* that the bitstream is
> correct. Correct means that DONE will assert after programming. A 4
> byte bitstream will never assert DONE.
>
> Arguably the threshold should be larger but we don't know what the
> true minimum is.
>
> > + /* All valid bitstreams are multiples of 32 bits */
> > + if (!count || (count % 4) != 0)
> > + return -EINVAL;
> > +
>
> Too clever for my taste.
>
> Aside from this, is the general idea even OK? In my world I cannonize
> the bitstream to start with the sync word, but others may not be doing
> that.
>
> I designed this patch based on the prior work to remove the
> auto-detection, eg that the driver should be passed a canonized
> bitstream.
I'm fine with checking for boundary cases where the bitstreams are
obviously too small or wrong size, I'm not convinced that checking using
internal knowledge about the bistream format inside the kernel is the
right place.
> The problem with the way it is now is how hard it is to figure out
> what the right bitstream format should be. Clear instructions to
> canonize by droping the header before the sync word and byteswap so
> the sync word is in the given order is much clearer..
I don't know about you, but for my designs I can just use what drops out
of my Vivado build. Are you unhappy with the way we document which
format to use, or that we don't slice off the beginning (that gets
ignored by hardware?).
Thanks for addressing the issues with the length calculations,
Moritz
^ permalink raw reply
* [PATCH v2 3/3] reset: Add the TI SCI reset driver
From: Andrew F. Davis @ 2016-10-28 18:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CANLsYkyiC9o9h_mq4D-6pMKNVAKQEnXR5rswiCVa8-mbtgRoJg@mail.gmail.com>
On 10/28/2016 12:43 PM, Mathieu Poirier wrote:
> On 27 October 2016 at 15:49, Andrew F. Davis <afd@ti.com> wrote:
>> Some TI Keystone family of SoCs contain a system controller (like the
>> Power Management Micro Controller (PMMC) on K2G SoCs) that manage the
>> low-level device control (like clocks, resets etc) for the various
>> hardware modules present on the SoC. These device control operations
>> are provided to the host processor OS through a communication protocol
>> called the TI System Control Interface (TI SCI) protocol.
>>
>> This patch adds a reset driver that communicates to the system
>> controller over the TI SCI protocol for performing reset management
>> of various devices present on the SoC. Various reset functionalities
>> are achieved by the means of different TI SCI device operations
>> provided by the TI SCI framework.
>>
>> Signed-off-by: Andrew F. Davis <afd@ti.com>
>> [s-anna at ti.com: documentation changes, revised commit message]
>> Signed-off-by: Suman Anna <s-anna@ti.com>
>> Signed-off-by: Nishanth Menon <nm@ti.com>
>> ---
>> MAINTAINERS | 1 +
>> drivers/reset/Kconfig | 9 ++
>> drivers/reset/Makefile | 1 +
>> drivers/reset/reset-ti-sci.c | 262 +++++++++++++++++++++++++++++++++++++++++++
>> 4 files changed, 273 insertions(+)
>> create mode 100644 drivers/reset/reset-ti-sci.c
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index accf991..b93d91a 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -11901,6 +11901,7 @@ F: include/dt-bindings/clock/k2g.h
>> F: drivers/clk/keystone/sci-clk.c
>> F: Documentation/devicetree/bindings/reset/ti,sci-reset.txt
>> F: include/dt-bindings/reset/k2g.h
>> +F: drivers/reset/reset-ti-sci.c
>>
>> THANKO'S RAREMONO AM/FM/SW RADIO RECEIVER USB DRIVER
>> M: Hans Verkuil <hverkuil@xs4all.nl>
>> diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
>> index 06d9fa2..4c21c9d 100644
>> --- a/drivers/reset/Kconfig
>> +++ b/drivers/reset/Kconfig
>> @@ -66,6 +66,15 @@ config RESET_SUNXI
>> help
>> This enables the reset driver for Allwinner SoCs.
>>
>> +config RESET_TI_SCI
>> + tristate "TI System Control Interface (TI-SCI) reset driver"
>> + depends on RESET_CONTROLLER
>> + depends on TI_SCI_PROTOCOL
>> + help
>> + This enables the reset driver support over TI System Control Interface
>> + available on some new TI SoCs. If you wish to use reset resources
>> + managed by the TI System Controller, say Y here. Otherwise, say N.
>> +
>> config TI_SYSCON_RESET
>> tristate "TI SYSCON Reset Driver"
>> depends on HAS_IOMEM
>> diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
>> index bbe7026..36321f2 100644
>> --- a/drivers/reset/Makefile
>> +++ b/drivers/reset/Makefile
>> @@ -10,6 +10,7 @@ obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o
>> obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o
>> obj-$(CONFIG_RESET_STM32) += reset-stm32.o
>> obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o
>> +obj-$(CONFIG_RESET_TI_SCI) += reset-ti-sci.o
>> obj-$(CONFIG_TI_SYSCON_RESET) += reset-ti-syscon.o
>> obj-$(CONFIG_RESET_UNIPHIER) += reset-uniphier.o
>> obj-$(CONFIG_RESET_ZYNQ) += reset-zynq.o
>> diff --git a/drivers/reset/reset-ti-sci.c b/drivers/reset/reset-ti-sci.c
>> new file mode 100644
>> index 0000000..42ccf12
>> --- /dev/null
>> +++ b/drivers/reset/reset-ti-sci.c
>> @@ -0,0 +1,262 @@
>> +/*
>> + * Texas Instrument's System Control Interface (TI-SCI) reset driver
>> + *
>> + * Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/
>> + * Andrew F. Davis <afd@ti.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + *
>> + * This program is distributed "as is" WITHOUT ANY WARRANTY of any
>> + * kind, whether express or implied; without even the implied warranty
>> + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <linux/idr.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/reset-controller.h>
>> +#include <linux/soc/ti/ti_sci_protocol.h>
>> +
>> +/**
>> + * struct ti_sci_reset_control - reset control structure
>> + * @dev_id: SoC-specific device identifier
>> + * @reset_mask: reset mask to use for toggling reset
>> + */
>> +struct ti_sci_reset_control {
>> + u32 dev_id;
>> + u32 reset_mask;
>> +};
>> +
>> +/**
>> + * struct ti_sci_reset_data - reset controller information structure
>> + * @rcdev: reset controller entity
>> + * @dev: reset controller device pointer
>> + * @sci: TI SCI handle used for communication with system controller
>> + * @idr: idr structure for mapping ids to reset control structures
>> + */
>> +struct ti_sci_reset_data {
>> + struct reset_controller_dev rcdev;
>> + struct device *dev;
>> + const struct ti_sci_handle *sci;
>> + struct idr idr;
>> +};
>> +
>> +#define to_ti_sci_reset_data(p) \
>> + container_of((p), struct ti_sci_reset_data, rcdev)
>> +
>> +/**
>> + * ti_sci_reset_set() - program a device's reset
>> + * @rcdev: reset controller entity
>> + * @id: ID of the reset to toggle
>> + * @assert: boolean flag to indicate assert or deassert
>> + *
>> + * This is a common internal function used to assert or deassert a device's
>> + * reset using the TI SCI protocol. The device's reset is asserted if the
>> + * @assert argument is true, or deasserted if @assert argument is false.
>> + * The mechanism itself is a read-modify-write procedure, the current device
>> + * reset register is read using a TI SCI device operation, the new value is
>> + * set or un-set using the reset's mask, and the new reset value written by
>> + * using another TI SCI device operation.
>> + *
>> + * Return: 0 for successful request, else a corresponding error value
>> + */
>> +static int ti_sci_reset_set(struct reset_controller_dev *rcdev,
>> + unsigned long id, bool assert)
>> +{
>> + struct ti_sci_reset_data *data = to_ti_sci_reset_data(rcdev);
>> + const struct ti_sci_handle *sci = data->sci;
>> + const struct ti_sci_dev_ops *dev_ops = &sci->ops.dev_ops;
>> + struct ti_sci_reset_control *control;
>> + u32 reset_state;
>> + int ret;
>> +
>> + control = idr_find(&data->idr, id);
>> + if (!control)
>> + return -EINVAL;
>> +
>> + ret = dev_ops->get_device_resets(sci, control->dev_id,
>> + &reset_state);
>> + if (ret)
>> + return ret;
>> +
>> + if (assert)
>> + reset_state |= control->reset_mask;
>> + else
>> + reset_state &= ~control->reset_mask;
>> +
>> + return dev_ops->set_device_resets(sci, control->dev_id,
>> + reset_state);
>> +}
>> +
>> +/**
>> + * ti_sci_reset_assert() - assert device reset
>> + * @rcdev: reset controller entity
>> + * @id: ID of the reset to be asserted
>> + *
>> + * This function implements the reset driver op to assert a device's reset
>> + * using the TI SCI protocol. This invokes the function ti_sci_reset_set()
>> + * with the corresponding parameters as passed in, but with the @assert
>> + * argument set to true for asserting the reset.
>> + *
>> + * Return: 0 for successful request, else a corresponding error value
>> + */
>> +static int ti_sci_reset_assert(struct reset_controller_dev *rcdev,
>> + unsigned long id)
>> +{
>> + return ti_sci_reset_set(rcdev, id, true);
>> +}
>> +
>> +/**
>> + * ti_sci_reset_deassert() - deassert device reset
>> + * @rcdev: reset controller entity
>> + * @id: ID of the reset to be deasserted
>> + *
>> + * This function implements the reset driver op to deassert a device's reset
>> + * using the TI SCI protocol. This invokes the function ti_sci_reset_set()
>> + * with the corresponding parameters as passed in, but with the @assert
>> + * argument set to false for deasserting the reset.
>> + *
>> + * Return: 0 for successful request, else a corresponding error value
>> + */
>> +static int ti_sci_reset_deassert(struct reset_controller_dev *rcdev,
>> + unsigned long id)
>> +{
>> + return ti_sci_reset_set(rcdev, id, false);
>> +}
>> +
>> +/**
>> + * ti_sci_reset_status() - check device reset status
>> + * @rcdev: reset controller entity
>> + * @id: ID of reset to be checked
>> + *
>> + * This function implements the reset driver op to return the status of a
>> + * device's reset using the TI SCI protocol. The reset register value is read
>> + * by invoking the TI SCI device opertation .get_device_resets(), and the
>> + * status of the specific reset is extracted and returned using this reset's
>> + * reset mask.
>> + *
>> + * Return: 0 if reset is deasserted, or a non-zero value if reset is asserted
>> + */
>> +static int ti_sci_reset_status(struct reset_controller_dev *rcdev,
>> + unsigned long id)
>> +{
>> + struct ti_sci_reset_data *data = to_ti_sci_reset_data(rcdev);
>> + const struct ti_sci_handle *sci = data->sci;
>> + const struct ti_sci_dev_ops *dev_ops = &sci->ops.dev_ops;
>> + struct ti_sci_reset_control *control;
>> + u32 reset_state;
>> + int ret;
>> +
>> + control = idr_find(&data->idr, id);
>> + if (!control)
>> + return -EINVAL;
>> +
>> + ret = dev_ops->get_device_resets(sci, control->dev_id,
>> + &reset_state);
>> + if (ret)
>> + return ret;
>> +
>> + return reset_state & control->reset_mask;
>> +}
>> +
>> +static struct reset_control_ops ti_sci_reset_ops = {
>> + .assert = ti_sci_reset_assert,
>> + .deassert = ti_sci_reset_deassert,
>> + .status = ti_sci_reset_status,
>> +};
>> +
>> +/**
>> + * ti_sci_reset_of_xlate() - translate a set of OF arguments to a reset ID
>> + * @rcdev: reset controller entity
>> + * @reset_spec: OF reset argument specifier
>> + *
>> + * This function performs the translation of the reset argument specifier
>> + * values defined in a reset consumer device node. The function allocates a
>> + * reset control structure for that device reset, and will be used by the
>> + * driver for performing any reset functions on that reset. An idr structure
>> + * is allocated and used to map to the reset control structure. This idr
>> + * is used by the driver to do reset lookups.
>> + *
>> + * Return: 0 for successful request, else a corresponding error value
>> + */
>> +static int ti_sci_reset_of_xlate(struct reset_controller_dev *rcdev,
>> + const struct of_phandle_args *reset_spec)
>> +{
>> + struct ti_sci_reset_data *data = to_ti_sci_reset_data(rcdev);
>> + struct ti_sci_reset_control *control;
>> +
>> + if (WARN_ON(reset_spec->args_count != rcdev->of_reset_n_cells))
>> + return -EINVAL;
>> +
>> + control = devm_kzalloc(data->dev, sizeof(*control), GFP_KERNEL);
>> + if (!control)
>> + return -ENOMEM;
>> +
>> + control->dev_id = reset_spec->args[0];
>> + control->reset_mask = reset_spec->args[1];
>> +
>> + return idr_alloc(&data->idr, control, 0, 0, GFP_KERNEL);
>> +}
>> +
>> +static const struct of_device_id ti_sci_reset_of_match[] = {
>> + { .compatible = "ti,sci-reset", },
>> + { /* sentinel */ },
>> +};
>> +MODULE_DEVICE_TABLE(of, ti_sci_reset_of_match);
>> +
>> +static int ti_sci_reset_probe(struct platform_device *pdev)
>> +{
>> + struct ti_sci_reset_data *data;
>> +
>> + if (!pdev->dev.of_node)
>> + return -ENODEV;
>> +
>> + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
>> + if (!data)
>> + return -ENOMEM;
>> +
>> + data->sci = devm_ti_sci_get_handle(&pdev->dev);
>> + if (IS_ERR(data->sci))
>> + return PTR_ERR(data->sci);
>> +
>> + data->rcdev.ops = &ti_sci_reset_ops;
>> + data->rcdev.owner = THIS_MODULE;
>> + data->rcdev.of_node = pdev->dev.of_node;
>> + data->rcdev.of_reset_n_cells = 2;
>> + data->rcdev.of_xlate = ti_sci_reset_of_xlate;
>> + data->dev = &pdev->dev;
>> + idr_init(&data->idr);
>
> Hello Andrew,
>
> For my own education, is there a specific reason to use a struct idr
> as opposed to keeping a pointer to a struct ti_sci_reset_control in
> truct ti_sci_reset_data? I'm not opposed to the way you've done
> things but simply keeping a pointer sound more intuitive to me.
>
> Thanks,
> Mathieu
>
Hi Mathieu,
There is one ti_sci_reset_data per reset controller, and each reset
controller can have many resets (each described by a
ti_sci_reset_control instance). When a consumer requests a reset we need
to both generate a unique ID to give back to the consumer to reference
this reset and then internally map this ID to a new ti_sci_reset_control
instance for later look-up when the consumer uses this reset, IDR does
both of these for us.
Thanks,
Andrew
>> +
>> + platform_set_drvdata(pdev, data);
>> +
>> + return reset_controller_register(&data->rcdev);
>> +}
>> +
>> +static int ti_sci_reset_remove(struct platform_device *pdev)
>> +{
>> + struct ti_sci_reset_data *data = platform_get_drvdata(pdev);
>> +
>> + reset_controller_unregister(&data->rcdev);
>> +
>> + idr_destroy(&data->idr);
>> +
>> + return 0;
>> +}
>> +
>> +static struct platform_driver ti_sci_reset_driver = {
>> + .probe = ti_sci_reset_probe,
>> + .remove = ti_sci_reset_remove,
>> + .driver = {
>> + .name = "ti-sci-reset",
>> + .of_match_table = ti_sci_reset_of_match,
>> + },
>> +};
>> +module_platform_driver(ti_sci_reset_driver);
>> +
>> +MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
>> +MODULE_DESCRIPTION("TI System Control Interface (TI SCI) Reset driver");
>> +MODULE_LICENSE("GPL v2");
>> --
>> 2.10.1
>>
^ permalink raw reply
* SMR masking and PCI
From: Stuart Yoder @ 2016-10-28 18:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <c39b785a-0f18-fc0a-ce08-7ebe3cfaf8c5@arm.com>
> -----Original Message-----
> From: Stuart Yoder
> Sent: Friday, October 28, 2016 12:12 PM
> To: 'Robin Murphy' <robin.murphy@arm.com>; Mark Rutland <mark.rutland@arm.com>
> Cc: linux-arm-kernel at lists.infradead.org; Will Deacon <will.deacon@arm.com>; Diana Madalina Craciun
> <diana.craciun@nxp.com>; Nipun Gupta <nipun.gupta@nxp.com>; iommu at lists.linux-foundation.org
> Subject: RE: SMR masking and PCI
>
>
>
> > -----Original Message-----
> > From: Robin Murphy [mailto:robin.murphy at arm.com]
> > Sent: Friday, October 28, 2016 11:17 AM
> > To: Stuart Yoder <stuart.yoder@nxp.com>; Mark Rutland <mark.rutland@arm.com>
> > Cc: linux-arm-kernel at lists.infradead.org; Will Deacon <will.deacon@arm.com>; Diana Madalina Craciun
> > <diana.craciun@nxp.com>; Nipun Gupta <nipun.gupta@nxp.com>; iommu at lists.linux-foundation.org
> > Subject: Re: SMR masking and PCI
> >
> > Hi Stuart,
> >
> > On 27/10/16 18:10, Stuart Yoder wrote:
> > > Hi Robin,
> > >
> > > A question about how the SMR masking defined in the arm,smmu binding
> > > relates to the PCI iommu-map.
> > >
> > > The #iommu-cells property defines the number of cells an "IOMMU specifier"
> > > takes and 2 is specified to be:
> > >
> > > SMMUs with stream matching support and complex masters
> > > may use a value of 2, where the second cell represents
> > > an SMR mask to combine with the ID in the first cell.
> > >
> > > An iommu-map entry is defined as:
> > >
> > > (rid-base,iommu,iommu-base,length)
> > >
> > > What seems to be currently missing in the iommu-map support is
> > > the possibility the case where #iommu-cells=<2>.
> >
> > Indeed. The bindings have so far rather implicitly assumed the case of
> > #{msi,iommu}-cells = 1, and the code has followed suit.
> >
> > > In this case iommu-base which is an IOMMU specifier should
> > > occupy 2 cells. For example on an ls2085a we would want:
> > >
> > > iommu-map = <0x0 0x6 0x7 0x3ff 0x1
> > > 0x100 0x6 0x8 0x3ff 0x1>;
> > >
> > > ...to mask our stream IDs to 10 bits.
> > >
> > > This should work in theory and comply with the bindings, no?
> >
> > In theory, but now consider:
> >
> > iommu-map = <0x0 0x6 0x7 0x3ff 0x2>;
> >
> > faced with ID 1. The input base is 0, the output base is the 2-cell
> > value 0x7000003ff, so the final ID value should be 0x700000400, right?
>
> No. The second cell as per the SMMU binding is the SMR mask...applied
> by the SMMU before matching stream IDs.
I think I now understand what you mean. I missed that you envisioned the
ID and mask being returned as a single unit and concatenated together...and
are split apart later by the SMMU driver.
> In our case we want to mask off the upper TBU ID bits that the SMMU tags
> onto the stream ID in our RID->SID LUT table.
>
> RID = 0
> SID in LUT and seen by SMMU = 7
> SMMU-500 TBU appends bits, making SID something like: 0xC07
> SMR mask of 0x3ff should be applied making the SID: 0x7
>
> > > of_pci_map_rid() seems to have a hardcoded assumption that
> > > each field in the map is 4 bytes.
> >
> > It does. I guess we should explicitly check that #{msi,iommu}-cells = 1
> > on the target node, and maybe clarify in the binding that that cell
> > should represent a plain linear ID value (although that's pretty obvious
> > from context IMO).
> >
> > > (Also, I guess that msi-map is not affected by this since it
> > > is not related to the IOMMU...but we do have common code
> > > handling both maps.)
> >
> > I'd say it's definitely affected, since #msi-cells can equally be more
> > than 1, and encodes an equally opaque value.
> >
> > It seems pretty reasonable to me that we could extend the binding to
> > accommodate #cells > 1 iff length == 1. Mark?
>
> I'm not following why the length matters.
Never mind the comment...think I follow now. Supporting #cells > 1 if
length == 1 sounds good.
Stuart
^ permalink raw reply
* [PATCH V2] arm64: Add support of R_AARCH64_PREL32 relocation in purgatory
From: Geoff Levand @ 2016-10-28 18:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <f970f7938109c40833fc45e3d6d72b1dcdf14595.1477627895.git.panand@redhat.com>
On 10/27/2016 09:16 PM, Pratyush Anand wrote:
> gcc version in fedora koji is 6.2.1-2.fc25. kexec-tools compiled with this
> gcc produced another relocation error:
>
> machine_apply_elf_rel: ERROR Unknown type: 261
>
> This patch fixes the above error.
Looks good. Simon, please apply.
Reviewed-by: Geoff Levand <geoff@infradead.org>
^ permalink raw reply
* [PATCH v2 1/5] lib: add bitrev8x4()
From: kbuild test robot @ 2016-10-28 18:50 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <bc92eb1507448731163ae67fc888668d327f9168.1477669745.git.stillcompiling@gmail.com>
Hi Joshua,
[auto build test WARNING on linus/master]
[also build test WARNING on v4.9-rc2 next-20161028]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
[Suggest to use git(>=2.9.0) format-patch --base=<commit> (or --base=auto for convenience) to record what (public, well-known) commit your patch series was built on]
[Check https://git-scm.com/docs/git-format-patch for more information]
url: https://github.com/0day-ci/linux/commits/Joshua-Clayton/lib-add-bitrev8x4/20161029-012535
config: arm-s5pv210_defconfig (attached as .config)
compiler: arm-linux-gnueabi-gcc (Debian 6.1.1-9) 6.1.1 20160705
reproduce:
wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=arm
All warnings (new ones prefixed by >>):
In file included from include/linux/bitrev.h:7:0,
from include/linux/crc32.h:9,
from lib/crc32.c:29:
arch/arm/include/asm/bitrev.h: In function '__arch_bitrev8x4':
>> arch/arm/include/asm/bitrev.h:23:1: warning: no return statement in function returning non-void [-Wreturn-type]
}
^
vim +23 arch/arm/include/asm/bitrev.h
7 return x;
8 }
9
10 static __always_inline __attribute_const__ u16 __arch_bitrev16(u16 x)
11 {
12 return __arch_bitrev32((u32)x) >> 16;
13 }
14
15 static __always_inline __attribute_const__ u8 __arch_bitrev8(u8 x)
16 {
17 return __arch_bitrev32((u32)x) >> 24;
18 }
19
20 static __always_inline __attribute_const__ u32 __arch_bitrev8x4(u32 x)
21 {
22 __asm__ ("rbit %0, %1; rev %0, %0" : "=r" (x) : "r" (x));
> 23 }
24
25 #endif
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
-------------- next part --------------
A non-text attachment was scrubbed...
Name: .config.gz
Type: application/gzip
Size: 10244 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161029/c7c967b4/attachment-0001.gz>
^ permalink raw reply
* [PATCH v2 3/5] fpga manager: Add cyclone-ps-spi driver for Altera FPGAs
From: kbuild test robot @ 2016-10-28 18:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <202cdeff42a2de149c471630110a8b2657ccf5ca.1477669745.git.stillcompiling@gmail.com>
Hi Joshua,
[auto build test ERROR on linus/master]
[also build test ERROR on v4.9-rc2 next-20161028]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
[Suggest to use git(>=2.9.0) format-patch --base=<commit> (or --base=auto for convenience) to record what (public, well-known) commit your patch series was built on]
[Check https://git-scm.com/docs/git-format-patch for more information]
url: https://github.com/0day-ci/linux/commits/Joshua-Clayton/lib-add-bitrev8x4/20161029-012535
config: i386-allmodconfig (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
# save the attached .config to linux build tree
make ARCH=i386
All error/warnings (new ones prefixed by >>):
In file included from include/linux/delay.h:10:0,
from drivers/fpga/cyclone-ps-spi.c:14:
drivers/fpga/cyclone-ps-spi.c: In function 'cyclonespi_write':
>> drivers/fpga/cyclone-ps-spi.c:89:46: error: 'SZ_4K' undeclared (first use in this function)
size_t stride = min(fw_data_end - fw_data, SZ_4K);
^
include/linux/kernel.h:738:2: note: in definition of macro '__min'
t2 min2 = (y); \
^~
>> drivers/fpga/cyclone-ps-spi.c:89:19: note: in expansion of macro 'min'
size_t stride = min(fw_data_end - fw_data, SZ_4K);
^~~
drivers/fpga/cyclone-ps-spi.c:89:46: note: each undeclared identifier is reported only once for each function it appears in
size_t stride = min(fw_data_end - fw_data, SZ_4K);
^
include/linux/kernel.h:738:2: note: in definition of macro '__min'
t2 min2 = (y); \
^~
>> drivers/fpga/cyclone-ps-spi.c:89:19: note: in expansion of macro 'min'
size_t stride = min(fw_data_end - fw_data, SZ_4K);
^~~
vim +/SZ_4K +89 drivers/fpga/cyclone-ps-spi.c
8 * Works on Cyclone V. Should work on cyclone series.
9 * May work on other Altera fpgas.
10 *
11 */
12
13 #include <linux/bitrev.h>
> 14 #include <linux/delay.h>
15 #include <linux/fpga/fpga-mgr.h>
16 #include <linux/gpio/consumer.h>
17 #include <linux/module.h>
18 #include <linux/of_gpio.h>
19 #include <linux/spi/spi.h>
20
21 #define FPGA_RESET_TIME 50 /* time in usecs to trigger FPGA config */
22 #define FPGA_MIN_DELAY 250 /* min usecs to wait for config status */
23
24 struct cyclonespi_conf {
25 struct gpio_desc *config;
26 struct gpio_desc *status;
27 struct spi_device *spi;
28 };
29
30 static const struct of_device_id of_ef_match[] = {
31 { .compatible = "altr,cyclone-ps-spi-fpga-mgr", },
32 {}
33 };
34 MODULE_DEVICE_TABLE(of, of_ef_match);
35
36 static enum fpga_mgr_states cyclonespi_state(struct fpga_manager *mgr)
37 {
38 return mgr->state;
39 }
40
41 static int cyclonespi_write_init(struct fpga_manager *mgr, u32 flags,
42 const char *buf, size_t count)
43 {
44 struct cyclonespi_conf *conf = (struct cyclonespi_conf *)mgr->priv;
45
46 if (flags & FPGA_MGR_PARTIAL_RECONFIG) {
47 dev_err(&mgr->dev, "Partial reconfiguration not supported.\n");
48 return -EINVAL;
49 }
50
51 gpiod_set_value(conf->config, 0);
52 usleep_range(FPGA_RESET_TIME, FPGA_RESET_TIME + 20);
53 if (gpiod_get_value(conf->status) == 1) {
54 dev_err(&mgr->dev, "Status pin should be low.\n");
55 return -EIO;
56 }
57
58 gpiod_set_value(conf->config, 1);
59 usleep_range(FPGA_MIN_DELAY, FPGA_MIN_DELAY + 20);
60 if (gpiod_get_value(conf->status) == 0) {
61 dev_err(&mgr->dev, "Status pin not ready.\n");
62 return -EIO;
63 }
64
65 return 0;
66 }
67
68 static void rev_buf(void *buf, size_t len)
69 {
70 u32 *fw32 = (u32 *)buf;
71 const u32 *fw_end = (u32 *)(buf + len);
72
73 /* set buffer to lsb first */
74 while (fw32 < fw_end) {
75 *fw32 = bitrev8x4(*fw32);
76 fw32++;
77 }
78 }
79
80 static int cyclonespi_write(struct fpga_manager *mgr, const char *buf,
81 size_t count)
82 {
83 struct cyclonespi_conf *conf = (struct cyclonespi_conf *)mgr->priv;
84 const char *fw_data = buf;
85 const char *fw_data_end = fw_data + count;
86
87 while (fw_data < fw_data_end) {
88 int ret;
> 89 size_t stride = min(fw_data_end - fw_data, SZ_4K);
90
91 rev_buf((void *)fw_data, stride);
92 ret = spi_write(conf->spi, fw_data, stride);
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
-------------- next part --------------
A non-text attachment was scrubbed...
Name: .config.gz
Type: application/gzip
Size: 56833 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161029/6a9787c0/attachment-0001.gz>
^ permalink raw reply
* [PATCH v2 3/5] fpga manager: Add cyclone-ps-spi driver for Altera FPGAs
From: kbuild test robot @ 2016-10-28 19:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <202cdeff42a2de149c471630110a8b2657ccf5ca.1477669745.git.stillcompiling@gmail.com>
Hi Joshua,
[auto build test ERROR on linus/master]
[also build test ERROR on v4.9-rc2 next-20161028]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
[Suggest to use git(>=2.9.0) format-patch --base=<commit> (or --base=auto for convenience) to record what (public, well-known) commit your patch series was built on]
[Check https://git-scm.com/docs/git-format-patch for more information]
url: https://github.com/0day-ci/linux/commits/Joshua-Clayton/lib-add-bitrev8x4/20161029-012535
config: arm64-allmodconfig (attached as .config)
compiler: aarch64-linux-gnu-gcc (Debian 6.1.1-9) 6.1.1 20160705
reproduce:
wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=arm64
All error/warnings (new ones prefixed by >>):
In file included from drivers/fpga/cyclone-ps-spi.c:13:0:
drivers/fpga/cyclone-ps-spi.c: In function 'rev_buf':
>> include/linux/bitrev.h:12:21: error: implicit declaration of function '__arch_bitrev8x4' [-Werror=implicit-function-declaration]
#define __bitrev8x4 __arch_bitrev8x4
^
>> include/linux/bitrev.h:101:2: note: in expansion of macro '__bitrev8x4'
__bitrev8x4(__x); \
^~~~~~~~~~~
>> drivers/fpga/cyclone-ps-spi.c:75:11: note: in expansion of macro 'bitrev8x4'
*fw32 = bitrev8x4(*fw32);
^~~~~~~~~
In file included from include/linux/delay.h:10:0,
from drivers/fpga/cyclone-ps-spi.c:14:
drivers/fpga/cyclone-ps-spi.c: In function 'cyclonespi_write':
include/linux/kernel.h:739:16: warning: comparison of distinct pointer types lacks a cast
(void) (&min1 == &min2); \
^
include/linux/kernel.h:742:2: note: in expansion of macro '__min'
__min(typeof(x), typeof(y), \
^~~~~
drivers/fpga/cyclone-ps-spi.c:89:19: note: in expansion of macro 'min'
size_t stride = min(fw_data_end - fw_data, SZ_4K);
^~~
cc1: some warnings being treated as errors
vim +/__arch_bitrev8x4 +12 include/linux/bitrev.h
556d2f05 Yalin Wang 2014-11-03 6 #ifdef CONFIG_HAVE_ARCH_BITREVERSE
556d2f05 Yalin Wang 2014-11-03 7 #include <asm/bitrev.h>
556d2f05 Yalin Wang 2014-11-03 8
556d2f05 Yalin Wang 2014-11-03 9 #define __bitrev32 __arch_bitrev32
556d2f05 Yalin Wang 2014-11-03 10 #define __bitrev16 __arch_bitrev16
556d2f05 Yalin Wang 2014-11-03 11 #define __bitrev8 __arch_bitrev8
69ff2a81 Joshua Clayton 2016-10-28 @12 #define __bitrev8x4 __arch_bitrev8x4
a5cfc1ec Akinobu Mita 2006-12-08 13
556d2f05 Yalin Wang 2014-11-03 14 #else
556d2f05 Yalin Wang 2014-11-03 15 extern u8 const byte_rev_table[256];
556d2f05 Yalin Wang 2014-11-03 16 static inline u8 __bitrev8(u8 byte)
a5cfc1ec Akinobu Mita 2006-12-08 17 {
a5cfc1ec Akinobu Mita 2006-12-08 18 return byte_rev_table[byte];
a5cfc1ec Akinobu Mita 2006-12-08 19 }
a5cfc1ec Akinobu Mita 2006-12-08 20
556d2f05 Yalin Wang 2014-11-03 21 static inline u16 __bitrev16(u16 x)
556d2f05 Yalin Wang 2014-11-03 22 {
556d2f05 Yalin Wang 2014-11-03 23 return (__bitrev8(x & 0xff) << 8) | __bitrev8(x >> 8);
556d2f05 Yalin Wang 2014-11-03 24 }
556d2f05 Yalin Wang 2014-11-03 25
556d2f05 Yalin Wang 2014-11-03 26 static inline u32 __bitrev32(u32 x)
556d2f05 Yalin Wang 2014-11-03 27 {
556d2f05 Yalin Wang 2014-11-03 28 return (__bitrev16(x & 0xffff) << 16) | __bitrev16(x >> 16);
556d2f05 Yalin Wang 2014-11-03 29 }
556d2f05 Yalin Wang 2014-11-03 30
69ff2a81 Joshua Clayton 2016-10-28 31 static inline u32 __bitrev8x4(u32 x)
69ff2a81 Joshua Clayton 2016-10-28 32 {
69ff2a81 Joshua Clayton 2016-10-28 33 return(__bitrev8(x & 0xff) |
69ff2a81 Joshua Clayton 2016-10-28 34 (__bitrev8((x >> 8) & 0xff) << 8) |
69ff2a81 Joshua Clayton 2016-10-28 35 (__bitrev8((x >> 16) & 0xff) << 16) |
69ff2a81 Joshua Clayton 2016-10-28 36 (__bitrev8((x >> 24) & 0xff) << 24));
69ff2a81 Joshua Clayton 2016-10-28 37 }
69ff2a81 Joshua Clayton 2016-10-28 38
556d2f05 Yalin Wang 2014-11-03 39 #endif /* CONFIG_HAVE_ARCH_BITREVERSE */
556d2f05 Yalin Wang 2014-11-03 40
556d2f05 Yalin Wang 2014-11-03 41 #define __constant_bitrev32(x) \
556d2f05 Yalin Wang 2014-11-03 42 ({ \
556d2f05 Yalin Wang 2014-11-03 43 u32 __x = x; \
556d2f05 Yalin Wang 2014-11-03 44 __x = (__x >> 16) | (__x << 16); \
556d2f05 Yalin Wang 2014-11-03 45 __x = ((__x & (u32)0xFF00FF00UL) >> 8) | ((__x & (u32)0x00FF00FFUL) << 8); \
556d2f05 Yalin Wang 2014-11-03 46 __x = ((__x & (u32)0xF0F0F0F0UL) >> 4) | ((__x & (u32)0x0F0F0F0FUL) << 4); \
556d2f05 Yalin Wang 2014-11-03 47 __x = ((__x & (u32)0xCCCCCCCCUL) >> 2) | ((__x & (u32)0x33333333UL) << 2); \
556d2f05 Yalin Wang 2014-11-03 48 __x = ((__x & (u32)0xAAAAAAAAUL) >> 1) | ((__x & (u32)0x55555555UL) << 1); \
556d2f05 Yalin Wang 2014-11-03 49 __x; \
556d2f05 Yalin Wang 2014-11-03 50 })
556d2f05 Yalin Wang 2014-11-03 51
556d2f05 Yalin Wang 2014-11-03 52 #define __constant_bitrev16(x) \
556d2f05 Yalin Wang 2014-11-03 53 ({ \
556d2f05 Yalin Wang 2014-11-03 54 u16 __x = x; \
556d2f05 Yalin Wang 2014-11-03 55 __x = (__x >> 8) | (__x << 8); \
556d2f05 Yalin Wang 2014-11-03 56 __x = ((__x & (u16)0xF0F0U) >> 4) | ((__x & (u16)0x0F0FU) << 4); \
556d2f05 Yalin Wang 2014-11-03 57 __x = ((__x & (u16)0xCCCCU) >> 2) | ((__x & (u16)0x3333U) << 2); \
556d2f05 Yalin Wang 2014-11-03 58 __x = ((__x & (u16)0xAAAAU) >> 1) | ((__x & (u16)0x5555U) << 1); \
556d2f05 Yalin Wang 2014-11-03 59 __x; \
556d2f05 Yalin Wang 2014-11-03 60 })
556d2f05 Yalin Wang 2014-11-03 61
69ff2a81 Joshua Clayton 2016-10-28 62 #define __constant_bitrev8x4(x) \
69ff2a81 Joshua Clayton 2016-10-28 63 ({ \
69ff2a81 Joshua Clayton 2016-10-28 64 u32 __x = x; \
69ff2a81 Joshua Clayton 2016-10-28 65 __x = ((__x & (u32)0xF0F0F0F0UL) >> 4) | ((__x & (u32)0x0F0F0F0FUL) << 4); \
69ff2a81 Joshua Clayton 2016-10-28 66 __x = ((__x & (u32)0xCCCCCCCCUL) >> 2) | ((__x & (u32)0x33333333UL) << 2); \
69ff2a81 Joshua Clayton 2016-10-28 67 __x = ((__x & (u32)0xAAAAAAAAUL) >> 1) | ((__x & (u32)0x55555555UL) << 1); \
69ff2a81 Joshua Clayton 2016-10-28 68 __x; \
69ff2a81 Joshua Clayton 2016-10-28 69 })
69ff2a81 Joshua Clayton 2016-10-28 70
556d2f05 Yalin Wang 2014-11-03 71 #define __constant_bitrev8(x) \
556d2f05 Yalin Wang 2014-11-03 72 ({ \
556d2f05 Yalin Wang 2014-11-03 73 u8 __x = x; \
556d2f05 Yalin Wang 2014-11-03 74 __x = (__x >> 4) | (__x << 4); \
556d2f05 Yalin Wang 2014-11-03 75 __x = ((__x & (u8)0xCCU) >> 2) | ((__x & (u8)0x33U) << 2); \
556d2f05 Yalin Wang 2014-11-03 76 __x = ((__x & (u8)0xAAU) >> 1) | ((__x & (u8)0x55U) << 1); \
556d2f05 Yalin Wang 2014-11-03 77 __x; \
556d2f05 Yalin Wang 2014-11-03 78 })
556d2f05 Yalin Wang 2014-11-03 79
556d2f05 Yalin Wang 2014-11-03 80 #define bitrev32(x) \
556d2f05 Yalin Wang 2014-11-03 81 ({ \
556d2f05 Yalin Wang 2014-11-03 82 u32 __x = x; \
556d2f05 Yalin Wang 2014-11-03 83 __builtin_constant_p(__x) ? \
556d2f05 Yalin Wang 2014-11-03 84 __constant_bitrev32(__x) : \
556d2f05 Yalin Wang 2014-11-03 85 __bitrev32(__x); \
556d2f05 Yalin Wang 2014-11-03 86 })
556d2f05 Yalin Wang 2014-11-03 87
556d2f05 Yalin Wang 2014-11-03 88 #define bitrev16(x) \
556d2f05 Yalin Wang 2014-11-03 89 ({ \
556d2f05 Yalin Wang 2014-11-03 90 u16 __x = x; \
556d2f05 Yalin Wang 2014-11-03 91 __builtin_constant_p(__x) ? \
556d2f05 Yalin Wang 2014-11-03 92 __constant_bitrev16(__x) : \
556d2f05 Yalin Wang 2014-11-03 93 __bitrev16(__x); \
556d2f05 Yalin Wang 2014-11-03 94 })
a5cfc1ec Akinobu Mita 2006-12-08 95
69ff2a81 Joshua Clayton 2016-10-28 96 #define bitrev8x4(x) \
69ff2a81 Joshua Clayton 2016-10-28 97 ({ \
69ff2a81 Joshua Clayton 2016-10-28 98 u32 __x = x; \
69ff2a81 Joshua Clayton 2016-10-28 99 __builtin_constant_p(__x) ? \
69ff2a81 Joshua Clayton 2016-10-28 100 __constant_bitrev8x4(__x) : \
69ff2a81 Joshua Clayton 2016-10-28 @101 __bitrev8x4(__x); \
69ff2a81 Joshua Clayton 2016-10-28 102 })
69ff2a81 Joshua Clayton 2016-10-28 103
556d2f05 Yalin Wang 2014-11-03 104 #define bitrev8(x) \
:::::: The code at line 12 was first introduced by commit
:::::: 69ff2a81709e0969b2d1a0efaa9a010e0093917c lib: add bitrev8x4()
:::::: TO: Joshua Clayton <stillcompiling@gmail.com>
:::::: CC: 0day robot <fengguang.wu@intel.com>
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
-------------- next part --------------
A non-text attachment was scrubbed...
Name: .config.gz
Type: application/gzip
Size: 52470 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161029/eb4a95b7/attachment-0001.gz>
^ permalink raw reply
* [PATCH v3 1/2] clk: imx: fix integer overflow in AV PLL round rate
From: Fabio Estevam @ 2016-10-28 19:32 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161028181316.GI16026@codeaurora.org>
On Fri, Oct 28, 2016 at 4:13 PM, Stephen Boyd <sboyd@codeaurora.org> wrote:
> The clk-fixes branch is for patches that fix problems in code
> merged during the merge window as well as small one-liners and
> things that are causing great pain for people on the latest
> release. Given that this fixes a regression in v4.8 and we're
> trying to stabilize v4.9 it looked like it could wait until
> v4.10.
The regression affects 4.8 and 4.9.
> So is there something going on here where the pain is too much to
> wait for the next merge window? If so the commit text should
Yes, people reported HDMI issues because of this bug:
https://www.spinics.net/lists/arm-kernel/msg535304.html
> mention something about what's causing that pain. Perhaps by
> referencing the commit that merged outside of clk tree that
> caused problems.
This patch clearly states that the problem is caused by ba7f4f557eb6
("clk: imx: correct AV PLL rate formula").
Since this is a regression, I don't understand why we need to wait
until 4.10 to get it applied.
^ permalink raw reply
* [PATCH v3 11/14] ACPI: irq: introduce interrupt producer
From: agustinv at codeaurora.org @ 2016-10-28 19:32 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477408169-22217-12-git-send-email-guohanjun@huawei.com>
Hey Hanjun,
On 2016-10-25 11:09, Hanjun Guo wrote:
> From: Hanjun Guo <hanjun.guo@linaro.org>
>
> In ACPI 6.1 spec, section 19.6.62, Interrupt Resource Descriptor Macro,
>
> Interrupt (ResourceUsage, EdgeLevel, ActiveLevel, Shared,
> ResourceSourceIndex, ResourceSource, DescriptorName)
> { InterruptList } => Buffer
>
> For the arguement ResourceUsage and DescriptorName, which means:
>
> ResourceUsage describes whether the device consumes the specified
> interrupt ( ResourceConsumer ) or produces it for use by a child
> device ( ResourceProducer ).
> If nothing is specified, then ResourceConsumer is assumed.
>
> DescriptorName evaluates to a name string which refers to the
> entire resource descriptor.
>
> So it can be used for devices connecting to a specific interrupt
> prodcucer instead of the main interrupt controller in MADT. In the
> real world, we have irqchip such as mbi-gen which connecting to
> a group of wired interrupts and then issue msi to the ITS, devices
> connecting to such interrupt controller fit this scope.
>
> For now the irq for ACPI only pointer to the main interrupt
> controller's irqdomain, for devices not connecting to those
> irqdomains, which need to present its irq parent, we can use
> following ASL code to represent it:
>
> Interrupt(ResourceConsumer,..., "\_SB.IRQP") {12,14,....}
>
> then we can parse the interrupt producer with the full
> path name "\_SB.IRQP".
>
> In order to do that, we introduce a pointer interrupt_producer
> in struct acpi_device, and fill it when scanning irq resources
> for acpi device if it specifies the interrupt producer.
>
> But for now when parsing the resources for acpi devices, we don't
> pass the acpi device for acpi_walk_resoures() in
> drivers/acpi/resource.c,
> so introduce a adev in struct res_proc_context to pass it as a context
> to scan the interrupt resources, then finally pass to
> acpi_register_gsi()
> to find its interrupt producer to get the virq from diffrent domains.
>
> With steps above ready, rework acpi_register_gsi() to get other
> interrupt producer if devices not connecting to main interrupt
> controller.
>
> Since we often pass NULL to acpi_register_gsi() and there is no
> interrupt
> producer for devices connect to gicd on ARM or io-apic on X86, so it
> will
> use the default irqdomain for those deivces and no functional changes
> to
> those devices.
>
> Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
> Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
> Cc: Marc Zyngier <marc.zyngier@arm.com>
> Cc: Agustin Vega-Frias <agustinv@codeaurora.org>
> Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
> ---
> drivers/acpi/gsi.c | 10 ++++--
> drivers/acpi/resource.c | 85
> ++++++++++++++++++++++++++++++++++---------------
> include/acpi/acpi_bus.h | 1 +
> 3 files changed, 68 insertions(+), 28 deletions(-)
>
> diff --git a/drivers/acpi/gsi.c b/drivers/acpi/gsi.c
> index ee9e0f2..29ee547 100644
> --- a/drivers/acpi/gsi.c
> +++ b/drivers/acpi/gsi.c
> @@ -55,13 +55,19 @@ int acpi_register_gsi(struct device *dev, u32 gsi,
> int trigger,
> int polarity)
> {
> struct irq_fwspec fwspec;
> + struct acpi_device *adev = dev ? to_acpi_device(dev) : NULL;
>
> - if (WARN_ON(!acpi_gsi_domain_id)) {
> + if (adev && &adev->fwnode && adev->interrupt_producer)
> + /* devices in DSDT connecting to spefic interrupt producer */
> + fwspec.fwnode = adev->interrupt_producer;
> + else if (acpi_gsi_domain_id)
> + /* devices connecting to gicd in default */
> + fwspec.fwnode = acpi_gsi_domain_id;
> + else {
> pr_warn("GSI: No registered irqchip, giving up\n");
> return -EINVAL;
> }
>
> - fwspec.fwnode = acpi_gsi_domain_id;
> fwspec.param[0] = gsi;
> fwspec.param[1] = acpi_dev_get_irq_type(trigger, polarity);
> fwspec.param_count = 2;
> diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c
> index 56241eb..f1371cf 100644
> --- a/drivers/acpi/resource.c
> +++ b/drivers/acpi/resource.c
> @@ -381,7 +381,7 @@ static void acpi_dev_irqresource_disabled(struct
> resource *res, u32 gsi)
> res->flags = IORESOURCE_IRQ | IORESOURCE_DISABLED | IORESOURCE_UNSET;
> }
>
> -static void acpi_dev_get_irqresource(struct resource *res, u32 gsi,
> +static void acpi_dev_get_irqresource(struct acpi_device *adev, struct
> resource *res, u32 gsi,
> u8 triggering, u8 polarity, u8 shareable,
> bool legacy)
> {
> @@ -415,7 +415,7 @@ static void acpi_dev_get_irqresource(struct
> resource *res, u32 gsi,
> }
>
> res->flags = acpi_dev_irq_flags(triggering, polarity, shareable);
> - irq = acpi_register_gsi(NULL, gsi, triggering, polarity);
> + irq = acpi_register_gsi(&adev->dev, gsi, triggering, polarity);
> if (irq >= 0) {
> res->start = irq;
> res->end = irq;
> @@ -424,27 +424,9 @@ static void acpi_dev_get_irqresource(struct
> resource *res, u32 gsi,
> }
> }
>
> -/**
> - * acpi_dev_resource_interrupt - Extract ACPI interrupt resource
> information.
> - * @ares: Input ACPI resource object.
> - * @index: Index into the array of GSIs represented by the resource.
> - * @res: Output generic resource object.
> - *
> - * Check if the given ACPI resource object represents an interrupt
> resource
> - * and @index does not exceed the resource's interrupt count (true is
> returned
> - * in that case regardless of the results of the other checks)). If
> that's the
> - * case, register the GSI corresponding to @index from the array of
> interrupts
> - * represented by the resource and populate the generic resource
> object pointed
> - * to by @res accordingly. If the registration of the GSI is not
> successful,
> - * IORESOURCE_DISABLED will be set it that object's flags.
> - *
> - * Return:
> - * 1) false with res->flags setting to zero: not the expected resource
> type
> - * 2) false with IORESOURCE_DISABLED in res->flags: valid unassigned
> resource
> - * 3) true: valid assigned resource
> - */
> -bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int
> index,
> - struct resource *res)
> +static bool __acpi_dev_resource_interrupt(struct acpi_device *adev,
> + struct acpi_resource *ares, int index,
> + struct resource *res)
> {
> struct acpi_resource_irq *irq;
> struct acpi_resource_extended_irq *ext_irq;
> @@ -460,7 +442,7 @@ bool acpi_dev_resource_interrupt(struct
> acpi_resource *ares, int index,
> acpi_dev_irqresource_disabled(res, 0);
> return false;
> }
> - acpi_dev_get_irqresource(res, irq->interrupts[index],
> + acpi_dev_get_irqresource(adev, res, irq->interrupts[index],
> irq->triggering, irq->polarity,
> irq->sharable, true);
> break;
> @@ -470,7 +452,31 @@ bool acpi_dev_resource_interrupt(struct
> acpi_resource *ares, int index,
> acpi_dev_irqresource_disabled(res, 0);
> return false;
> }
> - acpi_dev_get_irqresource(res, ext_irq->interrupts[index],
> +
> + /*
> + * It's a interrupt consumer device and connecting to specfic
> + * interrupt controller. For now, we only support connecting
> + * interrupts to one irq controller for a single device
> + */
> + if (ext_irq->producer_consumer == ACPI_CONSUMER
> + && ext_irq->resource_source.string_length != 0
> + && !adev->interrupt_producer) {
> + acpi_status status;
> + acpi_handle handle;
> + struct acpi_device *device;
> +
> + status = acpi_get_handle(NULL,
> ext_irq->resource_source.string_ptr, &handle);
> + if (ACPI_FAILURE(status))
> + return false;
> +
> + device = acpi_bus_get_acpi_device(handle);
> + if (!device)
> + return false;
> +
> + adev->interrupt_producer = &device->fwnode;
You are missing an 'acpi_bus_put_acpi_device(device)' call here.
Besides that, this approach will not work in the case where a device
wants to consume interrupts from multiple controllers since you are
forcing adev->interrupt_producer to be the first resource_source
found.
That's the reason I was advocating dynamic lookup (see [1]).
I am about to submit V6 of my series where I also address the probe
ordering issues by enabling re-initialization of platform_device
resources when the resource was marked disabled due to the domain
nor being there during ACPI bus scan.
Thanks,
Agustin
[1] https://lkml.org/lkml/2016/10/18/592
> + }
> +
> + acpi_dev_get_irqresource(adev, res, ext_irq->interrupts[index],
> ext_irq->triggering, ext_irq->polarity,
> ext_irq->sharable, false);
> break;
> @@ -481,6 +487,31 @@ bool acpi_dev_resource_interrupt(struct
> acpi_resource *ares, int index,
>
> return true;
> }
> +
> +/**
> + * acpi_dev_resource_interrupt - Extract ACPI interrupt resource
> information.
> + * @ares: Input ACPI resource object.
> + * @index: Index into the array of GSIs represented by the resource.
> + * @res: Output generic resource object.
> + *
> + * Check if the given ACPI resource object represents an interrupt
> resource
> + * and @index does not exceed the resource's interrupt count (true is
> returned
> + * in that case regardless of the results of the other checks)). If
> that's the
> + * case, register the GSI corresponding to @index from the array of
> interrupts
> + * represented by the resource and populate the generic resource
> object pointed
> + * to by @res accordingly. If the registration of the GSI is not
> successful,
> + * IORESOURCE_DISABLED will be set it that object's flags.
> + *
> + * Return:
> + * 1) false with res->flags setting to zero: not the expected resource
> type
> + * 2) false with IORESOURCE_DISABLED in res->flags: valid unassigned
> resource
> + * 3) true: valid assigned resource
> + */
> +bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int
> index,
> + struct resource *res)
> +{
> + return __acpi_dev_resource_interrupt(NULL, ares, index, res);
> +}
> EXPORT_SYMBOL_GPL(acpi_dev_resource_interrupt);
>
> /**
> @@ -499,6 +530,7 @@ struct res_proc_context {
> void *preproc_data;
> int count;
> int error;
> + struct acpi_device *adev;
> };
>
> static acpi_status acpi_dev_new_resource_entry(struct resource_win
> *win,
> @@ -546,7 +578,7 @@ static acpi_status
> acpi_dev_process_resource(struct acpi_resource *ares,
> || acpi_dev_resource_ext_address_space(ares, &win))
> return acpi_dev_new_resource_entry(&win, c);
>
> - for (i = 0; acpi_dev_resource_interrupt(ares, i, res); i++) {
> + for (i = 0; __acpi_dev_resource_interrupt(c->adev, ares, i, res);
> i++) {
> acpi_status status;
>
> status = acpi_dev_new_resource_entry(&win, c);
> @@ -599,6 +631,7 @@ int acpi_dev_get_resources(struct acpi_device
> *adev, struct list_head *list,
> c.preproc_data = preproc_data;
> c.count = 0;
> c.error = 0;
> + c.adev = adev;
> status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS,
> acpi_dev_process_resource, &c);
> if (ACPI_FAILURE(status)) {
> diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
> index 4242c31..5410d3b 100644
> --- a/include/acpi/acpi_bus.h
> +++ b/include/acpi/acpi_bus.h
> @@ -355,6 +355,7 @@ struct acpi_device {
> int device_type;
> acpi_handle handle; /* no handle for fixed hardware */
> struct fwnode_handle fwnode;
> + struct fwnode_handle *interrupt_producer;
> struct acpi_device *parent;
> struct list_head children;
> struct list_head node;
> --
> 1.7.12.4
--
Qualcomm Datacenter Technologies, Inc. on behalf of the Qualcomm
Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a
Linux Foundation Collaborative Project.
^ permalink raw reply
* [PATCH v2 5/5] fpga manager: cyclone-ps-spi: make delay variable
From: Joshua Clayton @ 2016-10-28 19:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161028174142.GA18325@live.com>
Hi Moritz,
Thanks for the review.
On 10/28/2016 10:41 AM, Moritz Fischer wrote:
> Hi Joshua,
>
> looks good to me; however, I think since you're adding initial support,
> I'd squash this together with [3/5].
I agree. I didn't want to squash it in before putting it up for review, though.
>
> On Fri, Oct 28, 2016 at 09:56:42AM -0700, Joshua Clayton wrote:
>> The status pin may not show ready in the time described in the
>> Altetera manual. check the value several times before giving up
> s/Altetera/Altera
Thanks. I'll fix this. And it will go away when it gets squashed.
>> For the hardware I am working on, the status pin takes 250 us,
>> 5 times as long as described by Altera.
>>
>> Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
>> ---
>> drivers/fpga/cyclone-ps-spi.c | 13 ++++++++-----
>> 1 file changed, 8 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/fpga/cyclone-ps-spi.c b/drivers/fpga/cyclone-ps-spi.c
>> index 4b70d5c..c368223 100644
>> --- a/drivers/fpga/cyclone-ps-spi.c
>> +++ b/drivers/fpga/cyclone-ps-spi.c
>> @@ -20,6 +20,7 @@
>>
>> #define FPGA_RESET_TIME 50 /* time in usecs to trigger FPGA config */
>> -#define FPGA_MIN_DELAY 250 /* min usecs to wait for config status */
>> +#define FPGA_MIN_DELAY 50 /* min usecs to wait for config status */
>> +#define FPGA_MAX_DELAY 1000 /* max usecs to wait for config status */
>>
>> struct cyclonespi_conf {
>> struct gpio_desc *config;
>> @@ -42,6 +43,7 @@ static int cyclonespi_write_init(struct fpga_manager *mgr, u32 flags,
>> const char *buf, size_t count)
>> {
>> struct cyclonespi_conf *conf = (struct cyclonespi_conf *)mgr->priv;
>> + int i;
>>
>> if (flags & FPGA_MGR_PARTIAL_RECONFIG) {
>> dev_err(&mgr->dev, "Partial reconfiguration not supported.\n");
>> @@ -56,13 +58,14 @@ static int cyclonespi_write_init(struct fpga_manager *mgr, u32 flags,
>> }
>>
>> gpiod_set_value(conf->config, 1);
>> - usleep_range(FPGA_MIN_DELAY, FPGA_MIN_DELAY + 20);
>> - if (gpiod_get_value(conf->status) == 0) {
>> - dev_err(&mgr->dev, "Status pin not ready.\n");
>> - return -EIO;
>> + for (i = 0; i < (FPGA_MAX_DELAY / FPGA_MIN_DELAY); i++) {
>> + usleep_range(FPGA_MIN_DELAY, FPGA_MIN_DELAY + 20);
>> + if (gpiod_get_value(conf->status))
>> + return 0;
>> }
>>
>> - return 0;
>> + dev_err(&mgr->dev, "Status pin not ready.\n");
>> + return -EIO;
>> }
>>
>> static void rev_buf(void *buf, size_t len)
>> --
>> 2.7.4
>>
> Cheers,
>
> Moritz
Joshua
^ permalink raw reply
* [PATCH v2] irqchip/bcm2836: Prevent spurious interrupts
From: Thomas Gleixner @ 2016-10-28 19:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <87bmy4qxnb.fsf@eliezer.anholt.net>
On Fri, 28 Oct 2016, Eric Anholt wrote:
> Thomas Gleixner <tglx@linutronix.de> writes:
>
> > On Thu, 27 Oct 2016, Eric Anholt wrote:
> >
> >> From: Phil Elwell <phil@raspberrypi.org>
> >>
> >> The old arch-specific IRQ macros included a dsb to ensure the
Which old arch-specific macros?
> >> write to clear the mailbox interrupt completed before returning
> >> from the interrupt.
That's not what the patch does. It makes sure that the interrupt it cleared
_before_ the IPI handler is called.
> >> The BCM2836 irqchip driver needs the same precaution to avoid
> >> spurious interrupts.
Please describe issues proper. Something like this:
The interrupt demultiplexer code clears a pending mailbox interrupt by
writing the pending bit back to the hardware, but it does not ensure that
the write is completed before proceeding. This causes the machine to
catch fire and scares my cat. <<-- Replace by a proper description.
The write() macro which was used, when the driver was developed,
contained the necessary data sycnhronization barrier, but it was replaced
by writel() when the driver got merged without adding the explicit
barrier after the write.
Add and document the barrier.
Can you spot the difference?
> writel(1 << ipi, mailbox0);
/* Barriers need to be documented with a comment */
> + dsb(sy);
> handle_IPI(ipi, regs);
> > This is missing a fixes tag. I have no idea when that problem was
> > introduced, so I have no way to decide whether this needs to be tagged
> > stable or not.
>
> This code has been there since introduction of the driver, so:
>
> Fixes: 1a15aaa998dc ("irqchip: Add bcm2836 interrupt controller for Raspberry Pi 2")
So it want's a stable tag, right?
Thanks,
tglx
^ permalink raw reply
* [PATCH 0/3] Support userspace irqchip with arch timers
From: Alexander Graf @ 2016-10-28 20:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <56812724-4bac-8265-b6f6-b1ff00308ec0@arm.com>
> Am 28.10.2016 um 17:57 schrieb Marc Zyngier <marc.zyngier@arm.com>:
>
>> On 28/10/16 16:52, Alexander Graf wrote:
>>
>>
>>> Am 28.10.2016 um 16:38 schrieb Marc Zyngier <marc.zyngier@arm.com>:
>>>
>>> Alex,
>>>
>>>> On 30/09/16 20:31, Alexander Graf wrote:
>>>>
>>>>
>>>>>> On 30.09.16 17:43, Christoffer Dall wrote:
>>>>>> On Fri, Sep 30, 2016 at 05:38:11PM +0200, Alexander Graf wrote:
>>>>>>
>>>>>>
>>>>>>> On 30.09.16 16:54, Alexander Graf wrote:
>>>>>>>
>>>>>>>
>>>>>>>> On 27.09.16 21:08, Christoffer Dall wrote:
>>>>>>>> Hi Alex,
>>>>>>>>
>>>>>>>> Marc and I have been looking at this during Linaro connect and have
>>>>>>>> slightly reworked your patch into this small series.
>>>>>>>>
>>>>>>>> It would be good if you could have a look at it and test it out.
>>>>>>>>
>>>>>>>> I've tested it with your QEMU, and it works for UP, but secondary CPUs
>>>>>>>> fail to come up, and it looks like the kernel never gets an IPI for
>>>>>>>> those CPUs from userspace. Any chance you're willing to take a look at
>>>>>>>> that?
>>>>>>>
>>>>>>> I still need to see whether I can come up with a prettier solution, but
>>>>>>> for now this works:
>>>>>>>
>>>>>>> diff --git a/target-i386/kvm.c b/target-i386/kvm.c
>>>>>>
>>>>>> Eh, no, not in i386 code :). But the problem seems to be a missing
>>>>>> mpstate sync.
>>>>>>
>>>>> Yeah, that looked really dodgy. Have you tested it? :)
>>>>
>>>> This time around tested with the correct command line parameters I hope
>>>> :). I'll send a pretty patch later.
>>>>
>>>> diff --git a/target-arm/kvm.c b/target-arm/kvm.c
>>>> index b4c8fe2..b549f00 100644
>>>> --- a/target-arm/kvm.c
>>>> +++ b/target-arm/kvm.c
>>>> @@ -173,6 +173,12 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
>>>> */
>>>> kvm_async_interrupts_allowed = true;
>>>>
>>>> + /*
>>>> + * PSCI wakes up secondary cores, so we always need to
>>>> + * have vCPUs waiting in kernel space
>>>> + */
>>>> + kvm_halt_in_kernel_allowed = true;
>>>> +
>>>> cap_has_mp_state = kvm_check_extension(s, KVM_CAP_MP_STATE);
>>>>
>>>> type_register_static(&host_arm_cpu_type_info);
>>>
>>> What the status of userspace for this thing? Are QEMU patches being
>>> posted and reviewed?
>>
>> I didn't see a notification that the patches were merged. Are they in
>> Linus' tree yet? Then I can post enablement to qemu-devel.
>
> I think you got it backward. I have no intention of merging them until I
> see a vague consensus on the userspace API, and a set of patches ready
> to be merged in QEMU.
That's not how kvm apis are made.
Alex
^ permalink raw reply
* [PATCH] fpga zynq: Check the bitstream for validity
From: Jason Gunthorpe @ 2016-10-28 20:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161028182308.GB18325@live.com>
On Fri, Oct 28, 2016 at 11:23:08AM -0700, Moritz Fischer wrote:
> I'm fine with checking for boundary cases where the bitstreams are
> obviously too small or wrong size, I'm not convinced that checking using
> internal knowledge about the bistream format inside the kernel is the
> right place.
To be clear, the sync word is documented in the Xilinx public docs, it
is mandatory. I don't think there is anything wrong with doing basic
validation on the structure of the bitstream before sending it.
> > The problem with the way it is now is how hard it is to figure out
> > what the right bitstream format should be. Clear instructions to
> > canonize by droping the header before the sync word and byteswap so
> > the sync word is in the given order is much clearer..
>
> I don't know about you, but for my designs I can just use what drops out
> of my Vivado build.
Are you sure? With a 4.8 kernel?
# (in vivado 2016.3) write_bitstream -bin_file foo
$ hexdump -C foo.bin
00000000 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
00000020 00 00 00 bb 11 22 00 44 ff ff ff ff ff ff ff ff |.....".D........|
00000030 aa 99 55 66 20 00 00 00 30 02 20 01 00 00 00 00 |..Uf ...0. .....|
So that isn't going to work, it needs byte swapping
$ hexdump -C foo.bit
000000a0 bb 11 22 00 44 ff ff ff ff ff ff ff ff aa 99 55 |..".D..........U|
000000b0 66 20 00 00 00 30 02 20 01 00 00 00 00 30 02 00 |f ...0. .....0..|
This also is not going to work, it needs byteswapping and the sync word
has to be 4 byte aligned.
What did you do to get a working bitfile? Are you using one of the
Vivado automatic flows that 'handles' this for you? I am not.
Remember, 4.8 now has the patch to remove the autodetection that used
to correct both of the above two problems..
So from my perspective, this driver is incompatible with the output of
the Xilinx tools. I don't really care, we always post-process the
output of write_bitfile, and I am happy to provide a canonized
bitstream, but the driver needs to do more to help people get this
right.
> Are you unhappy with the way we document which format to use, or
> that we don't slice off the beginning (that gets ignored by hardware?).
Well, I'm unhappy I spent an hour wondering why things didn't work
with no information on what the expected format was now for this
driver. For a bit I wondered if there was a driver bug as this stuff
all worked for me with the original xdevcfg driver.
Some of the problems were bugs on my part (which would have been found
much faster with validation), but at the end of the day this is
horribly unfriendly. You get a timeout and a 'Failed' message, thats
it.
I think all common bitstream formatting errors would be detected by
simply validating the sync word.
Jason
^ permalink raw reply
* [PATCH v2 3/5] fpga manager: Add cyclone-ps-spi driver for Altera FPGAs
From: kbuild test robot @ 2016-10-28 20:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <202cdeff42a2de149c471630110a8b2657ccf5ca.1477669745.git.stillcompiling@gmail.com>
Hi Joshua,
[auto build test ERROR on linus/master]
[also build test ERROR on v4.9-rc2 next-20161028]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
[Suggest to use git(>=2.9.0) format-patch --base=<commit> (or --base=auto for convenience) to record what (public, well-known) commit your patch series was built on]
[Check https://git-scm.com/docs/git-format-patch for more information]
url: https://github.com/0day-ci/linux/commits/Joshua-Clayton/lib-add-bitrev8x4/20161029-012535
config: openrisc-allmodconfig (attached as .config)
compiler: or32-linux-gcc (GCC) 4.5.1-or32-1.0rc1
reproduce:
wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=openrisc
All errors (new ones prefixed by >>):
drivers/fpga/cyclone-ps-spi.c: In function 'cyclonespi_write':
drivers/fpga/cyclone-ps-spi.c:89:19: error: 'SZ_4K' undeclared (first use in this function)
drivers/fpga/cyclone-ps-spi.c:89:19: note: each undeclared identifier is reported only once for each function it appears in
>> drivers/fpga/cyclone-ps-spi.c:89:19: error: type defaults to 'int' in declaration of '__UNIQUE_ID_min2_15'
vim +89 drivers/fpga/cyclone-ps-spi.c
83 struct cyclonespi_conf *conf = (struct cyclonespi_conf *)mgr->priv;
84 const char *fw_data = buf;
85 const char *fw_data_end = fw_data + count;
86
87 while (fw_data < fw_data_end) {
88 int ret;
> 89 size_t stride = min(fw_data_end - fw_data, SZ_4K);
90
91 rev_buf((void *)fw_data, stride);
92 ret = spi_write(conf->spi, fw_data, stride);
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
-------------- next part --------------
A non-text attachment was scrubbed...
Name: .config.gz
Type: application/gzip
Size: 39284 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161029/d37f2726/attachment-0001.gz>
^ permalink raw reply
* [PATCH V6 0/3] irqchip: qcom: Add IRQ combiner driver
From: Agustin Vega-Frias @ 2016-10-28 20:48 UTC (permalink / raw)
To: linux-arm-kernel
Add support for IRQ combiners in the Top-level Control and Status
Registers (TCSR) hardware block in Qualcomm Technologies chips.
The first patch fixes probe deferral by allowing platform_device
IRQ resources to be re-initialized if the ACPI core failed to find
the IRQ domain during ACPI bus scan.
The second patch adds support for ResourceSource/IRQ domain mapping
when using Extended IRQ Resources with a specific ResourceSource.
The core ACPI resource management code has been changed to lookup
the IRQ domain when an IRQ resource indicates a ResourceSource,
and register the IRQ on that domain, instead of a GSI.
The third patch takes advantage of the new capabilities to implement
the driver for the IRQ combiners.
Changes V5 -> V6:
* Drop probe table and on-demand probing based on resource_source
* Add patch to fix probe deferral
* Change back combiner driver to use the platform_device/platform_driver
APIs
Changes V4 -> V5:
* Fix issues flagged by the 0-DAY build bot
* Fix some minor style issues raised by Timur
Changes V3 -> V4:
* Add a DSDT device probe table that is used to probe DSDT IRQ chips
as necessary when converting HW IRQs to Linux IRQs
* Describe IRQ combiner registers as ACPI Register resources
Agustin Vega-Frias (3):
ACPI: Retry IRQ conversion if it failed previously
ACPI: Add support for ResourceSource/IRQ domain mapping
irqchip: qcom: Add IRQ combiner driver
drivers/acpi/Makefile | 1 +
drivers/acpi/irqdomain.c | 119 +++++++++++++
drivers/acpi/resource.c | 80 ++++++++-
drivers/base/platform.c | 9 +-
drivers/irqchip/Kconfig | 8 +
drivers/irqchip/Makefile | 1 +
drivers/irqchip/qcom-irq-combiner.c | 337 ++++++++++++++++++++++++++++++++++++
include/linux/acpi.h | 32 ++++
8 files changed, 577 insertions(+), 10 deletions(-)
create mode 100644 drivers/acpi/irqdomain.c
create mode 100644 drivers/irqchip/qcom-irq-combiner.c
--
Qualcomm Datacenter Technologies, Inc. on behalf of the Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.
^ permalink raw reply
* [PATCH V6 1/3] ACPI: Retry IRQ conversion if it failed previously
From: Agustin Vega-Frias @ 2016-10-28 20:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477687696-1509-1-git-send-email-agustinv@codeaurora.org>
This allows probe deferral to work properly when a dependent device
fails to get a valid IRQ because the IRQ domain was not registered
at the time the resources were added to the platform_device.
Signed-off-by: Agustin Vega-Frias <agustinv@codeaurora.org>
---
drivers/acpi/resource.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++
drivers/base/platform.c | 9 +++++++-
include/linux/acpi.h | 7 ++++++
3 files changed, 74 insertions(+), 1 deletion(-)
diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c
index 56241eb..4beda15 100644
--- a/drivers/acpi/resource.c
+++ b/drivers/acpi/resource.c
@@ -664,3 +664,62 @@ int acpi_dev_filter_resource_type(struct acpi_resource *ares,
return (type & types) ? 0 : 1;
}
EXPORT_SYMBOL_GPL(acpi_dev_filter_resource_type);
+
+struct acpi_irq_get_ctx {
+ unsigned int index;
+ struct resource *res;
+};
+
+static acpi_status acpi_irq_get_cb(struct acpi_resource *ares, void *context)
+{
+ struct acpi_irq_get_ctx *ctx = context;
+ struct acpi_resource_irq *irq;
+ struct acpi_resource_extended_irq *ext_irq;
+
+ switch (ares->type) {
+ case ACPI_RESOURCE_TYPE_IRQ:
+ irq = &ares->data.irq;
+ if (ctx->index < irq->interrupt_count) {
+ acpi_dev_resource_interrupt(ares, ctx->index, ctx->res);
+ return AE_CTRL_TERMINATE;
+ }
+ ctx->index -= irq->interrupt_count;
+ break;
+ case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
+ ext_irq = &ares->data.extended_irq;
+ if (ctx->index < ext_irq->interrupt_count) {
+ acpi_dev_resource_interrupt(ares, ctx->index, ctx->res);
+ return AE_CTRL_TERMINATE;
+ }
+ ctx->index -= ext_irq->interrupt_count;
+ break;
+ }
+
+ return AE_OK;
+}
+
+/**
+ * acpi_irq_get - Look for the ACPI IRQ resource with the given index and
+ * use it to initialize the given Linux IRQ resource.
+ * @handle ACPI device handle
+ * @index ACPI IRQ resource index to lookup
+ * @res Linux IRQ resource to initialize
+ *
+ * Return: 0 on success
+ * -EINVAL if an error occurs
+ * -EPROBE_DEFER if the IRQ lookup/conversion failed
+ */
+int acpi_irq_get(acpi_handle handle, unsigned int index, struct resource *res)
+{
+ struct acpi_irq_get_ctx ctx = { index, res };
+ acpi_status status;
+
+ status = acpi_walk_resources(handle, METHOD_NAME__CRS,
+ acpi_irq_get_cb, &ctx);
+ if (ACPI_FAILURE(status))
+ return -EINVAL;
+ if (res->flags & IORESOURCE_DISABLED)
+ return -EPROBE_DEFER;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_irq_get);
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index c4af003..61423d2 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -102,6 +102,14 @@ int platform_get_irq(struct platform_device *dev, unsigned int num)
}
r = platform_get_resource(dev, IORESOURCE_IRQ, num);
+ if (r && r->flags & IORESOURCE_DISABLED && ACPI_COMPANION(&dev->dev)) {
+ int ret;
+
+ ret = acpi_irq_get(ACPI_HANDLE(&dev->dev), num, r);
+ if (ret)
+ return ret;
+ }
+
/*
* The resources may pass trigger flags to the irqs that need
* to be set up. It so happens that the trigger flags for
@@ -1450,4 +1458,3 @@ void __init early_platform_cleanup(void)
memset(&pd->dev.devres_head, 0, sizeof(pd->dev.devres_head));
}
}
-
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index ddbeda6..40213c4 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -405,6 +405,7 @@ bool acpi_dev_resource_ext_address_space(struct acpi_resource *ares,
unsigned int acpi_dev_get_irq_type(int triggering, int polarity);
bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index,
struct resource *res);
+int acpi_irq_get(acpi_handle handle, unsigned int index, struct resource *res);
void acpi_dev_free_resource_list(struct list_head *list);
int acpi_dev_get_resources(struct acpi_device *adev, struct list_head *list,
@@ -762,6 +763,12 @@ static inline int acpi_reconfig_notifier_unregister(struct notifier_block *nb)
return -EINVAL;
}
+static inline int acpi_irq_get(acpi_handle handle, unsigned int index,
+ struct resource *res)
+{
+ return -EINVAL;
+}
+
#endif /* !CONFIG_ACPI */
#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC
--
Qualcomm Datacenter Technologies, Inc. on behalf of the Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.
^ permalink raw reply related
* [PATCH V6 2/3] ACPI: Add support for ResourceSource/IRQ domain mapping
From: Agustin Vega-Frias @ 2016-10-28 20:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477687696-1509-1-git-send-email-agustinv@codeaurora.org>
This allows irqchip drivers to associate an ACPI DSDT device to
an IRQ domain and provides support for using the ResourceSource
in Extended IRQ Resources to find the domain and map the IRQs
specified on that domain.
Signed-off-by: Agustin Vega-Frias <agustinv@codeaurora.org>
---
drivers/acpi/Makefile | 1 +
drivers/acpi/irqdomain.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++
drivers/acpi/resource.c | 21 +++++----
include/linux/acpi.h | 25 ++++++++++
4 files changed, 157 insertions(+), 9 deletions(-)
create mode 100644 drivers/acpi/irqdomain.c
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 9ed0878..880401b 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -57,6 +57,7 @@ acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o
acpi-y += acpi_lpat.o
acpi-$(CONFIG_ACPI_GENERIC_GSI) += gsi.o
acpi-$(CONFIG_ACPI_WATCHDOG) += acpi_watchdog.o
+acpi-$(CONFIG_IRQ_DOMAIN) += irqdomain.o
# These are (potentially) separate modules
diff --git a/drivers/acpi/irqdomain.c b/drivers/acpi/irqdomain.c
new file mode 100644
index 0000000..11d3658
--- /dev/null
+++ b/drivers/acpi/irqdomain.c
@@ -0,0 +1,119 @@
+/*
+ * ACPI ResourceSource/IRQ domain mapping support
+ *
+ * Copyright (c) 2016, 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/acpi.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+
+/**
+ * acpi_irq_domain_register_irq() - Register the mapping for an IRQ produced
+ * by the given acpi_resource_source to a
+ * Linux IRQ number
+ * @source: IRQ source
+ * @hwirq: Hardware IRQ number
+ * @trigger: trigger type of the IRQ number to be mapped
+ * @polarity: polarity of the IRQ to be mapped
+ *
+ * Returns: a valid linux IRQ number on success
+ * -ENODEV if the given acpi_resource_source cannot be found
+ * -EPROBE_DEFER if the IRQ domain has not been registered
+ * -EINVAL for all other errors
+ */
+int acpi_irq_domain_register_irq(const struct acpi_resource_source *source,
+ u32 hwirq, int trigger, int polarity)
+{
+ struct irq_fwspec fwspec;
+ struct acpi_device *device;
+ acpi_handle handle;
+ acpi_status status;
+ int ret;
+
+ /* An empty acpi_resource_source means it is a GSI */
+ if (!source->string_length)
+ return acpi_register_gsi(NULL, hwirq, trigger, polarity);
+
+ status = acpi_get_handle(NULL, source->string_ptr, &handle);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ device = acpi_bus_get_acpi_device(handle);
+ if (!device)
+ return -ENODEV;
+
+ if (irq_find_matching_fwnode(&device->fwnode, DOMAIN_BUS_ANY) == NULL) {
+ ret = -EPROBE_DEFER;
+ goto out_put_device;
+ }
+
+ fwspec.fwnode = &device->fwnode;
+ fwspec.param[0] = hwirq;
+ fwspec.param[1] = acpi_dev_get_irq_type(trigger, polarity);
+ fwspec.param_count = 2;
+
+ ret = irq_create_fwspec_mapping(&fwspec);
+
+out_put_device:
+ acpi_bus_put_acpi_device(device);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(acpi_irq_domain_register_irq);
+
+/**
+ * acpi_irq_domain_unregister_irq() - Delete the mapping for an IRQ produced
+ * by the given acpi_resource_source to a
+ * Linux IRQ number
+ * @source: IRQ source
+ * @hwirq: Hardware IRQ number
+ *
+ * Returns: 0 on success
+ * -ENODEV if the given acpi_resource_source cannot be found
+ * -EINVAL for all other errors
+ */
+int acpi_irq_domain_unregister_irq(const struct acpi_resource_source *source,
+ u32 hwirq)
+{
+ struct irq_domain *domain;
+ struct acpi_device *device;
+ acpi_handle handle;
+ acpi_status status;
+ int ret = 0;
+
+ if (!source->string_length) {
+ acpi_unregister_gsi(hwirq);
+ return 0;
+ }
+
+ status = acpi_get_handle(NULL, source->string_ptr, &handle);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ device = acpi_bus_get_acpi_device(handle);
+ if (!device)
+ return -ENODEV;
+
+ domain = irq_find_matching_fwnode(&device->fwnode, DOMAIN_BUS_ANY);
+ if (!domain) {
+ ret = -EINVAL;
+ goto out_put_device;
+ }
+
+ irq_dispose_mapping(irq_find_mapping(domain, hwirq));
+
+out_put_device:
+ acpi_bus_put_acpi_device(device);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(acpi_irq_domain_unregister_irq);
diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c
index 4beda15..ccb6f4f 100644
--- a/drivers/acpi/resource.c
+++ b/drivers/acpi/resource.c
@@ -381,14 +381,15 @@ static void acpi_dev_irqresource_disabled(struct resource *res, u32 gsi)
res->flags = IORESOURCE_IRQ | IORESOURCE_DISABLED | IORESOURCE_UNSET;
}
-static void acpi_dev_get_irqresource(struct resource *res, u32 gsi,
+static void acpi_dev_get_irqresource(struct resource *res, u32 hwirq,
+ const struct acpi_resource_source *source,
u8 triggering, u8 polarity, u8 shareable,
bool legacy)
{
int irq, p, t;
- if (!valid_IRQ(gsi)) {
- acpi_dev_irqresource_disabled(res, gsi);
+ if ((source->string_length == 0) && !valid_IRQ(hwirq)) {
+ acpi_dev_irqresource_disabled(res, hwirq);
return;
}
@@ -402,25 +403,25 @@ static void acpi_dev_get_irqresource(struct resource *res, u32 gsi,
* using extended IRQ descriptors we take the IRQ configuration
* from _CRS directly.
*/
- if (legacy && !acpi_get_override_irq(gsi, &t, &p)) {
+ if (legacy && !acpi_get_override_irq(hwirq, &t, &p)) {
u8 trig = t ? ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE;
u8 pol = p ? ACPI_ACTIVE_LOW : ACPI_ACTIVE_HIGH;
if (triggering != trig || polarity != pol) {
- pr_warning("ACPI: IRQ %d override to %s, %s\n", gsi,
- t ? "level" : "edge", p ? "low" : "high");
+ pr_warn("ACPI: IRQ %d override to %s, %s\n", hwirq,
+ t ? "level" : "edge", p ? "low" : "high");
triggering = trig;
polarity = pol;
}
}
res->flags = acpi_dev_irq_flags(triggering, polarity, shareable);
- irq = acpi_register_gsi(NULL, gsi, triggering, polarity);
+ irq = acpi_irq_domain_register_irq(source, hwirq, triggering, polarity);
if (irq >= 0) {
res->start = irq;
res->end = irq;
} else {
- acpi_dev_irqresource_disabled(res, gsi);
+ acpi_dev_irqresource_disabled(res, hwirq);
}
}
@@ -446,6 +447,7 @@ static void acpi_dev_get_irqresource(struct resource *res, u32 gsi,
bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index,
struct resource *res)
{
+ const struct acpi_resource_source dummy = { 0, 0, NULL };
struct acpi_resource_irq *irq;
struct acpi_resource_extended_irq *ext_irq;
@@ -460,7 +462,7 @@ bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index,
acpi_dev_irqresource_disabled(res, 0);
return false;
}
- acpi_dev_get_irqresource(res, irq->interrupts[index],
+ acpi_dev_get_irqresource(res, irq->interrupts[index], &dummy,
irq->triggering, irq->polarity,
irq->sharable, true);
break;
@@ -471,6 +473,7 @@ bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index,
return false;
}
acpi_dev_get_irqresource(res, ext_irq->interrupts[index],
+ &ext_irq->resource_source,
ext_irq->triggering, ext_irq->polarity,
ext_irq->sharable, false);
break;
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 40213c4..ce30a12 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -321,6 +321,31 @@ void acpi_set_irq_model(enum acpi_irq_model_id model,
*/
void acpi_unregister_gsi (u32 gsi);
+#ifdef CONFIG_IRQ_DOMAIN
+
+int acpi_irq_domain_register_irq(const struct acpi_resource_source *source,
+ u32 hwirq, int trigger, int polarity);
+int acpi_irq_domain_unregister_irq(const struct acpi_resource_source *source,
+ u32 hwirq);
+
+#else
+
+static inline int acpi_irq_domain_register_irq(
+ const struct acpi_resource_source *source, u32 hwirq, int trigger,
+ int polarity)
+{
+ return acpi_register_gsi(NULL, hwirq, trigger, polarity);
+}
+
+static inline int acpi_irq_domain_unregister_irq(
+ const struct acpi_resource_source *source, u32 hwirq)
+{
+ acpi_unregister_gsi(hwirq);
+ return 0;
+}
+
+#endif /* CONFIG_IRQ_DOMAIN */
+
struct pci_dev;
int acpi_pci_irq_enable (struct pci_dev *dev);
--
Qualcomm Datacenter Technologies, Inc. on behalf of the Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.
^ permalink raw reply related
* [PATCH V6 3/3] irqchip: qcom: Add IRQ combiner driver
From: Agustin Vega-Frias @ 2016-10-28 20:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477687696-1509-1-git-send-email-agustinv@codeaurora.org>
Driver for interrupt combiners in the Top-level Control and Status
Registers (TCSR) hardware block in Qualcomm Technologies chips.
An interrupt combiner in this block combines a set of interrupts by
OR'ing the individual interrupt signals into a summary interrupt
signal routed to a parent interrupt controller, and provides read-
only, 32-bit registers to query the status of individual interrupts.
The status bit for IRQ n is bit (n % 32) within register (n / 32)
of the given combiner. Thus, each combiner can be described as a set
of register offsets and the number of IRQs managed.
Signed-off-by: Agustin Vega-Frias <agustinv@codeaurora.org>
---
drivers/irqchip/Kconfig | 8 +
drivers/irqchip/Makefile | 1 +
drivers/irqchip/qcom-irq-combiner.c | 337 ++++++++++++++++++++++++++++++++++++
3 files changed, 346 insertions(+)
create mode 100644 drivers/irqchip/qcom-irq-combiner.c
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index bc0af33..610f902 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -279,3 +279,11 @@ config EZNPS_GIC
config STM32_EXTI
bool
select IRQ_DOMAIN
+
+config QCOM_IRQ_COMBINER
+ bool "QCOM IRQ combiner support"
+ depends on ARCH_QCOM
+ select IRQ_DOMAIN
+ help
+ Say yes here to add support for the IRQ combiner devices embedded
+ in Qualcomm Technologies chips.
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index e4dbfc8..1818a0b 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -74,3 +74,4 @@ obj-$(CONFIG_LS_SCFG_MSI) += irq-ls-scfg-msi.o
obj-$(CONFIG_EZNPS_GIC) += irq-eznps.o
obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-vic.o
obj-$(CONFIG_STM32_EXTI) += irq-stm32-exti.o
+obj-$(CONFIG_QCOM_IRQ_COMBINER) += qcom-irq-combiner.o
diff --git a/drivers/irqchip/qcom-irq-combiner.c b/drivers/irqchip/qcom-irq-combiner.c
new file mode 100644
index 0000000..fc25251
--- /dev/null
+++ b/drivers/irqchip/qcom-irq-combiner.c
@@ -0,0 +1,337 @@
+/* Copyright (c) 2015-2016, 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.
+ */
+
+/*
+ * Driver for interrupt combiners in the Top-level Control and Status
+ * Registers (TCSR) hardware block in Qualcomm Technologies chips.
+ * An interrupt combiner in this block combines a set of interrupts by
+ * OR'ing the individual interrupt signals into a summary interrupt
+ * signal routed to a parent interrupt controller, and provides read-
+ * only, 32-bit registers to query the status of individual interrupts.
+ * The status bit for IRQ n is bit (n % 32) within register (n / 32)
+ * of the given combiner. Thus, each combiner can be described as a set
+ * of register offsets and the number of IRQs managed.
+ */
+
+#include <linux/acpi.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/platform_device.h>
+
+#define REG_SIZE 32
+
+struct combiner_reg {
+ void __iomem *addr;
+ unsigned long mask;
+};
+
+struct combiner {
+ struct irq_chip irq_chip;
+ struct irq_domain *domain;
+ int parent_irq;
+ u32 nirqs;
+ u32 nregs;
+ struct combiner_reg regs[0];
+};
+
+static inline u32 irq_register(int irq)
+{
+ return irq / REG_SIZE;
+}
+
+static inline u32 irq_bit(int irq)
+{
+ return irq % REG_SIZE;
+
+}
+
+static inline int irq_nr(u32 reg, u32 bit)
+{
+ return reg * REG_SIZE + bit;
+}
+
+/*
+ * Handler for the cascaded IRQ.
+ */
+static void combiner_handle_irq(struct irq_desc *desc)
+{
+ struct combiner *combiner = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ u32 reg;
+
+ chained_irq_enter(chip, desc);
+
+ for (reg = 0; reg < combiner->nregs; reg++) {
+ int virq;
+ int hwirq;
+ u32 bit;
+ u32 status;
+
+ if (combiner->regs[reg].mask == 0)
+ continue;
+
+ status = readl_relaxed(combiner->regs[reg].addr);
+ status &= combiner->regs[reg].mask;
+
+ while (status) {
+ bit = __ffs(status);
+ status &= ~(1 << bit);
+ hwirq = irq_nr(reg, bit);
+ virq = irq_find_mapping(combiner->domain, hwirq);
+ if (virq >= 0)
+ generic_handle_irq(virq);
+
+ }
+ }
+
+ chained_irq_exit(chip, desc);
+}
+
+/*
+ * irqchip callbacks
+ */
+
+static void combiner_irq_chip_mask_irq(struct irq_data *data)
+{
+ struct combiner *combiner = irq_data_get_irq_chip_data(data);
+ struct combiner_reg *reg = combiner->regs + irq_register(data->hwirq);
+
+ clear_bit(irq_bit(data->hwirq), ®->mask);
+}
+
+static void combiner_irq_chip_unmask_irq(struct irq_data *data)
+{
+ struct combiner *combiner = irq_data_get_irq_chip_data(data);
+ struct combiner_reg *reg = combiner->regs + irq_register(data->hwirq);
+
+ set_bit(irq_bit(data->hwirq), ®->mask);
+}
+
+/*
+ * irq_domain_ops callbacks
+ */
+
+static int combiner_irq_map(struct irq_domain *domain, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ struct combiner *combiner = domain->host_data;
+
+ if (hwirq >= combiner->nirqs)
+ return -EINVAL;
+
+ irq_set_chip_and_handler(irq, &combiner->irq_chip, handle_level_irq);
+ irq_set_chip_data(irq, combiner);
+ irq_set_parent(irq, combiner->parent_irq);
+ irq_set_noprobe(irq);
+ return 0;
+}
+
+static void combiner_irq_unmap(struct irq_domain *domain, unsigned int irq)
+{
+ irq_set_chip_and_handler(irq, NULL, NULL);
+ irq_set_chip_data(irq, NULL);
+ irq_set_parent(irq, -1);
+}
+
+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
+static int combiner_irq_translate(struct irq_domain *d, struct irq_fwspec *fws,
+ unsigned long *hwirq, unsigned int *type)
+{
+ if (is_acpi_node(fws->fwnode)) {
+ if (fws->param_count != 2)
+ return -EINVAL;
+
+ *hwirq = fws->param[0];
+ *type = fws->param[1];
+ return 0;
+ }
+
+ return -EINVAL;
+}
+#endif
+
+static const struct irq_domain_ops domain_ops = {
+ .map = combiner_irq_map,
+ .unmap = combiner_irq_unmap,
+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
+ .translate = combiner_irq_translate
+#endif
+};
+
+/*
+ * Device probing
+ */
+
+#ifdef CONFIG_ACPI
+
+static acpi_status count_registers_cb(struct acpi_resource *ares, void *context)
+{
+ int *count = context;
+
+ if (ares->type == ACPI_RESOURCE_TYPE_GENERIC_REGISTER)
+ ++(*count);
+ return AE_OK;
+}
+
+static int count_registers(struct platform_device *pdev)
+{
+ struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
+ acpi_status status;
+ int count = 0;
+
+ if (!acpi_has_method(adev->handle, METHOD_NAME__CRS))
+ return -EINVAL;
+
+ status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS,
+ count_registers_cb, &count);
+ if (ACPI_FAILURE(status))
+ return -EINVAL;
+ return count;
+}
+
+struct get_registers_context {
+ struct device *dev;
+ struct combiner *combiner;
+ int err;
+};
+
+static acpi_status get_registers_cb(struct acpi_resource *ares, void *context)
+{
+ struct get_registers_context *ctx = context;
+ struct acpi_resource_generic_register *reg;
+ phys_addr_t paddr;
+ void __iomem *vaddr;
+
+ if (ares->type != ACPI_RESOURCE_TYPE_GENERIC_REGISTER)
+ return AE_OK;
+
+ reg = &ares->data.generic_reg;
+ paddr = reg->address;
+ if ((reg->space_id != ACPI_SPACE_MEM) ||
+ (reg->bit_offset != 0) ||
+ (reg->bit_width > REG_SIZE)) {
+ dev_err(ctx->dev, "Bad register resource @%pa\n", &paddr);
+ ctx->err = -EINVAL;
+ return AE_ERROR;
+ }
+
+ vaddr = devm_ioremap(ctx->dev, reg->address, REG_SIZE);
+ if (IS_ERR(vaddr)) {
+ dev_err(ctx->dev, "Can't map register @%pa\n", &paddr);
+ ctx->err = PTR_ERR(vaddr);
+ return AE_ERROR;
+ }
+
+ ctx->combiner->regs[ctx->combiner->nregs].addr = vaddr;
+ ctx->combiner->nirqs += reg->bit_width;
+ ctx->combiner->nregs++;
+ return AE_OK;
+}
+
+static int get_registers(struct platform_device *pdev, struct combiner *comb)
+{
+ struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
+ acpi_status status;
+ struct get_registers_context ctx;
+
+ if (!acpi_has_method(adev->handle, METHOD_NAME__CRS))
+ return -EINVAL;
+
+ ctx.dev = &pdev->dev;
+ ctx.combiner = comb;
+ ctx.err = 0;
+
+ status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS,
+ get_registers_cb, &ctx);
+ if (ACPI_FAILURE(status))
+ return ctx.err;
+ return 0;
+}
+
+#else /* !CONFIG_ACPI */
+
+static int count_registers(struct platform_device *pdev)
+{
+ return -EINVAL;
+}
+
+static int get_registers(struct platform_device *pdev, struct combiner *comb)
+{
+ return -EINVAL;
+}
+
+#endif
+
+static int __init combiner_probe(struct platform_device *pdev)
+{
+ struct combiner *combiner;
+ size_t alloc_sz;
+ u32 nregs;
+ int err;
+
+ nregs = count_registers(pdev);
+ if (nregs <= 0) {
+ dev_err(&pdev->dev, "Error reading register resources\n");
+ return -EINVAL;
+ }
+
+ alloc_sz = sizeof(*combiner) + sizeof(struct combiner_reg) * nregs;
+ combiner = devm_kzalloc(&pdev->dev, alloc_sz, GFP_KERNEL);
+ if (!combiner)
+ return -ENOMEM;
+
+ err = get_registers(pdev, combiner);
+ if (err < 0)
+ return err;
+
+ combiner->parent_irq = platform_get_irq(pdev, 0);
+ if (combiner->parent_irq <= 0) {
+ dev_err(&pdev->dev, "Error getting IRQ resource\n");
+ return -EINVAL;
+ }
+
+ combiner->domain = irq_domain_create_linear(
+ pdev->dev.fwnode, combiner->nirqs, &domain_ops, combiner);
+ if (!combiner->domain)
+ /* Errors printed by irq_domain_create_linear */
+ return -ENODEV;
+
+ irq_set_chained_handler_and_data(combiner->parent_irq,
+ combiner_handle_irq, combiner);
+ combiner->irq_chip.irq_mask = combiner_irq_chip_mask_irq;
+ combiner->irq_chip.irq_unmask = combiner_irq_chip_unmask_irq;
+ combiner->irq_chip.name = pdev->name;
+
+ dev_info(&pdev->dev, "Initialized with [p=%d,n=%d,r=%p]\n",
+ combiner->parent_irq, combiner->nirqs, combiner->regs[0].addr);
+ return 0;
+}
+
+static const struct acpi_device_id qcom_irq_combiner_acpi_match[] = {
+ { "QCOM80B1", },
+ { }
+};
+
+static struct platform_driver qcom_irq_combiner_probe = {
+ .driver = {
+ .name = "qcom-irq-combiner",
+ .owner = THIS_MODULE,
+ .acpi_match_table = ACPI_PTR(qcom_irq_combiner_acpi_match),
+ },
+ .probe = combiner_probe,
+};
+
+static int __init register_qcom_irq_combiner(void)
+{
+ return platform_driver_register(&qcom_irq_combiner_probe);
+}
+device_initcall(register_qcom_irq_combiner);
--
Qualcomm Datacenter Technologies, Inc. on behalf of the Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.
^ permalink raw reply related
* [PATCH v7] soc: qcom: add l2 cache perf events driver
From: Neil Leeder @ 2016-10-28 20:50 UTC (permalink / raw)
To: linux-arm-kernel
Adds perf events support for L2 cache PMU.
The L2 cache PMU driver is named 'l2cache_0' and can be used
with perf events to profile L2 events such as cache hits
and misses on Qualcomm Technologies processors.
Signed-off-by: Neil Leeder <nleeder@codeaurora.org>
---
v7:
Move to drivers/perf
Rebased on 4.9-rc1
Successfully ran perf fuzzer against driver
v6: restore accidentally dropped Kconfig dependencies
v5:
Fold the header and l2-accessors into .c file
Use multi-instance framework for hotplug
Change terminology from slice to cluster for clarity
Remove unnecessary rmw sequence for enable registers
Use prev_count in hwc rather than in slice
Enforce all events in same group on same CPU
Add comments, rename variables for clarity
v4:
Replace notifier with hotplug statemachine
Allocate PMU struct dynamically
v3:
Remove exports from l2-accessors
Change l2-accessors Kconfig to make it not user-selectable
Reorder and remove unnecessary includes
v2:
Add the l2-accessors patch to this patchset, previously posted separately.
Remove sampling and per-task functionality for this uncore PMU.
Use cpumask to replace code which filtered events to one cpu per slice.
Replace manual event filtering with filter_match callback.
Use a separate used_mask for event groups.
Add hotplug notifier for CPU and irq migration.
Remove extraneous synchronisation instructions.
Other miscellaneous cleanup.
drivers/perf/Kconfig | 9 +
drivers/perf/Makefile | 1 +
drivers/perf/qcom_l2_pmu.c | 947 +++++++++++++++++++++++++++++++++++++++++++++
include/linux/cpuhotplug.h | 1 +
4 files changed, 958 insertions(+)
create mode 100644 drivers/perf/qcom_l2_pmu.c
diff --git a/drivers/perf/Kconfig b/drivers/perf/Kconfig
index 4d5c5f9..9365190 100644
--- a/drivers/perf/Kconfig
+++ b/drivers/perf/Kconfig
@@ -12,6 +12,15 @@ config ARM_PMU
Say y if you want to use CPU performance monitors on ARM-based
systems.
+config QCOM_L2_PMU
+ bool "Qualcomm Technologies L2-cache PMU"
+ depends on ARCH_QCOM && ARM64 && PERF_EVENTS && ACPI
+ help
+ Provides support for the L2 cache performance monitor unit (PMU)
+ in Qualcomm Technologies processors.
+ Adds the L2 cache PMU into the perf events subsystem for
+ monitoring L2 cache events.
+
config XGENE_PMU
depends on PERF_EVENTS && ARCH_XGENE
bool "APM X-Gene SoC PMU"
diff --git a/drivers/perf/Makefile b/drivers/perf/Makefile
index b116e98..ef24833 100644
--- a/drivers/perf/Makefile
+++ b/drivers/perf/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_ARM_PMU) += arm_pmu.o
+obj-$(CONFIG_QCOM_L2_PMU) += qcom_l2_pmu.o
obj-$(CONFIG_XGENE_PMU) += xgene_pmu.o
diff --git a/drivers/perf/qcom_l2_pmu.c b/drivers/perf/qcom_l2_pmu.c
new file mode 100644
index 0000000..c7b159f
--- /dev/null
+++ b/drivers/perf/qcom_l2_pmu.c
@@ -0,0 +1,947 @@
+/* Copyright (c) 2015,2016 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/acpi.h>
+#include <linux/interrupt.h>
+#include <linux/perf_event.h>
+#include <linux/platform_device.h>
+
+#define MAX_L2_CTRS 9
+
+#define L2PMCR_NUM_EV_SHIFT 11
+#define L2PMCR_NUM_EV_MASK 0x1F
+
+#define L2PMCR 0x400
+#define L2PMCNTENCLR 0x403
+#define L2PMCNTENSET 0x404
+#define L2PMINTENCLR 0x405
+#define L2PMINTENSET 0x406
+#define L2PMOVSCLR 0x407
+#define L2PMOVSSET 0x408
+#define L2PMCCNTCR 0x409
+#define L2PMCCNTR 0x40A
+#define L2PMCCNTSR 0x40C
+#define L2PMRESR 0x410
+#define IA_L2PMXEVCNTCR_BASE 0x420
+#define IA_L2PMXEVCNTR_BASE 0x421
+#define IA_L2PMXEVFILTER_BASE 0x423
+#define IA_L2PMXEVTYPER_BASE 0x424
+
+#define IA_L2_REG_OFFSET 0x10
+
+#define L2PMXEVFILTER_SUFILTER_ALL 0x000E0000
+#define L2PMXEVFILTER_ORGFILTER_IDINDEP 0x00000004
+#define L2PMXEVFILTER_ORGFILTER_ALL 0x00000003
+
+#define L2PM_CC_ENABLE 0x80000000
+
+#define L2EVTYPER_REG_SHIFT 3
+
+#define L2PMRESR_GROUP_BITS 8
+#define L2PMRESR_GROUP_MASK GENMASK(7, 0)
+
+#define L2CYCLE_CTR_BIT 31
+#define L2CYCLE_CTR_RAW_CODE 0xFE
+
+#define L2PMCR_RESET_ALL 0x6
+#define L2PMCR_COUNTERS_ENABLE 0x1
+#define L2PMCR_COUNTERS_DISABLE 0x0
+
+#define L2PMRESR_EN ((u64)1 << 63)
+
+#define L2_EVT_MASK 0x00000FFF
+#define L2_EVT_CODE_MASK 0x00000FF0
+#define L2_EVT_GRP_MASK 0x0000000F
+#define L2_EVT_CODE_SHIFT 4
+#define L2_EVT_GRP_SHIFT 0
+
+#define L2_EVT_CODE(event) (((event) & L2_EVT_CODE_MASK) >> L2_EVT_CODE_SHIFT)
+#define L2_EVT_GROUP(event) (((event) & L2_EVT_GRP_MASK) >> L2_EVT_GRP_SHIFT)
+
+#define L2_EVT_GROUP_MAX 7
+
+#define L2_MAX_PERIOD U32_MAX
+#define L2_CNT_PERIOD (U32_MAX - GENMASK(26, 0))
+
+#define L2CPUSRSELR_EL1 S3_3_c15_c0_6
+#define L2CPUSRDR_EL1 S3_3_c15_c0_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
+ */
+static void set_l2_indirect_reg(u64 reg, u64 val)
+{
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&l2_access_lock, flags);
+ write_sysreg(reg, L2CPUSRSELR_EL1);
+ isb();
+ write_sysreg(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(reg, L2CPUSRSELR_EL1);
+ isb();
+ val = read_sysreg(L2CPUSRDR_EL1);
+ raw_spin_unlock_irqrestore(&l2_access_lock, flags);
+
+ return val;
+}
+
+/*
+ * Aggregate PMU. Implements the core pmu functions and manages
+ * the hardware PMUs.
+ */
+struct l2cache_pmu {
+ struct hlist_node node;
+ u32 num_pmus;
+ struct pmu pmu;
+ int num_counters;
+ cpumask_t cpumask;
+ struct platform_device *pdev;
+};
+
+/*
+ * The cache is made up of one or more clusters, each cluster has its own PMU.
+ * Each cluster is associated with one or more CPUs.
+ * This structure represents one of the hardware PMUs.
+ *
+ * Events can be envisioned as a 2-dimensional array. Each column represents
+ * a group of events. There are 8 groups. Only one entry from each
+ * group can be in use at a time. When an event is assigned a counter
+ * by *_event_add(), the counter index is assigned to group_to_counter[group].
+ * This allows *filter_match() to detect and reject conflicting events in
+ * the same group.
+ * Events are specified as 0xCCG, where CC is 2 hex digits specifying
+ * the code (array row) and G specifies the group (column).
+ *
+ * In addition there is a cycle counter event specified by L2CYCLE_CTR_RAW_CODE
+ * which is outside the above scheme.
+ */
+struct hml2_pmu {
+ struct perf_event *events[MAX_L2_CTRS];
+ struct l2cache_pmu *l2cache_pmu;
+ DECLARE_BITMAP(used_counters, MAX_L2_CTRS);
+ DECLARE_BITMAP(used_groups, L2_EVT_GROUP_MAX + 1);
+ int group_to_counter[L2_EVT_GROUP_MAX + 1];
+ int irq;
+ /* The CPU that is used for collecting events on this cluster */
+ int on_cpu;
+ /* All the CPUs associated with this cluster */
+ cpumask_t cluster_cpus;
+ spinlock_t pmu_lock;
+};
+
+#define to_l2cache_pmu(p) (container_of(p, struct l2cache_pmu, pmu))
+
+static DEFINE_PER_CPU(struct hml2_pmu *, pmu_cluster);
+static u32 l2_cycle_ctr_idx;
+static u32 l2_counter_present_mask;
+
+static inline u32 idx_to_reg_bit(u32 idx)
+{
+ if (idx == l2_cycle_ctr_idx)
+ return BIT(L2CYCLE_CTR_BIT);
+
+ return BIT(idx);
+}
+
+static inline struct hml2_pmu *get_hml2_pmu(int cpu)
+{
+ return per_cpu(pmu_cluster, cpu);
+}
+
+static void hml2_pmu__reset_on_cluster(void *x)
+{
+ /* Reset all ctrs */
+ set_l2_indirect_reg(L2PMCR, L2PMCR_RESET_ALL);
+ set_l2_indirect_reg(L2PMCNTENCLR, l2_counter_present_mask);
+ set_l2_indirect_reg(L2PMINTENCLR, l2_counter_present_mask);
+ set_l2_indirect_reg(L2PMOVSCLR, l2_counter_present_mask);
+}
+
+static inline void hml2_pmu__reset(struct hml2_pmu *cluster)
+{
+ cpumask_t *mask = &cluster->cluster_cpus;
+
+ if (smp_call_function_any(mask, hml2_pmu__reset_on_cluster, NULL, 1))
+ dev_err(&cluster->l2cache_pmu->pdev->dev,
+ "Failed to reset on cluster with cpu %d\n",
+ cpumask_first(&cluster->cluster_cpus));
+}
+
+static inline void hml2_pmu__enable(void)
+{
+ set_l2_indirect_reg(L2PMCR, L2PMCR_COUNTERS_ENABLE);
+}
+
+static inline void hml2_pmu__disable(void)
+{
+ set_l2_indirect_reg(L2PMCR, L2PMCR_COUNTERS_DISABLE);
+}
+
+static inline void hml2_pmu__counter_set_value(u32 idx, u64 value)
+{
+ u32 counter_reg;
+
+ if (idx == l2_cycle_ctr_idx) {
+ set_l2_indirect_reg(L2PMCCNTR, value);
+ } else {
+ counter_reg = (idx * IA_L2_REG_OFFSET) + IA_L2PMXEVCNTR_BASE;
+ set_l2_indirect_reg(counter_reg, value & GENMASK(31, 0));
+ }
+}
+
+static inline u64 hml2_pmu__counter_get_value(u32 idx)
+{
+ u64 value;
+ u32 counter_reg;
+
+ if (idx == l2_cycle_ctr_idx) {
+ value = get_l2_indirect_reg(L2PMCCNTR);
+ } else {
+ counter_reg = (idx * IA_L2_REG_OFFSET) + IA_L2PMXEVCNTR_BASE;
+ value = get_l2_indirect_reg(counter_reg);
+ }
+
+ return value;
+}
+
+static inline void hml2_pmu__counter_enable(u32 idx)
+{
+ set_l2_indirect_reg(L2PMCNTENSET, idx_to_reg_bit(idx));
+}
+
+static inline void hml2_pmu__counter_disable(u32 idx)
+{
+ set_l2_indirect_reg(L2PMCNTENCLR, idx_to_reg_bit(idx));
+}
+
+static inline void hml2_pmu__counter_enable_interrupt(u32 idx)
+{
+ set_l2_indirect_reg(L2PMINTENSET, idx_to_reg_bit(idx));
+}
+
+static inline void hml2_pmu__counter_disable_interrupt(u32 idx)
+{
+ set_l2_indirect_reg(L2PMINTENCLR, idx_to_reg_bit(idx));
+}
+
+static inline void hml2_pmu__set_evccntcr(u32 val)
+{
+ set_l2_indirect_reg(L2PMCCNTCR, val);
+}
+
+static inline void hml2_pmu__set_evcntcr(u32 ctr, u32 val)
+{
+ u32 evtcr_reg = (ctr * IA_L2_REG_OFFSET) + IA_L2PMXEVCNTCR_BASE;
+
+ set_l2_indirect_reg(evtcr_reg, val);
+}
+
+static inline void hml2_pmu__set_evtyper(u32 ctr, u32 val)
+{
+ u32 evtype_reg = (ctr * IA_L2_REG_OFFSET) + IA_L2PMXEVTYPER_BASE;
+
+ set_l2_indirect_reg(evtype_reg, val);
+}
+
+static void hml2_pmu__set_resr(struct hml2_pmu *cluster,
+ u32 event_group, u32 event_cc)
+{
+ u64 field;
+ u64 resr_val;
+ u32 shift;
+ unsigned long flags;
+
+ shift = L2PMRESR_GROUP_BITS * event_group;
+ field = ((u64)(event_cc & L2PMRESR_GROUP_MASK) << shift) | L2PMRESR_EN;
+
+ spin_lock_irqsave(&cluster->pmu_lock, flags);
+
+ resr_val = get_l2_indirect_reg(L2PMRESR);
+ resr_val &= ~(L2PMRESR_GROUP_MASK << shift);
+ resr_val |= field;
+ set_l2_indirect_reg(L2PMRESR, resr_val);
+
+ spin_unlock_irqrestore(&cluster->pmu_lock, flags);
+}
+
+/*
+ * Hardware allows filtering of events based on the originating
+ * CPU. Turn this off by setting filter bits to allow events from
+ * all CPUS, subunits and ID independent events in this cluster.
+ */
+static inline void hml2_pmu__set_evfilter_sys_mode(u32 ctr)
+{
+ u32 reg = (ctr * IA_L2_REG_OFFSET) + IA_L2PMXEVFILTER_BASE;
+ u32 val = L2PMXEVFILTER_SUFILTER_ALL |
+ L2PMXEVFILTER_ORGFILTER_IDINDEP |
+ L2PMXEVFILTER_ORGFILTER_ALL;
+
+ set_l2_indirect_reg(reg, val);
+}
+
+static inline u32 hml2_pmu__getreset_ovsr(void)
+{
+ u32 result = get_l2_indirect_reg(L2PMOVSSET);
+
+ set_l2_indirect_reg(L2PMOVSCLR, result);
+ return result;
+}
+
+static inline bool hml2_pmu__has_overflowed(u32 ovsr)
+{
+ return !!(ovsr & l2_counter_present_mask);
+}
+
+static inline bool hml2_pmu__counter_has_overflowed(u32 ovsr, u32 idx)
+{
+ return !!(ovsr & idx_to_reg_bit(idx));
+}
+
+static void l2_cache__event_update_from_cluster(struct perf_event *event,
+ struct hml2_pmu *cluster)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ u64 delta64, prev, now;
+ u32 delta;
+ u32 idx = hwc->idx;
+
+ do {
+ prev = local64_read(&hwc->prev_count);
+ now = hml2_pmu__counter_get_value(idx);
+ } while (local64_cmpxchg(&hwc->prev_count, prev, now) != prev);
+
+ if (idx == l2_cycle_ctr_idx) {
+ /*
+ * The cycle counter is 64-bit so needs separate handling
+ * of 64-bit delta.
+ */
+ delta64 = now - prev;
+ local64_add(delta64, &event->count);
+ } else {
+ /*
+ * 32-bit counters need the unsigned 32-bit math to handle
+ * overflow and now < prev
+ */
+ delta = now - prev;
+ local64_add(delta, &event->count);
+ }
+}
+
+static void l2_cache__cluster_set_period(struct hml2_pmu *cluster,
+ struct hw_perf_event *hwc)
+{
+ u64 base = L2_MAX_PERIOD - (L2_CNT_PERIOD - 1);
+ u32 idx = hwc->idx;
+ u64 prev = local64_read(&hwc->prev_count);
+ u64 value;
+
+ /*
+ * Limit the maximum period to prevent the counter value
+ * from overtaking the one we are about to program.
+ * Use a starting value which is high enough that after
+ * an overflow, interrupt latency will not cause the count
+ * to reach the base value. If the previous value
+ * is below the base, increase it to be above the base
+ * and update prev_count accordingly. Otherwise if
+ * the previous value is already above the base
+ * nothing needs to be done to prev_count.
+ */
+ if (prev < base) {
+ value = base + prev;
+ local64_set(&hwc->prev_count, value);
+ } else {
+ value = prev;
+ }
+
+ hml2_pmu__counter_set_value(idx, value);
+}
+
+static int l2_cache__get_event_idx(struct hml2_pmu *cluster,
+ struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ int idx;
+
+ if (hwc->config_base == L2CYCLE_CTR_RAW_CODE) {
+ if (test_and_set_bit(l2_cycle_ctr_idx, cluster->used_counters))
+ return -EAGAIN;
+
+ return l2_cycle_ctr_idx;
+ }
+
+ for (idx = 0; idx < cluster->l2cache_pmu->num_counters - 1; idx++) {
+ if (!test_and_set_bit(idx, cluster->used_counters)) {
+ set_bit(L2_EVT_GROUP(hwc->config_base),
+ cluster->used_groups);
+ return idx;
+ }
+ }
+
+ /* The counters are all in use. */
+ return -EAGAIN;
+}
+
+static void l2_cache__clear_event_idx(struct hml2_pmu *cluster,
+ struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ int idx = hwc->idx;
+
+ clear_bit(idx, cluster->used_counters);
+ if (hwc->config_base != L2CYCLE_CTR_RAW_CODE)
+ clear_bit(L2_EVT_GROUP(hwc->config_base), cluster->used_groups);
+}
+
+static irqreturn_t l2_cache__handle_irq(int irq_num, void *data)
+{
+ struct hml2_pmu *cluster = data;
+ int num_counters = cluster->l2cache_pmu->num_counters;
+ u32 ovsr;
+ int idx;
+
+ ovsr = hml2_pmu__getreset_ovsr();
+ if (!hml2_pmu__has_overflowed(ovsr))
+ return IRQ_NONE;
+
+ for_each_set_bit(idx, cluster->used_counters, num_counters) {
+ struct perf_event *event = cluster->events[idx];
+ struct hw_perf_event *hwc;
+
+ if (!hml2_pmu__counter_has_overflowed(ovsr, idx))
+ continue;
+
+ l2_cache__event_update_from_cluster(event, cluster);
+ hwc = &event->hw;
+
+ l2_cache__cluster_set_period(cluster, hwc);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Implementation of abstract pmu functionality required by
+ * the core perf events code.
+ */
+
+static void l2_cache__pmu_enable(struct pmu *pmu)
+{
+ /*
+ * Although there is only one PMU (per socket) controlling multiple
+ * physical PMUs (per cluster), because we do not support per-task mode
+ * each event is associated with a CPU. Each event has pmu_enable
+ * called on its CPU, so here it is only necessary to enable the
+ * counters for the current CPU.
+ */
+
+ hml2_pmu__enable();
+}
+
+static void l2_cache__pmu_disable(struct pmu *pmu)
+{
+ hml2_pmu__disable();
+}
+
+static int l2_cache__event_init(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ struct hml2_pmu *cluster;
+ struct perf_event *sibling;
+ struct l2cache_pmu *l2cache_pmu;
+
+ if (event->attr.type != event->pmu->type)
+ return -ENOENT;
+
+ l2cache_pmu = to_l2cache_pmu(event->pmu);
+
+ if (hwc->sample_period) {
+ dev_warn(&l2cache_pmu->pdev->dev, "Sampling not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (event->cpu < 0) {
+ dev_warn(&l2cache_pmu->pdev->dev, "Per-task mode not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ /* We cannot filter accurately so we just don't allow it. */
+ if (event->attr.exclude_user || event->attr.exclude_kernel ||
+ event->attr.exclude_hv || event->attr.exclude_idle) {
+ dev_warn(&l2cache_pmu->pdev->dev, "Can't exclude execution levels\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (((L2_EVT_GROUP(event->attr.config) > L2_EVT_GROUP_MAX) ||
+ ((event->attr.config & ~L2_EVT_MASK) != 0)) &&
+ (event->attr.config != L2CYCLE_CTR_RAW_CODE)) {
+ dev_warn(&l2cache_pmu->pdev->dev, "Invalid config %llx\n",
+ event->attr.config);
+ return -EINVAL;
+ }
+
+ /* Don't allow groups with mixed PMUs, except for s/w events */
+ if (event->group_leader->pmu != event->pmu &&
+ !is_software_event(event->group_leader)) {
+ dev_warn(&l2cache_pmu->pdev->dev,
+ "Can't create mixed PMU group\n");
+ return -EINVAL;
+ }
+
+ list_for_each_entry(sibling, &event->group_leader->sibling_list,
+ group_entry)
+ if (sibling->pmu != event->pmu &&
+ !is_software_event(sibling)) {
+ dev_warn(&l2cache_pmu->pdev->dev,
+ "Can't create mixed PMU group\n");
+ return -EINVAL;
+ }
+
+ /* Ensure all events in a group are on the same cpu */
+ cluster = get_hml2_pmu(event->cpu);
+ if ((event->group_leader != event) &&
+ (cluster->on_cpu != event->group_leader->cpu)) {
+ dev_warn(&l2cache_pmu->pdev->dev,
+ "Can't create group on CPUs %d and %d",
+ event->cpu, event->group_leader->cpu);
+ return -EINVAL;
+ }
+
+ hwc->idx = -1;
+ hwc->config_base = event->attr.config;
+
+ /*
+ * Ensure all events are on the same cpu so all events are in the
+ * same cpu context, to avoid races on pmu_enable etc.
+ */
+ event->cpu = cluster->on_cpu;
+
+ return 0;
+}
+
+static void l2_cache__event_start(struct perf_event *event, int flags)
+{
+ struct hml2_pmu *cluster;
+ struct hw_perf_event *hwc = &event->hw;
+ int idx = hwc->idx;
+ u32 config;
+ u32 event_cc, event_group;
+
+ hwc->state = 0;
+
+ cluster = get_hml2_pmu(event->cpu);
+ l2_cache__cluster_set_period(cluster, hwc);
+
+ if (hwc->config_base == L2CYCLE_CTR_RAW_CODE) {
+ hml2_pmu__set_evccntcr(0x0);
+ } else {
+ config = hwc->config_base;
+ event_cc = L2_EVT_CODE(config);
+ event_group = L2_EVT_GROUP(config);
+
+ hml2_pmu__set_evcntcr(idx, 0x0);
+ hml2_pmu__set_evtyper(idx, event_group);
+ hml2_pmu__set_resr(cluster, event_group, event_cc);
+ hml2_pmu__set_evfilter_sys_mode(idx);
+ }
+
+ hml2_pmu__counter_enable_interrupt(idx);
+ hml2_pmu__counter_enable(idx);
+}
+
+static void l2_cache__event_stop(struct perf_event *event, int flags)
+{
+ struct hml2_pmu *cluster;
+ struct hw_perf_event *hwc = &event->hw;
+ int idx = hwc->idx;
+
+ if (!(hwc->state & PERF_HES_STOPPED)) {
+ cluster = get_hml2_pmu(event->cpu);
+ hml2_pmu__counter_disable_interrupt(idx);
+ hml2_pmu__counter_disable(idx);
+
+ if (flags & PERF_EF_UPDATE)
+ l2_cache__event_update_from_cluster(event, cluster);
+ hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;
+ }
+}
+
+static int l2_cache__event_add(struct perf_event *event, int flags)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ int idx;
+ int err = 0;
+ struct hml2_pmu *cluster;
+
+ cluster = get_hml2_pmu(event->cpu);
+
+ idx = l2_cache__get_event_idx(cluster, event);
+ if (idx < 0) {
+ err = idx;
+ return err;
+ }
+
+ hwc->idx = idx;
+ hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE;
+ cluster->events[idx] = event;
+ cluster->group_to_counter[L2_EVT_GROUP(hwc->config_base)] = idx;
+ local64_set(&hwc->prev_count, 0ULL);
+
+ if (flags & PERF_EF_START)
+ l2_cache__event_start(event, flags);
+
+ /* Propagate changes to the userspace mapping. */
+ perf_event_update_userpage(event);
+
+ return err;
+}
+
+static void l2_cache__event_del(struct perf_event *event, int flags)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ struct hml2_pmu *cluster;
+ int idx = hwc->idx;
+
+ cluster = get_hml2_pmu(event->cpu);
+ l2_cache__event_stop(event, flags | PERF_EF_UPDATE);
+ cluster->events[idx] = NULL;
+ l2_cache__clear_event_idx(cluster, event);
+
+ perf_event_update_userpage(event);
+}
+
+static void l2_cache__event_read(struct perf_event *event)
+{
+ l2_cache__event_update_from_cluster(event, get_hml2_pmu(event->cpu));
+}
+
+static int l2_cache_filter_match(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ struct hml2_pmu *cluster = get_hml2_pmu(event->cpu);
+ unsigned int group = L2_EVT_GROUP(hwc->config_base);
+
+ /* check for column exclusion: group already in use by another event */
+ if (test_bit(group, cluster->used_groups) &&
+ cluster->events[cluster->group_to_counter[group]] != event)
+ return 0;
+
+ return 1;
+}
+
+static ssize_t l2_cache_pmu_cpumask_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct l2cache_pmu *l2cache_pmu = to_l2cache_pmu(dev_get_drvdata(dev));
+
+ return cpumap_print_to_pagebuf(true, buf, &l2cache_pmu->cpumask);
+}
+
+static DEVICE_ATTR(cpumask, S_IRUGO, l2_cache_pmu_cpumask_show, NULL);
+
+static struct attribute *l2_cache_pmu_cpumask_attrs[] = {
+ &dev_attr_cpumask.attr,
+ NULL,
+};
+
+static struct attribute_group l2_cache_pmu_cpumask_group = {
+ .attrs = l2_cache_pmu_cpumask_attrs,
+};
+
+/* CCG format for perf RAW codes. */
+PMU_FORMAT_ATTR(l2_code, "config:4-11");
+PMU_FORMAT_ATTR(l2_group, "config:0-3");
+static struct attribute *l2_cache_pmu_formats[] = {
+ &format_attr_l2_code.attr,
+ &format_attr_l2_group.attr,
+ NULL,
+};
+
+static struct attribute_group l2_cache_pmu_format_group = {
+ .name = "format",
+ .attrs = l2_cache_pmu_formats,
+};
+
+static const struct attribute_group *l2_cache_pmu_attr_grps[] = {
+ &l2_cache_pmu_format_group,
+ &l2_cache_pmu_cpumask_group,
+ NULL,
+};
+
+/*
+ * Generic device handlers
+ */
+
+static const struct acpi_device_id l2_cache_pmu_acpi_match[] = {
+ { "QCOM8130", },
+ { }
+};
+
+static int get_num_counters(void)
+{
+ int val;
+
+ val = get_l2_indirect_reg(L2PMCR);
+
+ /*
+ * Read number of counters from L2PMCR and add 1
+ * for the cycle counter.
+ */
+ return ((val >> L2PMCR_NUM_EV_SHIFT) & L2PMCR_NUM_EV_MASK) + 1;
+}
+
+static int l2cache_pmu_online_cpu(unsigned int cpu, struct hlist_node *node)
+{
+ struct hml2_pmu *cluster;
+ cpumask_t cluster_online_cpus;
+ struct l2cache_pmu *l2cache_pmu;
+
+ l2cache_pmu = hlist_entry_safe(node, struct l2cache_pmu, node);
+ cluster = get_hml2_pmu(cpu);
+ cpumask_and(&cluster_online_cpus, &cluster->cluster_cpus,
+ cpu_online_mask);
+
+ if (cpumask_weight(&cluster_online_cpus) == 1) {
+ /* all CPUs on this cluster were down, use this one */
+ cluster->on_cpu = cpu;
+ cpumask_set_cpu(cpu, &l2cache_pmu->cpumask);
+ WARN_ON(irq_set_affinity(cluster->irq, cpumask_of(cpu)));
+ }
+
+ return 0;
+}
+
+static int l2cache_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node)
+{
+ struct hml2_pmu *cluster;
+ struct l2cache_pmu *l2cache_pmu;
+ cpumask_t cluster_online_cpus;
+ unsigned int target;
+
+ l2cache_pmu = hlist_entry_safe(node, struct l2cache_pmu, node);
+
+ if (!cpumask_test_and_clear_cpu(cpu, &l2cache_pmu->cpumask))
+ return 0;
+ cluster = get_hml2_pmu(cpu);
+ cpumask_and(&cluster_online_cpus, &cluster->cluster_cpus,
+ cpu_online_mask);
+
+ /* Any other CPU for this cluster which is still online */
+ target = cpumask_any_but(&cluster_online_cpus, cpu);
+ if (target >= nr_cpu_ids)
+ return 0;
+
+ perf_pmu_migrate_context(&l2cache_pmu->pmu, cpu, target);
+ cluster->on_cpu = target;
+ cpumask_set_cpu(target, &l2cache_pmu->cpumask);
+ WARN_ON(irq_set_affinity(cluster->irq, cpumask_of(target)));
+
+ return 0;
+}
+
+static int l2_cache_pmu_probe_cluster(struct device *dev, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev->parent);
+ struct platform_device *sdev = to_platform_device(dev);
+ struct l2cache_pmu *l2cache_pmu = data;
+ struct hml2_pmu *cluster;
+ struct acpi_device *device;
+ unsigned long fw_cluster_id;
+ int cpu;
+ int err;
+ int irq;
+
+ if (acpi_bus_get_device(ACPI_HANDLE(dev), &device))
+ return -ENODEV;
+
+ if (kstrtol(device->pnp.unique_id, 10, &fw_cluster_id) < 0) {
+ dev_err(&pdev->dev, "unable to read ACPI uid\n");
+ return -ENODEV;
+ }
+
+ irq = platform_get_irq(sdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev,
+ "Failed to get valid irq for cluster %ld\n",
+ fw_cluster_id);
+ return irq;
+ }
+
+ cluster = devm_kzalloc(&pdev->dev, sizeof(*cluster), GFP_KERNEL);
+ if (!cluster)
+ return -ENOMEM;
+
+ cluster->l2cache_pmu = l2cache_pmu;
+ for_each_present_cpu(cpu) {
+ if (topology_physical_package_id(cpu) == fw_cluster_id) {
+ cpumask_set_cpu(cpu, &cluster->cluster_cpus);
+ per_cpu(pmu_cluster, cpu) = cluster;
+ }
+ }
+ cluster->irq = irq;
+
+ if (cpumask_empty(&cluster->cluster_cpus)) {
+ dev_err(&pdev->dev, "No CPUs found for L2 cache instance %ld\n",
+ fw_cluster_id);
+ return -ENODEV;
+ }
+
+ /* Pick one CPU to be the preferred one to use in the cluster */
+ cluster->on_cpu = cpumask_first(&cluster->cluster_cpus);
+
+ if (irq_set_affinity(irq, cpumask_of(cluster->on_cpu))) {
+ dev_err(&pdev->dev,
+ "Unable to set irq affinity (irq=%d, cpu=%d)\n",
+ irq, cluster->on_cpu);
+ return -ENODEV;
+ }
+
+ err = devm_request_irq(&pdev->dev, irq, l2_cache__handle_irq,
+ IRQF_NOBALANCING, "l2-cache-pmu", cluster);
+ if (err) {
+ dev_err(&pdev->dev,
+ "Unable to request IRQ%d for L2 PMU counters\n", irq);
+ return err;
+ }
+
+ dev_info(&pdev->dev,
+ "Registered L2 cache PMU instance %ld with %d CPUs\n",
+ fw_cluster_id, cpumask_weight(&cluster->cluster_cpus));
+
+ cluster->pmu_lock = __SPIN_LOCK_UNLOCKED(cluster->pmu_lock);
+ cpumask_set_cpu(cluster->on_cpu, &l2cache_pmu->cpumask);
+
+ hml2_pmu__reset(cluster);
+ l2cache_pmu->num_pmus++;
+
+ return 0;
+}
+
+static int l2_cache_pmu_probe(struct platform_device *pdev)
+{
+ int err;
+ struct l2cache_pmu *l2cache_pmu;
+
+ l2cache_pmu =
+ devm_kzalloc(&pdev->dev, sizeof(*l2cache_pmu), GFP_KERNEL);
+ if (!l2cache_pmu)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, l2cache_pmu);
+ l2cache_pmu->pmu = (struct pmu) {
+ /* suffix is instance id for future use with multiple sockets */
+ .name = "l2cache_0",
+ .task_ctx_nr = perf_invalid_context,
+ .pmu_enable = l2_cache__pmu_enable,
+ .pmu_disable = l2_cache__pmu_disable,
+ .event_init = l2_cache__event_init,
+ .add = l2_cache__event_add,
+ .del = l2_cache__event_del,
+ .start = l2_cache__event_start,
+ .stop = l2_cache__event_stop,
+ .read = l2_cache__event_read,
+ .attr_groups = l2_cache_pmu_attr_grps,
+ .filter_match = l2_cache_filter_match,
+ };
+
+ l2cache_pmu->num_counters = get_num_counters();
+ l2cache_pmu->pdev = pdev;
+ l2_cycle_ctr_idx = l2cache_pmu->num_counters - 1;
+ l2_counter_present_mask = GENMASK(l2cache_pmu->num_counters - 2, 0) |
+ L2PM_CC_ENABLE;
+
+ cpumask_clear(&l2cache_pmu->cpumask);
+
+ /* Read cluster info and initialize each cluster */
+ err = device_for_each_child(&pdev->dev, l2cache_pmu,
+ l2_cache_pmu_probe_cluster);
+ if (err < 0)
+ return err;
+
+ if (l2cache_pmu->num_pmus == 0) {
+ dev_err(&pdev->dev, "No hardware L2 cache PMUs found\n");
+ return -ENODEV;
+ }
+
+ err = perf_pmu_register(&l2cache_pmu->pmu, l2cache_pmu->pmu.name, -1);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Error %d registering L2 cache PMU\n", err);
+ return err;
+ }
+
+ dev_info(&pdev->dev, "Registered L2 cache PMU using %d HW PMUs\n",
+ l2cache_pmu->num_pmus);
+
+ err = cpuhp_state_add_instance_nocalls(CPUHP_AP_PERF_ARM_QCOM_L2_ONLINE,
+ &l2cache_pmu->node);
+
+ return err;
+}
+
+static int l2_cache_pmu_remove(struct platform_device *pdev)
+{
+ struct l2cache_pmu *l2cache_pmu =
+ to_l2cache_pmu(platform_get_drvdata(pdev));
+
+ cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_QCOM_L2_ONLINE,
+ &l2cache_pmu->node);
+ perf_pmu_unregister(&l2cache_pmu->pmu);
+ return 0;
+}
+
+static struct platform_driver l2_cache_pmu_driver = {
+ .driver = {
+ .name = "qcom-l2cache-pmu",
+ .owner = THIS_MODULE,
+ .acpi_match_table = ACPI_PTR(l2_cache_pmu_acpi_match),
+ },
+ .probe = l2_cache_pmu_probe,
+ .remove = l2_cache_pmu_remove,
+};
+
+static int __init register_l2_cache_pmu_driver(void)
+{
+ int err;
+
+ err = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_QCOM_L2_ONLINE,
+ "AP_PERF_ARM_QCOM_L2_ONLINE",
+ l2cache_pmu_online_cpu,
+ l2cache_pmu_offline_cpu);
+ if (err)
+ return err;
+
+ return platform_driver_register(&l2_cache_pmu_driver);
+}
+device_initcall(register_l2_cache_pmu_driver);
diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
index 9b207a8..fbe33d2 100644
--- a/include/linux/cpuhotplug.h
+++ b/include/linux/cpuhotplug.h
@@ -117,6 +117,7 @@ enum cpuhp_state {
CPUHP_AP_PERF_ARM_CCI_ONLINE,
CPUHP_AP_PERF_ARM_CCN_ONLINE,
CPUHP_AP_PERF_ARM_L2X0_ONLINE,
+ CPUHP_AP_PERF_ARM_QCOM_L2_ONLINE,
CPUHP_AP_WORKQUEUE_ONLINE,
CPUHP_AP_RCUTREE_ONLINE,
CPUHP_AP_NOTIFY_ONLINE,
--
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project.
^ 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