* [PATCH 1/3] gpiolib: Export gpiochip_irqchip_irq_valid() to drivers
From: Stephen Boyd @ 2018-01-10 1:58 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180110015848.11480-1-sboyd@codeaurora.org>
Some pinctrl drivers can use the gpiochip irq valid information
to figure out if certain gpios are exposed to the kernel for
usage or not. Expose this API so we can use it in the
pinmux_ops::request ops.
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
drivers/gpio/gpiolib.c | 5 +++--
include/linux/gpio/driver.h | 3 +++
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index b80936a25caa..c18b7b60ea1d 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -1503,14 +1503,15 @@ static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gpiochip)
gpiochip->irq.valid_mask = NULL;
}
-static bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gpiochip,
- unsigned int offset)
+bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gpiochip,
+ unsigned int offset)
{
/* No mask means all valid */
if (likely(!gpiochip->irq.valid_mask))
return true;
return test_bit(offset, gpiochip->irq.valid_mask);
}
+EXPORT_SYMBOL_GPL(gpiochip_irqchip_irq_valid);
/**
* gpiochip_set_cascaded_irqchip() - connects a cascaded irqchip to a gpiochip
diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h
index 7258cd676df4..1ba9a331ec51 100644
--- a/include/linux/gpio/driver.h
+++ b/include/linux/gpio/driver.h
@@ -436,6 +436,9 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gpiochip,
struct lock_class_key *lock_key,
struct lock_class_key *request_key);
+bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gpiochip,
+ unsigned int offset);
+
#ifdef CONFIG_LOCKDEP
/*
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related
* [PATCH 0/3] Support qcom pinctrl protected pins
From: Stephen Boyd @ 2018-01-10 1:58 UTC (permalink / raw)
To: linux-arm-kernel
This patchset proposes a solution to describing the valid
pins for a pin controller in a semi-generic way so that qcom
platforms can expose the pins that are really available.
Typically, this has been done by having drivers and firmware
descriptions only use pins they know they have access to, and that
still works now because we no longer read the pin direction at
boot. But there are still some userspace drivers and debugfs facilities
that don't know what pins are available and attempt to read everything
they can. On qcom platforms, this may lead to a system hang, which isn't
very nice behavior, even if root is the only user that can trigger it.
The proposal is to describe the valid pins and then not allow things to
cause problems by using the invalid pins. Obviously, the firmware may
mess this up, so this is mostly a nice to have feature or a safety net
so that things don't blow up easily.
Stephen Boyd (3):
gpiolib: Export gpiochip_irqchip_irq_valid() to drivers
dt-bindings: pinctrl: Add a ngpios-ranges property
pinctrl: qcom: Don't allow protected pins to be requested
.../bindings/pinctrl/qcom,msm8996-pinctrl.txt | 6 ++
drivers/gpio/gpiolib.c | 5 +-
drivers/pinctrl/qcom/pinctrl-msm.c | 98 +++++++++++++++++++++-
include/linux/gpio/driver.h | 3 +
4 files changed, 106 insertions(+), 6 deletions(-)
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply
* [PATCH 2/2] MAINTAINERS: mtd/nand: update Microchip nand entry
From: Yang, Wenyou @ 2018-01-10 1:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <d8a6b8a3fe05c57972de1c374fcdeb933717396b.1515503733.git.nicolas.ferre@microchip.com>
On 2018/1/9 21:46, Nicolas Ferre wrote:
> Update Wenyou Yang email address.
> Take advantage of this update to move this entry to the MICROCHIP / ATMEL
> location and add the DT binding documentation link.
>
> Signed-off-by: Nicolas Ferre <nicolas.ferre@microchip.com>
Acked-by: Wenyou Yang <wenyou.yang@microchip.com>
> ---
> Hi,
>
> Patch against next-20180109.
> This patch is somehow dependent on the previous one in the series
> ("MAINTAINERS: linux-media: update Microchip ISI and ISC entries") but can be
> rebased easily.
>
> I don't know if it's better to have them added at the end of the development
> cycle or just after rc1: let me know if you plan to take them or if I need to
> rebase them for next cycle.
>
> Best regards,
> Nicolas
>
>
> MAINTAINERS | 15 ++++++++-------
> 1 file changed, 8 insertions(+), 7 deletions(-)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 65c4b59b582f..b48e217d41fb 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -2373,13 +2373,6 @@ F: Documentation/devicetree/bindings/input/atmel,maxtouch.txt
> F: drivers/input/touchscreen/atmel_mxt_ts.c
> F: include/linux/platform_data/atmel_mxt_ts.h
>
> -ATMEL NAND DRIVER
> -M: Wenyou Yang <wenyou.yang@atmel.com>
> -M: Josh Wu <rainyfeeling@outlook.com>
> -L: linux-mtd at lists.infradead.org
> -S: Supported
> -F: drivers/mtd/nand/atmel/*
> -
> ATMEL SAMA5D2 ADC DRIVER
> M: Ludovic Desroches <ludovic.desroches@microchip.com>
> L: linux-iio at vger.kernel.org
> @@ -9110,6 +9103,14 @@ F: drivers/media/platform/atmel/atmel-isi.c
> F: include/media/atmel-isi.h
> F: Documentation/devicetree/bindings/media/atmel-isi.txt
>
> +MICROCHIP / ATMEL NAND DRIVER
> +M: Wenyou Yang <wenyou.yang@microchip.com>
> +M: Josh Wu <rainyfeeling@outlook.com>
> +L: linux-mtd at lists.infradead.org
> +S: Supported
> +F: drivers/mtd/nand/atmel/*
> +F: Documentation/devicetree/bindings/mtd/atmel-nand.txt
> +
> MICROCHIP KSZ SERIES ETHERNET SWITCH DRIVER
> M: Woojung Huh <Woojung.Huh@microchip.com>
> M: Microchip Linux Driver Support <UNGLinuxDriver@microchip.com>
Best Regards,
Wenyou Yang
^ permalink raw reply
* [PATCH 1/2] MAINTAINERS: linux-media: update Microchip ISI and ISC entries
From: Yang, Wenyou @ 2018-01-10 1:52 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <eb6b3cbe8e48faee7e88eca0649e42cbde91ffa6.1515503733.git.nicolas.ferre@microchip.com>
On 2018/1/9 21:46, Nicolas Ferre wrote:
> These two image capture interface drivers are now handled
> by Wenyou Yang.
> I benefit from this change to update the two entries by correcting the
> binding documentation link for ISC and moving the ISI to the new
> MICROCHIP / ATMEL location.
>
> Signed-off-by: Nicolas Ferre <nicolas.ferre@microchip.com>
Acked-by: Wenyou Yang <wenyou.yang@microchip.com>
> ---
> Hi,
>
> Patch against next-20180109.
> Note that I didn't find it useful to have several patches for these changes.
> Tell me if you feel differently.
>
> I would like to have the Ack from Ludovic and Wenyou obviously. I don't know if
> Songjun can answer as he's not with Microchip anymore.
>
> Best regards,
> Nicolas
>
> MAINTAINERS | 19 ++++++++++---------
> 1 file changed, 10 insertions(+), 9 deletions(-)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index a7d10a2bb980..65c4b59b582f 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -2353,13 +2353,6 @@ L: linux-i2c at vger.kernel.org
> S: Supported
> F: drivers/i2c/busses/i2c-at91.c
>
> -ATMEL ISI DRIVER
> -M: Ludovic Desroches <ludovic.desroches@microchip.com>
> -L: linux-media at vger.kernel.org
> -S: Supported
> -F: drivers/media/platform/atmel/atmel-isi.c
> -F: include/media/atmel-isi.h
> -
> ATMEL LCDFB DRIVER
> M: Nicolas Ferre <nicolas.ferre@microchip.com>
> L: linux-fbdev at vger.kernel.org
> @@ -9102,12 +9095,20 @@ S: Maintained
> F: drivers/crypto/atmel-ecc.*
>
> MICROCHIP / ATMEL ISC DRIVER
> -M: Songjun Wu <songjun.wu@microchip.com>
> +M: Wenyou Yang <wenyou.yang@microchip.com>
> L: linux-media at vger.kernel.org
> S: Supported
> F: drivers/media/platform/atmel/atmel-isc.c
> F: drivers/media/platform/atmel/atmel-isc-regs.h
> -F: devicetree/bindings/media/atmel-isc.txt
> +F: Documentation/devicetree/bindings/media/atmel-isc.txt
> +
> +MICROCHIP / ATMEL ISI DRIVER
> +M: Wenyou Yang <wenyou.yang@microchip.com>
> +L: linux-media at vger.kernel.org
> +S: Supported
> +F: drivers/media/platform/atmel/atmel-isi.c
> +F: include/media/atmel-isi.h
> +F: Documentation/devicetree/bindings/media/atmel-isi.txt
>
> MICROCHIP KSZ SERIES ETHERNET SWITCH DRIVER
> M: Woojung Huh <Woojung.Huh@microchip.com>
Best Regards,
Wenyou Yang
^ permalink raw reply
* [PATCH 3/9] soc: samsung: pmu: Add the PMU data of exynos5433 to support low-power state
From: Chanwoo Choi @ 2018-01-10 1:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <b639da18-a069-aeb7-ceca-30c79b1c25c8@arm.com>
On 2018? 01? 09? 23:11, Sudeep Holla wrote:
>
>
> On 09/01/18 07:59, Chanwoo Choi wrote:
>> This patch adds the PMU (Power Management Unit) data of exynos5433 SoC
>> in order to support the various power modes. Each power mode has
>> the different value for reducing the power-consumption.
>>
>> Signed-off-by: Jonghwa Lee <jonghwa3.lee@samsung.com>
>> Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
>> ---
>> arch/arm/mach-exynos/common.h | 2 -
>> drivers/soc/samsung/Makefile | 3 +-
>> drivers/soc/samsung/exynos-pmu.c | 1 +
>> drivers/soc/samsung/exynos-pmu.h | 2 +
>> drivers/soc/samsung/exynos5433-pmu.c | 286 ++++++++++++++++++++++++++++
>> include/linux/soc/samsung/exynos-regs-pmu.h | 148 ++++++++++++++
>> 6 files changed, 439 insertions(+), 3 deletions(-)
>> create mode 100644 drivers/soc/samsung/exynos5433-pmu.c
>>
>
>> diff --git a/drivers/soc/samsung/exynos5433-pmu.c b/drivers/soc/samsung/exynos5433-pmu.c
>> new file mode 100644
>> index 000000000000..2571e61522f0
>> --- /dev/null
>> +++ b/drivers/soc/samsung/exynos5433-pmu.c
>> @@ -0,0 +1,286 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +//
>> +// Copyright (c) 2018 Samsung Electronics Co., Ltd.
>> +// Copyright (c) Jonghwa Lee <jonghwa3.lee@samsung.com>
>> +// Copyright (c) Chanwoo Choi <cw00.choi@samsung.com>
>> +//
>> +// EXYNOS5433 - CPU PMU (Power Management Unit) support
>> +
>> +#include <linux/soc/samsung/exynos-regs-pmu.h>
>> +#include <linux/soc/samsung/exynos-pmu.h>
>> +
>> +#include "exynos-pmu.h"
>> +
>> +static struct exynos_pmu_conf exynos5433_pmu_config[] = {
>> + /* { .offset = address, .val = { AFTR, LPA, SLEEP } } */
>> + { EXYNOS5433_ATLAS_CPU0_SYS_PWR_REG, { 0x0, 0x0, 0x8 } },
>> + { EXYNOS5433_DIS_IRQ_ATLAS_CPU0_CENTRAL_SYS_PWR_REG, { 0x0, 0x0, 0x0 } },
>> + { EXYNOS5433_ATLAS_CPU1_SYS_PWR_REG, { 0x0, 0x0, 0x8 } },
>> + { EXYNOS5433_DIS_IRQ_ATLAS_CPU1_CENTRAL_SYS_PWR_REG, { 0x0, 0x0, 0x0 } },
>> + { EXYNOS5433_ATLAS_CPU2_SYS_PWR_REG, { 0x0, 0x0, 0x8 } },
>> + { EXYNOS5433_DIS_IRQ_ATLAS_CPU2_CENTRAL_SYS_PWR_REG, { 0x0, 0x0, 0x0 } },
>> + { EXYNOS5433_ATLAS_CPU3_SYS_PWR_REG, { 0x0, 0x0, 0x8 } },
>> + { EXYNOS5433_DIS_IRQ_ATLAS_CPU3_CENTRAL_SYS_PWR_REG, { 0x0, 0x0, 0x0 } },
>> + { EXYNOS5433_APOLLO_CPU0_SYS_PWR_REG, { 0x0, 0x0, 0x8 } },
>> + { EXYNOS5433_DIS_IRQ_APOLLO_CPU0_CENTRAL_SYS_PWR_REG, { 0x0, 0x0, 0x0 } },
>> + { EXYNOS5433_APOLLO_CPU1_SYS_PWR_REG, { 0x0, 0x0, 0x8 } },
>> + { EXYNOS5433_DIS_IRQ_APOLLO_CPU1_CENTRAL_SYS_PWR_REG, { 0x0, 0x0, 0x0 } },
>> + { EXYNOS5433_APOLLO_CPU2_SYS_PWR_REG, { 0x0, 0x0, 0x8 } },
>> + { EXYNOS5433_DIS_IRQ_APOLLO_CPU2_CENTRAL_SYS_PWR_REG, { 0x0, 0x0, 0x0 } },
>> + { EXYNOS5433_APOLLO_CPU3_SYS_PWR_REG, { 0x0, 0x0, 0x8 } },
>> + { EXYNOS5433_DIS_IRQ_APOLLO_CPU3_CENTRAL_SYS_PWR_REG, { 0x0, 0x0, 0x0 } },
>> + { EXYNOS5433_ATLAS_NONCPU_SYS_PWR_REG, { 0x0, 0x0, 0x8 } },
>> + { EXYNOS5433_APOLLO_NONCPU_SYS_PWR_REG, { 0x0, 0x0, 0x8 } },
>
>
> 1. First of all why do you need any of these CPU related PMU config
> registers in kernel ? From the information I gathered this is ARM64
> SoC using PSCI. These are needed just in PSCI implementation and not
> in kernel. So can you elaborate on why there are present here ?
The 32bit Exynos used the 'smc' call to enter the suspend mode
and need to handle the PMU registers.
Even if PSCI replaces the 'smc' call on the Exynos5433,
the Exynos5433's document requires the handling of PMU config
related to CPU for the suspend mode.
IMHO, If the secure OS implemented the all something related to CPU,
it might be unnecessary to handle the PMU registers. I think that
it depend on how to design the SoC by H/W Architect. This is just my opinion.
>
> 2. Are there any public documents that these names map to ?
There is no public document. It is confidential.
> If there is none, please replace these codenames(ATLAS, APOLLO) with
> appropriately.
In the Exynos5433, 'apollo' indicates the LITTLE cores (cpu0-3, cortex-a53)
and 'atlas' indicates the big cores (cpu4-7, cortex-a57)
Exynos5433 already used the 'apollo' and 'atlas' on clk-exynos5433.c driver
and thermal device-tree node. It is better to use the original register name
in the document in order to reduce the confusion of the change of register name
even if document is not public.
Also, exynos7 used the 'atlas' word for big cores.
>
> Sorry if these are already answered, just point me to those threads.
>
--
Best Regards,
Chanwoo Choi
Samsung Electronics
^ permalink raw reply
* [PATCH v2 4/6] arm: Add icache invalidation on switch_mm for Cortex-A15
From: Florian Fainelli @ 2018-01-10 1:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <3ab22cbc-76a2-01c6-d016-61bc874cd003@arm.com>
On 01/09/2018 05:33 PM, Andr? Przywara wrote:
> On 10/01/18 01:28, Florian Fainelli wrote:
>> On 01/08/2018 10:55 AM, Marc Zyngier wrote:
>>> In order to avoid aliasing attacks against the branch predictor,
>>> Cortex-A15 require to invalidate the BTB when switching
>>> from one user context to another. The only way to do so on this
>>> CPU is to perform an ICIALLU, having set ACTLR[0] to 1 from secure
>>> mode.
>>>
>>> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
>>> ---
>>
>> [snip]
>>
>>> diff --git a/arch/arm/mm/proc-v7-3level.S b/arch/arm/mm/proc-v7-3level.S
>>> index f6adfe88ead2..0a2245b309e5 100644
>>> --- a/arch/arm/mm/proc-v7-3level.S
>>> +++ b/arch/arm/mm/proc-v7-3level.S
>>> @@ -71,6 +71,22 @@ ENTRY(cpu_v7_switch_mm)
>>> ENDPROC(cpu_v7_switch_mm)
>>> ENDPROC(cpu_v7_btbinv_switch_mm)
>>>
>>> +/*
>>> + * Cortex-A15 requires ACTLR[0] to be set from secure in order
>>> + * for the icache invalidation to also invalidate the BTB.
>>
>> Considering that writes are ignored when we don't have the correct
>> permission level, how about set try to set this bit from the
>> __v7_ca15mp_setup and __v7_b15mp_setup labels just like we are setting
>> the SMP_EN bit for the poor bastards out there stuck with possibly
>> frozen bootloaders/ATF?
>
> Even when writes to ACTLR are allowed by secure world, this only
> actually applies to the SMP bit:
> ARM DDI0438H ARM Cortex-A15 TRM, 4.3.28 Auxiliary Control Register:
> "-- Read/write in Non-secure PL1 and PL2 modes if NSACR.NS_SMP is 1. In
> this case, all bits are write-ignored except for the SMP bit."
>
> So: good idea, but no luck here :-(
Looks like I failed basic reading exercise here, thanks ;)
--
Florian
^ permalink raw reply
* [PATCH v2 4/6] arm: Add icache invalidation on switch_mm for Cortex-A15
From: André Przywara @ 2018-01-10 1:33 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <a11193e5-0f0b-3280-e2b2-c0665676d230@gmail.com>
On 10/01/18 01:28, Florian Fainelli wrote:
> On 01/08/2018 10:55 AM, Marc Zyngier wrote:
>> In order to avoid aliasing attacks against the branch predictor,
>> Cortex-A15 require to invalidate the BTB when switching
>> from one user context to another. The only way to do so on this
>> CPU is to perform an ICIALLU, having set ACTLR[0] to 1 from secure
>> mode.
>>
>> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
>> ---
>
> [snip]
>
>> diff --git a/arch/arm/mm/proc-v7-3level.S b/arch/arm/mm/proc-v7-3level.S
>> index f6adfe88ead2..0a2245b309e5 100644
>> --- a/arch/arm/mm/proc-v7-3level.S
>> +++ b/arch/arm/mm/proc-v7-3level.S
>> @@ -71,6 +71,22 @@ ENTRY(cpu_v7_switch_mm)
>> ENDPROC(cpu_v7_switch_mm)
>> ENDPROC(cpu_v7_btbinv_switch_mm)
>>
>> +/*
>> + * Cortex-A15 requires ACTLR[0] to be set from secure in order
>> + * for the icache invalidation to also invalidate the BTB.
>
> Considering that writes are ignored when we don't have the correct
> permission level, how about set try to set this bit from the
> __v7_ca15mp_setup and __v7_b15mp_setup labels just like we are setting
> the SMP_EN bit for the poor bastards out there stuck with possibly
> frozen bootloaders/ATF?
Even when writes to ACTLR are allowed by secure world, this only
actually applies to the SMP bit:
ARM DDI0438H ARM Cortex-A15 TRM, 4.3.28 Auxiliary Control Register:
"-- Read/write in Non-secure PL1 and PL2 modes if NSACR.NS_SMP is 1. In
this case, all bits are write-ignored except for the SMP bit."
So: good idea, but no luck here :-(
Cheers,
Andre.
^ permalink raw reply
* [PATCH v2 4/6] arm: Add icache invalidation on switch_mm for Cortex-A15
From: Florian Fainelli @ 2018-01-10 1:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180108185533.9698-5-marc.zyngier@arm.com>
On 01/08/2018 10:55 AM, Marc Zyngier wrote:
> In order to avoid aliasing attacks against the branch predictor,
> Cortex-A15 require to invalidate the BTB when switching
> from one user context to another. The only way to do so on this
> CPU is to perform an ICIALLU, having set ACTLR[0] to 1 from secure
> mode.
>
> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
> ---
[snip]
> diff --git a/arch/arm/mm/proc-v7-3level.S b/arch/arm/mm/proc-v7-3level.S
> index f6adfe88ead2..0a2245b309e5 100644
> --- a/arch/arm/mm/proc-v7-3level.S
> +++ b/arch/arm/mm/proc-v7-3level.S
> @@ -71,6 +71,22 @@ ENTRY(cpu_v7_switch_mm)
> ENDPROC(cpu_v7_switch_mm)
> ENDPROC(cpu_v7_btbinv_switch_mm)
>
> +/*
> + * Cortex-A15 requires ACTLR[0] to be set from secure in order
> + * for the icache invalidation to also invalidate the BTB.
Considering that writes are ignored when we don't have the correct
permission level, how about set try to set this bit from the
__v7_ca15mp_setup and __v7_b15mp_setup labels just like we are setting
the SMP_EN bit for the poor bastards out there stuck with possibly
frozen bootloaders/ATF?
--
Florian
^ permalink raw reply
* [PATCH v3 2/2] dt-bindings: mailbox: Add Xilinx IPI Mailbox
From: Jiaying Liang @ 2018-01-10 1:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CABb+yY00n6fLiQXftj-TMhT6q=KopZMkLYr6DpYE8CvxH-gz8Q@mail.gmail.com>
> -----Original Message-----
> From: Jassi Brar [mailto:jassisinghbrar at gmail.com]
> Sent: Tuesday, January 09, 2018 12:00 AM
> To: Jiaying Liang <jliang@xilinx.com>
> Cc: Michal Simek <michal.simek@xilinx.com>; Rob Herring
> <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>; linux-arm-
> kernel at lists.infradead.org; Devicetree List <devicetree@vger.kernel.org>;
> Linux Kernel Mailing List <linux-kernel@vger.kernel.org>; Jiaying Liang
> <jliang@xilinx.com>
> Subject: Re: [PATCH v3 2/2] dt-bindings: mailbox: Add Xilinx IPI Mailbox
>
> On Fri, Jan 5, 2018 at 5:21 AM, Wendy Liang <wendy.liang@xilinx.com> wrote:
> > Xilinx ZynqMP IPI(Inter Processor Interrupt) is a hardware block in
> > ZynqMP SoC used for the communication between various processor
> > systems.
> >
> > Signed-off-by: Wendy Liang <jliang@xilinx.com>
> > ---
> > .../bindings/mailbox/xlnx,zynqmp-ipi-mailbox.txt | 104
> +++++++++++++++++++++
> > 1 file changed, 104 insertions(+)
> > create mode 100644
> > Documentation/devicetree/bindings/mailbox/xlnx,zynqmp-ipi-mailbox.txt
> >
> > diff --git
> > a/Documentation/devicetree/bindings/mailbox/xlnx,zynqmp-ipi-mailbox.tx
> > t
> > b/Documentation/devicetree/bindings/mailbox/xlnx,zynqmp-ipi-
> mailbox.tx
> > t
> > new file mode 100644
> > index 0000000..5e270a3
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/mailbox/xlnx,zynqmp-ipi-
> mailbo
> > +++ x.txt
> > @@ -0,0 +1,104 @@
> > +Xilinx IPI Mailbox Controller
> > +========================================
> > +
> > +The Xilinx IPI(Inter Processor Interrupt) mailbox controller is to
> > +manage messaging between two Xilinx Zynq UltraScale+ MPSoC IPI
> > +agents. Each IPI agent owns registers used for notification and buffers for
> message.
> > +
> > + +-------------------------------------+
> > + | Xilinx ZynqMP IPI Controller |
> > + +-------------------------------------+
> > + +--------------------------------------------------+
> > +ATF | |
> > + | |
> > + | |
> > + +--------------------------+ |
> > + | |
> > + | |
> > + +--------------------------------------------------+
> > + +------------------------------------------+
> > + | +----------------+ +----------------+ |
> > +Hardware | | IPI Agent | | IPI Buffers | |
> > + | | Registers | | | |
> > + | | | | | |
> > + | +----------------+ +----------------+ |
> > + | |
> > + | Xilinx IPI Agent Block |
> > + +------------------------------------------+
> > +
> > +
> > +Controller Device Node:
> > +===========================
> > +Required properties:
> > +--------------------
> > +- compatible: Shall be: "xlnx,zynqmp-ipi-mailbox"
> > +- reg: IPI buffers address ranges
> > +- reg-names: Names of the reg resources. It should have:
> > + * local_request_region
> > + - IPI request msg buffer written by local and read
> > + by remote
> > + * local_response_region
> > + - IPI response msg buffer written by local and read
> > + by remote
> > + * remote_request_region
> > + - IPI request msg buffer written by remote and read
> > + by local
> > + * remote_response_region
> > + - IPI response msg buffer written by remote and read
> > + by local
> >
> shmem is option and external to the controller. It should be passed via
> client's binding.
> Please have a look at Sudeep's proposed patch
> https://www.spinics.net/lists/arm-kernel/msg626120.html
[Wendy] thanks for the link, but those 'buffers" are registers in the hardware
but not memory. It looks like a bit hacky to access them as memory.
>
> > +- #mbox-cells: Shall be 1. It contains:
> > + * tx(0) or rx(1) channel
> > +- xlnx,ipi-ids: Xilinx IPI agent IDs of the two peers of the
> > + Xilinx IPI communication channel.
> > +- interrupt-parent: Phandle for the interrupt controller
> > +- interrupts: Interrupt information corresponding to the
> > + interrupt-names property.
> > +
> > +Optional properties:
> > +--------------------
> > +- method: The method of accessing the IPI agent registers.
> > + Permitted values are: "smc" and "hvc". Default is
> > + "smc".
> > +
> Andre almost implemented the generic driver. Can you please have a look at
> https://www.spinics.net/lists/arm-kernel/msg595416.html
> and see if you can just finish it off?
[Wendy] This mailbox controller is about to use Xilinx IPI hardware as mailbox.
We use it to send notification/short request to firmware (usually running on
another core on SoC), and also to receive notification/short request from firmware.
Interrupt is used in the receiving direction. It looks different to the usage of
mailbox driver from the link. Is there a plan to extend the ARM SMC mailbox driver
to both trigger firmware actions and receive request from firmware?
Thanks,
Wendy
>
> Thanks
^ permalink raw reply
* [PATCH v2 1/7] PCI: aardvark: fix logic in PCI configuration read/write functions
From: Bjorn Helgaas @ 2018-01-10 1:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180109174918.5c4b9ee6@windsurf.lan>
[+cc Lorenzo, since he takes care of this now]
On Tue, Jan 09, 2018 at 05:49:18PM +0100, Thomas Petazzoni wrote:
> Hello Bjorn,
>
> Again, reviving this very old thread :-)
>
> On Thu, 5 Oct 2017 12:23:30 -0500, Bjorn Helgaas wrote:
>
> > > - if (PCI_SLOT(devfn) != 0) {
> > > + if ((bus->number == pcie->root_bus_nr) && (PCI_SLOT(devfn) != 0)) {
> >
> > I'm fine with this, but please take a look at these:
> >
> > 8e7ca8ca5fd8 PCI: xilinx: Relax device number checking to allow SR-IOV
> > e18934b5e9c7 PCI: designware: Relax device number checking to allow SR-IOV
> > d99e30b7936a PCI: altera: Relax device number checking to allow SR-IOV
> >
> > and make sure that reasoning doesn't apply here, too.
> >
> > http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=8e7ca8ca5fd8
> > http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=e18934b5e9c7
> > http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=d99e30b7936a
>
> The original code for xilinx/designware/altera was doing:
>
> if (bus->number == port->root_busno && devfn > 0)
> return false;
>
> if (bus->primary == port->root_busno && devfn > 0)
> return false;
>
> I.e, it was checking both if bus->number *and* bus->primary were equal
> to port->root_busno.
>
> The commit you points removed the check on bus->primary, keeping the
> check on bus->number.
>
> Your patch for the Aadvark driver only adds a check on bus->number, i.e
> exactly what the xilinx/designware/altera code is still doing today:
This is a long time ago and I could have forgotten, but I don't think
this is *my* patch, is it?
> Altera:
>
> /* access only one slot on each root port */
> if (bus->number == pcie->root_bus_nr && dev > 0)
> return false;
>
> Designware:
>
> /* access only one slot on each root port */
> if (bus->number == pp->root_bus_nr && dev > 0)
> return 0;
>
> Xilinx:
>
> /* Only one device down on each root port */
> if (bus->number == port->root_busno && devfn > 0)
> return false;
>
> Aardvark (with our patch):
>
> if ((bus->number == pcie->root_bus_nr) && (PCI_SLOT(devfn) != 0)) {
> *val = 0xffffffff;
> return PCIBIOS_DEVICE_NOT_FOUND;
> }
>
> So we're doing exactly the same thing.
>
> Do you agree ?
I do agree. I can't remember what I was thinking when I first
responded.
I *would* suggest making an advk_pcie_valid_device() so your structure
matches the other drivers. I know it seems trivial when you're mostly
looking at one driver, but it really helps those who pay attention to
all of them.
Bjorn
^ permalink raw reply
* [xlnx:zynqmp/soc 3/3] xlnx_vcu.c:undefined reference to `devm_ioremap_nocache'
From: kbuild test robot @ 2018-01-10 0:01 UTC (permalink / raw)
To: linux-arm-kernel
tree: https://github.com/Xilinx/linux-xlnx zynqmp/soc
head: cee8113a295acfc4cd25728d7c3d44e6bc3bbff9
commit: cee8113a295acfc4cd25728d7c3d44e6bc3bbff9 [3/3] soc: xilinx: xlnx_vcu: Add Xilinx ZYNQMP VCU logicoreIP init driver
config: um-allyesconfig (attached as .config)
compiler: gcc-7 (Debian 7.2.0-12) 7.2.1 20171025
reproduce:
git checkout cee8113a295acfc4cd25728d7c3d44e6bc3bbff9
# save the attached .config to linux build tree
make ARCH=um
All errors (new ones prefixed by >>):
arch/um/drivers/vde.o: In function `vde_open_real':
(.text+0x951): warning: Using 'getgrnam' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
(.text+0x79c): warning: Using 'getpwuid' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
(.text+0xab5): warning: Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
arch/um/drivers/pcap.o: In function `pcap_nametoaddr':
(.text+0xdee5): warning: Using 'gethostbyname' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
arch/um/drivers/pcap.o: In function `pcap_nametonetaddr':
(.text+0xdf85): warning: Using 'getnetbyname' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
arch/um/drivers/pcap.o: In function `pcap_nametoproto':
(.text+0xe1a5): warning: Using 'getprotobyname' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
arch/um/drivers/pcap.o: In function `pcap_nametoport':
(.text+0xdfd7): warning: Using 'getservbyname' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
drivers/soc/xilinx/xlnx_vcu.o: In function `xvcu_probe':
>> xlnx_vcu.c:(.text+0x116): undefined reference to `devm_ioremap_nocache'
xlnx_vcu.c:(.text+0x1ae): undefined reference to `devm_ioremap_nocache'
collect2: error: ld returned 1 exit status
---
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: 19860 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20180110/9b37f20e/attachment-0001.gz>
^ permalink raw reply
* [PATCH v1 01/16] virtio: Validate queue pfn for 32bit transports
From: Michael S. Tsirkin @ 2018-01-09 23:29 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180109190414.4017-2-suzuki.poulose@arm.com>
On Tue, Jan 09, 2018 at 07:03:56PM +0000, Suzuki K Poulose wrote:
> virtio-mmio using virtio-v1 and virtio legacy pci use a 32bit PFN
> for the queue. If the queue pfn is too large to fit in 32bits, which
> we could hit on arm64 systems with 52bit physical addresses (even with
> 64K page size), we simply miss out a proper link to the other side of
> the queue.
>
> Add a check to validate the PFN, rather than silently breaking
> the devices.
>
> Cc: "Michael S. Tsirkin" <mst@redhat.com>
> Cc: Jason Wang <jasowang@redhat.com>
> Cc: Marc Zyngier <marc.zyngier@arm.com>
> Cc: Christoffer Dall <cdall@linaro.org>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Could you guys please work on virtio 1 support in
for virtio mmio in qemu though?
It's not a lot of code.
> ---
> drivers/virtio/virtio_mmio.c | 19 ++++++++++++++++---
> drivers/virtio/virtio_pci_legacy.c | 11 +++++++++--
> 2 files changed, 25 insertions(+), 5 deletions(-)
I'd rather see this as 2 patches.
> diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c
> index a9192fe4f345..47109baf37f7 100644
> --- a/drivers/virtio/virtio_mmio.c
> +++ b/drivers/virtio/virtio_mmio.c
> @@ -358,6 +358,7 @@ static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned index,
> struct virtqueue *vq;
> unsigned long flags;
> unsigned int num;
> + u64 addr;
> int err;
>
> if (!name)
> @@ -394,16 +395,26 @@ static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned index,
> goto error_new_virtqueue;
> }
>
> + addr = virtqueue_get_desc_addr(vq);
> + /*
> + * virtio-mmio v1 uses a 32bit QUEUE PFN. If we have something that
> + * doesn't fit in 32bit, fail the setup rather than pretending to
> + * be successful.
> + */
> + if (vm_dev->version == 1 && (addr >> (PAGE_SHIFT + 32))) {
> + dev_err(&vdev->dev, "virtio-mmio: queue address too large\n");
> + err = -ENOMEM;
> + goto error_bad_pfn;
> + }
> +
Can you please move this below to where it's actually used?
> /* Activate the queue */
> writel(virtqueue_get_vring_size(vq), vm_dev->base + VIRTIO_MMIO_QUEUE_NUM);
> if (vm_dev->version == 1) {
> writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_QUEUE_ALIGN);
> - writel(virtqueue_get_desc_addr(vq) >> PAGE_SHIFT,
> + writel(addr >> PAGE_SHIFT,
> vm_dev->base + VIRTIO_MMIO_QUEUE_PFN);
> } else {
> - u64 addr;
>
> - addr = virtqueue_get_desc_addr(vq);
> writel((u32)addr, vm_dev->base + VIRTIO_MMIO_QUEUE_DESC_LOW);
> writel((u32)(addr >> 32),
> vm_dev->base + VIRTIO_MMIO_QUEUE_DESC_HIGH);
> @@ -430,6 +441,8 @@ static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned index,
>
> return vq;
>
> +error_bad_pfn:
> + vring_del_virtqueue(vq);
> error_new_virtqueue:
> if (vm_dev->version == 1) {
> writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_PFN);
> diff --git a/drivers/virtio/virtio_pci_legacy.c b/drivers/virtio/virtio_pci_legacy.c
> index 2780886e8ba3..099d2cfb47b3 100644
> --- a/drivers/virtio/virtio_pci_legacy.c
> +++ b/drivers/virtio/virtio_pci_legacy.c
> @@ -122,6 +122,7 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev,
> struct virtqueue *vq;
> u16 num;
> int err;
> + u64 q_pfn;
>
> /* Select the queue we're interested in */
> iowrite16(index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL);
> @@ -141,9 +142,15 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev,
> if (!vq)
> return ERR_PTR(-ENOMEM);
>
> + q_pfn = virtqueue_get_desc_addr(vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT;
> + if (q_pfn >> 32) {
> + dev_err(&vp_dev->pci_dev->dev, "virtio-pci queue PFN too large\n");
> + err = -ENOMEM;
> + goto out_deactivate;
You never set up the address, it's cleaner to add another target
and not reset it.
> + }
> +
> /* activate the queue */
> - iowrite32(virtqueue_get_desc_addr(vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT,
> - vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
> + iowrite32((u32)q_pfn, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
>
> vq->priv = (void __force *)vp_dev->ioaddr + VIRTIO_PCI_QUEUE_NOTIFY;
>
> --
> 2.13.6
^ permalink raw reply
* [PATCH] arm64: Stop printing the virtual memory layout
From: Kees Cook @ 2018-01-09 23:02 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAGXu5jKH+XZLor5hApTpzbeq0SBz4Qo-AarAFjAfWXM5p3uSVA@mail.gmail.com>
On Tue, Dec 19, 2017 at 1:04 PM, Kees Cook <keescook@chromium.org> wrote:
> On Tue, Dec 19, 2017 at 11:28 AM, Laura Abbott <labbott@redhat.com> wrote:
>> Printing kernel addresses should be done in limited circumstances, mostly
>> for debugging purposes. Printing out the virtual memory layout at every
>> kernel bootup doesn't really fall into this category so delete the prints.
>> There are other ways to get the same information.
>
> In looking at this patch, I wonder: is there anything listed here that
> is _missing_ from CONFIG_PTDUMP? I would expect all of these to
> already be listed there, but I thought I'd ask...
>
> Regardless:
>
> Acked-by: Kees Cook <keescook@chromium.org>
>
> -Kees
>
>>
>> Signed-off-by: Laura Abbott <labbott@redhat.com>
Did this patch get picked up? I don't see it in -next.
-Kees
>> ---
>> Follow up to my previous proposal to switch all these to %px
>> ---
>> arch/arm64/mm/init.c | 43 -------------------------------------------
>> 1 file changed, 43 deletions(-)
>>
>> diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c
>> index 5960bef0170d..672094ed7e07 100644
>> --- a/arch/arm64/mm/init.c
>> +++ b/arch/arm64/mm/init.c
>> @@ -599,49 +599,6 @@ void __init mem_init(void)
>>
>> mem_init_print_info(NULL);
>>
>> -#define MLK(b, t) b, t, ((t) - (b)) >> 10
>> -#define MLM(b, t) b, t, ((t) - (b)) >> 20
>> -#define MLG(b, t) b, t, ((t) - (b)) >> 30
>> -#define MLK_ROUNDUP(b, t) b, t, DIV_ROUND_UP(((t) - (b)), SZ_1K)
>> -
>> - pr_notice("Virtual kernel memory layout:\n");
>> -#ifdef CONFIG_KASAN
>> - pr_notice(" kasan : 0x%16lx - 0x%16lx (%6ld GB)\n",
>> - MLG(KASAN_SHADOW_START, KASAN_SHADOW_END));
>> -#endif
>> - pr_notice(" modules : 0x%16lx - 0x%16lx (%6ld MB)\n",
>> - MLM(MODULES_VADDR, MODULES_END));
>> - pr_notice(" vmalloc : 0x%16lx - 0x%16lx (%6ld GB)\n",
>> - MLG(VMALLOC_START, VMALLOC_END));
>> - pr_notice(" .text : 0x%p" " - 0x%p" " (%6ld KB)\n",
>> - MLK_ROUNDUP(_text, _etext));
>> - pr_notice(" .rodata : 0x%p" " - 0x%p" " (%6ld KB)\n",
>> - MLK_ROUNDUP(__start_rodata, __init_begin));
>> - pr_notice(" .init : 0x%p" " - 0x%p" " (%6ld KB)\n",
>> - MLK_ROUNDUP(__init_begin, __init_end));
>> - pr_notice(" .data : 0x%p" " - 0x%p" " (%6ld KB)\n",
>> - MLK_ROUNDUP(_sdata, _edata));
>> - pr_notice(" .bss : 0x%p" " - 0x%p" " (%6ld KB)\n",
>> - MLK_ROUNDUP(__bss_start, __bss_stop));
>> - pr_notice(" fixed : 0x%16lx - 0x%16lx (%6ld KB)\n",
>> - MLK(FIXADDR_START, FIXADDR_TOP));
>> - pr_notice(" PCI I/O : 0x%16lx - 0x%16lx (%6ld MB)\n",
>> - MLM(PCI_IO_START, PCI_IO_END));
>> -#ifdef CONFIG_SPARSEMEM_VMEMMAP
>> - pr_notice(" vmemmap : 0x%16lx - 0x%16lx (%6ld GB maximum)\n",
>> - MLG(VMEMMAP_START, VMEMMAP_START + VMEMMAP_SIZE));
>> - pr_notice(" 0x%16lx - 0x%16lx (%6ld MB actual)\n",
>> - MLM((unsigned long)phys_to_page(memblock_start_of_DRAM()),
>> - (unsigned long)virt_to_page(high_memory)));
>> -#endif
>> - pr_notice(" memory : 0x%16lx - 0x%16lx (%6ld MB)\n",
>> - MLM(__phys_to_virt(memblock_start_of_DRAM()),
>> - (unsigned long)high_memory));
>> -
>> -#undef MLK
>> -#undef MLM
>> -#undef MLK_ROUNDUP
>> -
>> /*
>> * Check boundaries twice: Some fundamental inconsistencies can be
>> * detected at build time already.
>> --
>> 2.14.3
>>
>
>
>
> --
> Kees Cook
> Pixel Security
--
Kees Cook
Pixel Security
^ permalink raw reply
* arm: Is VFP hotplug notifiers wrong?
From: okuno.kohji at jp.panasonic.com @ 2018-01-09 22:51 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAOMZO5BKanS2xuAvG8i=JHY3zEaCWN4doyvB5o6HDEx++MYQwQ@mail.gmail.com>
Dear Fabio and Russel,
Fabio, thank you for providing your patch. It should be work good.
Unfortunately, I do not have an environment to try out the latest kernel right now.
Indeed, I encountered the issue(*) for VFP in using kernel 3.10.x. Then, I noticed the problem at the master.
kernel 3.10.x does not introduce this fix.
(*) Under certain conditions, VFP registers becomes undefined values after resume.
- a last thread using VFP before suspend was executed by CPU other than CPU#0
- the first thread using VFP after resume was the same thread as above,
and, the thread was executed on the same CPU as before suspend.
Best regards,
Kohji Okuno
> -----Original Message-----
> From: Fabio Estevam [mailto:festevam at gmail.com]
> Sent: Wednesday, January 10, 2018 12:02 AM
> To: Okuno Kohji (?? ??)
> Cc: Thomas Gleixner; Russell King - ARM Linux; moderated list:ARM/FREESCALE
> IMX / MXC ARM ARCHITECTURE
> Subject: Re: arm: Is VFP hotplug notifiers wrong?
>
> Hi Okuno,
>
> On Tue, Jan 9, 2018 at 12:46 PM, <okuno.kohji@jp.panasonic.com> wrote:
> > Dear Russel and Thomas,
> >
> > Thank you for your quick response.
> > Thomas, do you create the patch?
>
> Looks like the fix should be like this:
>
> --- a/arch/arm/vfp/vfpmodule.c
> +++ b/arch/arm/vfp/vfpmodule.c
> @@ -648,7 +648,7 @@ int vfp_restore_user_hwstate(struct user_vfp __user *ufp,
> */
> static int vfp_dying_cpu(unsigned int cpu) {
> - vfp_force_reload(cpu, current_thread_info());
> + vfp_current_hw_state[cpu] = NULL;
> return 0;
> }
>
> Could you please test it?
>
> Thanks
^ permalink raw reply
* [PATCH linux dev-4.10 6/6] drivers/hwmon: Add a driver for a generic PECI hwmon
From: Jae Hyun Yoo @ 2018-01-09 22:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180109223126.13093-1-jae.hyun.yoo@linux.intel.com>
This commit adds driver implementation for a generic PECI hwmon.
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
---
drivers/hwmon/Kconfig | 6 +
drivers/hwmon/Makefile | 1 +
drivers/hwmon/peci-hwmon.c | 953 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 960 insertions(+)
create mode 100644 drivers/hwmon/peci-hwmon.c
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 9256dd0..3a62c60 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1234,6 +1234,12 @@ config SENSORS_NCT7904
This driver can also be built as a module. If so, the module
will be called nct7904.
+config SENSORS_PECI_HWMON
+ tristate "PECI hwmon support"
+ depends on ASPEED_PECI
+ help
+ If you say yes here you get support for the generic PECI hwmon driver.
+
config SENSORS_NSA320
tristate "ZyXEL NSA320 and compatible fan speed and temperature sensors"
depends on GPIOLIB && OF
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 98000fc..41d43a5 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -131,6 +131,7 @@ obj-$(CONFIG_SENSORS_NCT7802) += nct7802.o
obj-$(CONFIG_SENSORS_NCT7904) += nct7904.o
obj-$(CONFIG_SENSORS_NSA320) += nsa320-hwmon.o
obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o
+obj-$(CONFIG_SENSORS_PECI_HWMON) += peci-hwmon.o
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
diff --git a/drivers/hwmon/peci-hwmon.c b/drivers/hwmon/peci-hwmon.c
new file mode 100644
index 0000000..2d2a288
--- /dev/null
+++ b/drivers/hwmon/peci-hwmon.c
@@ -0,0 +1,953 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2017 Intel Corporation
+
+#include <linux/delay.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/syscalls.h>
+#include <misc/peci.h>
+
+#define DEVICE_NAME "peci-hwmon"
+#define HWMON_NAME "peci_hwmon"
+
+#define CPU_ID_MAX 8 /* Max CPU number configured by socket ID */
+#define DIMM_NUMS_MAX 16 /* Max DIMM numbers (channel ranks x 2) */
+#define CORE_NUMS_MAX 28 /* Max core numbers (max on SKX Platinum) */
+#define TEMP_TYPE_PECI 6 /* Sensor type 6: Intel PECI */
+#define CORE_INDEX_OFFSET 100 /* sysfs filename start offset for core temp */
+#define DIMM_INDEX_OFFSET 200 /* sysfs filename start offset for DIMM temp */
+#define TEMP_NAME_HEADER_LEN 4 /* sysfs temp type header length */
+#define OF_DIMM_NUMS_DEFAULT 16 /* default dimm-nums setting */
+
+#define CORE_TEMP_ATTRS 5
+#define DIMM_TEMP_ATTRS 2
+#define ATTR_NAME_LEN 24
+
+#define UPDATE_INTERVAL_MIN HZ
+
+enum sign_t {
+ POS,
+ NEG
+};
+
+struct cpuinfo_t {
+ bool valid;
+ u32 dib;
+ u8 cpuid;
+ u8 platform_id;
+ u32 microcode;
+ u8 logical_thread_nums;
+};
+
+struct temp_data_t {
+ bool valid;
+ s32 value;
+ unsigned long last_updated;
+};
+
+struct temp_group_t {
+ struct temp_data_t tjmax;
+ struct temp_data_t tcontrol;
+ struct temp_data_t tthrottle;
+ struct temp_data_t dts_margin;
+ struct temp_data_t die;
+ struct temp_data_t core[CORE_NUMS_MAX];
+ struct temp_data_t dimm[DIMM_NUMS_MAX];
+};
+
+struct core_temp_attr_group_t {
+ struct sensor_device_attribute sd_attrs[CORE_NUMS_MAX][CORE_TEMP_ATTRS];
+ char attr_name[CORE_NUMS_MAX][CORE_TEMP_ATTRS][ATTR_NAME_LEN];
+ struct attribute *attrs[CORE_NUMS_MAX][CORE_TEMP_ATTRS + 1];
+ struct attribute_group attr_group[CORE_NUMS_MAX];
+};
+
+struct dimm_temp_attr_group_t {
+ struct sensor_device_attribute sd_attrs[DIMM_NUMS_MAX][DIMM_TEMP_ATTRS];
+ char attr_name[DIMM_NUMS_MAX][DIMM_TEMP_ATTRS][ATTR_NAME_LEN];
+ struct attribute *attrs[DIMM_NUMS_MAX][DIMM_TEMP_ATTRS + 1];
+ struct attribute_group attr_group[DIMM_NUMS_MAX];
+};
+
+struct peci_hwmon {
+ struct device *dev;
+ struct device *hwmon_dev;
+ char name[NAME_MAX];
+ const struct attribute_group **groups;
+ struct cpuinfo_t cpuinfo;
+ struct temp_group_t temp;
+ u32 cpu_id;
+ bool show_core;
+ u32 core_nums;
+ u32 dimm_nums;
+ atomic_t core_group_created;
+ struct core_temp_attr_group_t core;
+ struct dimm_temp_attr_group_t dimm;
+};
+
+enum label_t {
+ L_DIE,
+ L_DTS,
+ L_TCONTROL,
+ L_TTHROTTLE,
+ L_MAX
+};
+
+static const char *peci_label[L_MAX] = {
+ "Die temperature\n",
+ "DTS thermal margin to Tcontrol\n",
+ "Tcontrol temperature\n",
+ "Tthrottle temperature\n",
+};
+
+static DEFINE_MUTEX(peci_hwmon_lock);
+
+static int create_core_temp_group(struct peci_hwmon *priv, int core_no);
+
+
+static int xfer_peci_msg(int cmd, void *pmsg)
+{
+ int rc;
+
+ mutex_lock(&peci_hwmon_lock);
+ rc = peci_ioctl(NULL, cmd, (unsigned long)pmsg);
+ mutex_unlock(&peci_hwmon_lock);
+
+ return rc;
+}
+
+static int get_cpuinfo(struct peci_hwmon *priv)
+{
+ struct peci_get_dib_msg dib_msg;
+ struct peci_rd_pkg_cfg_msg cfg_msg;
+ int rc, i;
+
+ if (!priv->cpuinfo.valid) {
+ dib_msg.target = PECI_BASE_ADDR + priv->cpu_id;
+
+ rc = xfer_peci_msg(PECI_IOC_GET_DIB, (void *)&dib_msg);
+ if (rc < 0)
+ return rc;
+
+ priv->cpuinfo.dib = dib_msg.dib;
+
+ cfg_msg.target = PECI_BASE_ADDR + priv->cpu_id;
+ cfg_msg.index = MBX_INDEX_CPU_ID;
+ cfg_msg.param = 0;
+ cfg_msg.rx_len = 4;
+
+ rc = xfer_peci_msg(PECI_IOC_RD_PKG_CFG, (void *)&cfg_msg);
+ if (rc < 0)
+ return rc;
+
+ priv->cpuinfo.cpuid = cfg_msg.pkg_config[0];
+
+ cfg_msg.target = PECI_BASE_ADDR + priv->cpu_id;
+ cfg_msg.index = MBX_INDEX_CPU_ID;
+ cfg_msg.param = 1;
+ cfg_msg.rx_len = 4;
+
+ rc = xfer_peci_msg(PECI_IOC_RD_PKG_CFG, (void *)&cfg_msg);
+ if (rc < 0)
+ return rc;
+
+ priv->cpuinfo.platform_id = cfg_msg.pkg_config[0];
+
+ cfg_msg.target = PECI_BASE_ADDR + priv->cpu_id;
+ cfg_msg.index = MBX_INDEX_CPU_ID;
+ cfg_msg.param = 3;
+ cfg_msg.rx_len = 4;
+
+ rc = xfer_peci_msg(PECI_IOC_RD_PKG_CFG, (void *)&cfg_msg);
+ if (rc < 0)
+ return rc;
+
+ priv->cpuinfo.logical_thread_nums = cfg_msg.pkg_config[0] + 1;
+
+ cfg_msg.target = PECI_BASE_ADDR + priv->cpu_id;
+ cfg_msg.index = MBX_INDEX_CPU_ID;
+ cfg_msg.param = 4;
+ cfg_msg.rx_len = 4;
+
+ rc = xfer_peci_msg(PECI_IOC_RD_PKG_CFG, (void *)&cfg_msg);
+ if (rc < 0)
+ return rc;
+
+ priv->cpuinfo.microcode = (cfg_msg.pkg_config[3] << 24) |
+ (cfg_msg.pkg_config[2] << 16) |
+ (cfg_msg.pkg_config[1] << 8) |
+ cfg_msg.pkg_config[0];
+
+ priv->core_nums = priv->cpuinfo.logical_thread_nums / 2;
+
+ if (priv->show_core &&
+ atomic_inc_return(&priv->core_group_created) == 1) {
+ for (i = 0; i < priv->core_nums; i++) {
+ rc = create_core_temp_group(priv, i);
+ if (rc != 0) {
+ dev_err(priv->dev,
+ "Failed to create core temp group\n");
+ for (--i; i >= 0; i--) {
+ sysfs_remove_group(
+ &priv->hwmon_dev->kobj,
+ &priv->core.attr_group[i]);
+ }
+ atomic_set(&priv->core_group_created,
+ 0);
+ return rc;
+ }
+ }
+ }
+
+ priv->cpuinfo.valid = true;
+ }
+
+ return 0;
+}
+
+static int get_tjmax(struct peci_hwmon *priv)
+{
+ struct peci_rd_pkg_cfg_msg msg;
+ int rc;
+
+ rc = get_cpuinfo(priv);
+ if (rc < 0)
+ return rc;
+
+ if (!priv->temp.tjmax.valid) {
+ msg.target = PECI_BASE_ADDR + priv->cpu_id;
+ msg.index = MBX_INDEX_TEMP_TARGET;
+ msg.param = 0;
+ msg.rx_len = 4;
+
+ rc = xfer_peci_msg(PECI_IOC_RD_PKG_CFG, (void *)&msg);
+ if (rc < 0)
+ return rc;
+
+ priv->temp.tjmax.value = (s32)msg.pkg_config[2] * 1000;
+ priv->temp.tjmax.valid = true;
+ }
+
+ return 0;
+}
+
+static int get_tcontrol(struct peci_hwmon *priv)
+{
+ struct peci_rd_pkg_cfg_msg msg;
+ s32 tcontrol_margin;
+ int rc;
+
+ if (priv->temp.tcontrol.valid &&
+ time_before(jiffies, priv->temp.tcontrol.last_updated +
+ UPDATE_INTERVAL_MIN))
+ return 0;
+
+ rc = get_tjmax(priv);
+ if (rc < 0)
+ return rc;
+
+ msg.target = PECI_BASE_ADDR + priv->cpu_id;
+ msg.index = MBX_INDEX_TEMP_TARGET;
+ msg.param = 0;
+ msg.rx_len = 4;
+
+ rc = xfer_peci_msg(PECI_IOC_RD_PKG_CFG, (void *)&msg);
+ if (rc < 0)
+ return rc;
+
+ tcontrol_margin = msg.pkg_config[1];
+ tcontrol_margin = ((tcontrol_margin ^ 0x80) - 0x80) * 1000;
+
+ priv->temp.tcontrol.value = priv->temp.tjmax.value - tcontrol_margin;
+
+ if (!priv->temp.tcontrol.valid) {
+ priv->temp.tcontrol.last_updated = INITIAL_JIFFIES;
+ priv->temp.tcontrol.valid = true;
+ } else {
+ priv->temp.tcontrol.last_updated = jiffies;
+ }
+
+ return 0;
+}
+
+static int get_tthrottle(struct peci_hwmon *priv)
+{
+ struct peci_rd_pkg_cfg_msg msg;
+ s32 tthrottle_offset;
+ int rc;
+
+ if (priv->temp.tthrottle.valid &&
+ time_before(jiffies, priv->temp.tthrottle.last_updated +
+ UPDATE_INTERVAL_MIN))
+ return 0;
+
+ rc = get_tjmax(priv);
+ if (rc < 0)
+ return rc;
+
+ msg.target = PECI_BASE_ADDR + priv->cpu_id;
+ msg.index = MBX_INDEX_TEMP_TARGET;
+ msg.param = 0;
+ msg.rx_len = 4;
+
+ rc = xfer_peci_msg(PECI_IOC_RD_PKG_CFG, (void *)&msg);
+ if (rc < 0)
+ return rc;
+
+ tthrottle_offset = (msg.pkg_config[3] & 0x2f) * 1000;
+ priv->temp.tthrottle.value = priv->temp.tjmax.value - tthrottle_offset;
+
+ if (!priv->temp.tthrottle.valid) {
+ priv->temp.tthrottle.last_updated = INITIAL_JIFFIES;
+ priv->temp.tthrottle.valid = true;
+ } else {
+ priv->temp.tthrottle.last_updated = jiffies;
+ }
+
+ return 0;
+}
+
+static int get_die_temp(struct peci_hwmon *priv)
+{
+ struct peci_get_temp_msg msg;
+ int rc;
+
+ if (priv->temp.die.valid &&
+ time_before(jiffies, priv->temp.die.last_updated +
+ UPDATE_INTERVAL_MIN))
+ return 0;
+
+ rc = get_tjmax(priv);
+ if (rc < 0)
+ return rc;
+
+ msg.target = PECI_BASE_ADDR + priv->cpu_id;
+
+ rc = xfer_peci_msg(PECI_IOC_GET_TEMP, (void *)&msg);
+ if (rc < 0)
+ return rc;
+
+ priv->temp.die.value = priv->temp.tjmax.value +
+ ((s32)msg.temp_raw * 1000 / 64);
+
+ if (!priv->temp.die.valid) {
+ priv->temp.die.last_updated = INITIAL_JIFFIES;
+ priv->temp.die.valid = true;
+ } else {
+ priv->temp.die.last_updated = jiffies;
+ }
+
+ return 0;
+}
+
+static int get_dts_margin(struct peci_hwmon *priv)
+{
+ struct peci_rd_pkg_cfg_msg msg;
+ s32 dts_margin;
+ int rc;
+
+ if (priv->temp.dts_margin.valid &&
+ time_before(jiffies, priv->temp.dts_margin.last_updated +
+ UPDATE_INTERVAL_MIN))
+ return 0;
+
+ rc = get_cpuinfo(priv);
+ if (rc < 0)
+ return rc;
+
+ msg.target = PECI_BASE_ADDR + priv->cpu_id;
+ msg.index = MBX_INDEX_DTS_MARGIN;
+ msg.param = 0;
+ msg.rx_len = 4;
+
+ rc = xfer_peci_msg(PECI_IOC_RD_PKG_CFG, (void *)&msg);
+ if (rc < 0)
+ return rc;
+
+ dts_margin = (msg.pkg_config[1] << 8) | msg.pkg_config[0];
+
+ /*
+ * Processors return a value of DTS reading in 10.6 format
+ * (10 bits signed decimal, 6 bits fractional).
+ * Error codes:
+ * 0x8000: General sensor error
+ * 0x8001: Reserved
+ * 0x8002: Underflow on reading value
+ * 0x8003-0x81ff: Reserved
+ */
+ if (dts_margin >= 0x8000 && dts_margin <= 0x81ff)
+ return -1;
+
+ dts_margin = ((dts_margin ^ 0x8000) - 0x8000) * 1000 / 64;
+
+ priv->temp.dts_margin.value = dts_margin;
+
+ if (!priv->temp.dts_margin.valid) {
+ priv->temp.dts_margin.last_updated = INITIAL_JIFFIES;
+ priv->temp.dts_margin.valid = true;
+ } else {
+ priv->temp.dts_margin.last_updated = jiffies;
+ }
+
+ return 0;
+}
+
+static int get_core_temp(struct peci_hwmon *priv, int core_index)
+{
+ struct peci_rd_pkg_cfg_msg msg;
+ s32 core_dts_margin;
+ int rc;
+
+ if (priv->temp.core[core_index].valid &&
+ time_before(jiffies, priv->temp.core[core_index].last_updated +
+ UPDATE_INTERVAL_MIN))
+ return 0;
+
+ rc = get_tjmax(priv);
+ if (rc < 0)
+ return rc;
+
+ msg.target = PECI_BASE_ADDR + priv->cpu_id;
+ msg.index = MBX_INDEX_PER_CORE_DTS_TEMP;
+ msg.param = core_index;
+ msg.rx_len = 4;
+
+ rc = xfer_peci_msg(PECI_IOC_RD_PKG_CFG, (void *)&msg);
+ if (rc < 0)
+ return rc;
+
+ core_dts_margin = (msg.pkg_config[1] << 8) | msg.pkg_config[0];
+
+ /*
+ * Processors return a value of the core DTS reading in 10.6 format
+ * (10 bits signed decimal, 6 bits fractional).
+ * Error codes:
+ * 0x8000: General sensor error
+ * 0x8001: Reserved
+ * 0x8002: Underflow on reading value
+ * 0x8003-0x81ff: Reserved
+ */
+ if (core_dts_margin >= 0x8000 && core_dts_margin <= 0x81ff)
+ return -1;
+
+ core_dts_margin = ((core_dts_margin ^ 0x8000) - 0x8000) * 1000 / 64;
+
+ priv->temp.core[core_index].value = priv->temp.tjmax.value +
+ core_dts_margin;
+
+ if (!priv->temp.core[core_index].valid) {
+ priv->temp.core[core_index].last_updated = INITIAL_JIFFIES;
+ priv->temp.core[core_index].valid = true;
+ } else {
+ priv->temp.core[core_index].last_updated = jiffies;
+ }
+
+ return 0;
+}
+
+static int get_dimm_temp(struct peci_hwmon *priv, int dimm_index)
+{
+ struct peci_rd_pkg_cfg_msg msg;
+ int channel_rank = dimm_index / 2;
+ int dimm_order = dimm_index % 2;
+ int rc;
+
+ if (priv->temp.core[dimm_index].valid &&
+ time_before(jiffies, priv->temp.core[dimm_index].last_updated +
+ UPDATE_INTERVAL_MIN))
+ return 0;
+
+ rc = get_cpuinfo(priv);
+ if (rc < 0)
+ return rc;
+
+ msg.target = PECI_BASE_ADDR + priv->cpu_id;
+ msg.index = MBX_INDEX_DDR_DIMM_TEMP;
+ msg.param = channel_rank;
+ msg.rx_len = 4;
+
+ rc = xfer_peci_msg(PECI_IOC_RD_PKG_CFG, (void *)&msg);
+ if (rc < 0)
+ return rc;
+
+ priv->temp.dimm[dimm_index].value = msg.pkg_config[dimm_order] * 1000;
+
+ if (!priv->temp.dimm[dimm_index].valid) {
+ priv->temp.dimm[dimm_index].last_updated = INITIAL_JIFFIES;
+ priv->temp.dimm[dimm_index].valid = true;
+ } else {
+ priv->temp.dimm[dimm_index].last_updated = jiffies;
+ }
+
+ return 0;
+}
+
+static ssize_t show_info(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct peci_hwmon *priv = dev_get_drvdata(dev);
+ int rc;
+
+ rc = get_cpuinfo(priv);
+ if (rc < 0)
+ return rc;
+
+ return sprintf(buf, "dib : 0x%08x\n"
+ "cpuid : 0x%x\n"
+ "platform id : %d\n"
+ "stepping : %d\n"
+ "microcode : 0x%08x\n"
+ "logical thread nums : %d\n",
+ priv->cpuinfo.dib,
+ priv->cpuinfo.cpuid,
+ priv->cpuinfo.platform_id,
+ priv->cpuinfo.cpuid & 0xf,
+ priv->cpuinfo.microcode,
+ priv->cpuinfo.logical_thread_nums);
+}
+
+static ssize_t show_tcontrol(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct peci_hwmon *priv = dev_get_drvdata(dev);
+ int rc;
+
+ rc = get_tcontrol(priv);
+ if (rc < 0)
+ return rc;
+
+ return sprintf(buf, "%d\n", priv->temp.tcontrol.value);
+}
+
+static ssize_t show_tcontrol_margin(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct peci_hwmon *priv = dev_get_drvdata(dev);
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int rc;
+
+ rc = get_tcontrol(priv);
+ if (rc < 0)
+ return rc;
+
+ return sprintf(buf, "%d\n", sensor_attr->index == POS ?
+ priv->temp.tjmax.value -
+ priv->temp.tcontrol.value :
+ priv->temp.tcontrol.value -
+ priv->temp.tjmax.value);
+}
+
+static ssize_t show_tthrottle(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct peci_hwmon *priv = dev_get_drvdata(dev);
+ int rc;
+
+ rc = get_tthrottle(priv);
+ if (rc < 0)
+ return rc;
+
+ return sprintf(buf, "%d\n", priv->temp.tthrottle.value);
+}
+
+static ssize_t show_tjmax(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct peci_hwmon *priv = dev_get_drvdata(dev);
+ int rc;
+
+ rc = get_tjmax(priv);
+ if (rc < 0)
+ return rc;
+
+ return sprintf(buf, "%d\n", priv->temp.tjmax.value);
+}
+
+static ssize_t show_die_temp(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct peci_hwmon *priv = dev_get_drvdata(dev);
+ int rc;
+
+ rc = get_die_temp(priv);
+ if (rc < 0)
+ return rc;
+
+ return sprintf(buf, "%d\n", priv->temp.die.value);
+}
+
+static ssize_t show_dts_therm_margin(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct peci_hwmon *priv = dev_get_drvdata(dev);
+ int rc;
+
+ rc = get_dts_margin(priv);
+ if (rc < 0)
+ return rc;
+
+ return sprintf(buf, "%d\n", priv->temp.dts_margin.value);
+}
+
+static ssize_t show_core_temp(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct peci_hwmon *priv = dev_get_drvdata(dev);
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int core_index = sensor_attr->index;
+ int rc;
+
+ rc = get_core_temp(priv, core_index);
+ if (rc < 0)
+ return rc;
+
+ return sprintf(buf, "%d\n", priv->temp.core[core_index].value);
+}
+
+static ssize_t show_dimm_temp(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct peci_hwmon *priv = dev_get_drvdata(dev);
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int dimm_index = sensor_attr->index;
+ int rc;
+
+ rc = get_dimm_temp(priv, dimm_index);
+ if (rc < 0)
+ return rc;
+
+ return sprintf(buf, "%d\n", priv->temp.dimm[dimm_index].value);
+}
+
+static ssize_t show_value(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+
+ return sprintf(buf, "%d\n", sensor_attr->index);
+}
+
+static ssize_t show_label(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+
+ return sprintf(buf, peci_label[sensor_attr->index]);
+}
+
+static ssize_t show_core_label(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+
+ return sprintf(buf, "Core #%d temperature\n", sensor_attr->index);
+}
+
+static ssize_t show_dimm_label(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+
+ char channel = 'A' + (sensor_attr->index / 2);
+ int index = sensor_attr->index % 2;
+
+ return sprintf(buf, "Channel Rank %c DDR DIMM #%d temperature\n",
+ channel, index);
+}
+
+/* Die temperature */
+static SENSOR_DEVICE_ATTR(temp1_label, 0444, show_label, NULL, L_DIE);
+static SENSOR_DEVICE_ATTR(temp1_input, 0444, show_die_temp, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_max, 0444, show_tcontrol, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_crit, 0444, show_tjmax, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_crit_hyst, 0444, show_tcontrol_margin, NULL,
+ POS);
+
+static struct attribute *die_temp_attrs[] = {
+ &sensor_dev_attr_temp1_label.dev_attr.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group die_temp_attr_group = {
+ .attrs = die_temp_attrs,
+};
+
+/* DTS thermal margin temperature */
+static SENSOR_DEVICE_ATTR(temp2_label, 0444, show_label, NULL, L_DTS);
+static SENSOR_DEVICE_ATTR(temp2_input, 0444, show_dts_therm_margin, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_min, 0444, show_value, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_lcrit, 0444, show_tcontrol_margin, NULL, NEG);
+
+static struct attribute *dts_margin_temp_attrs[] = {
+ &sensor_dev_attr_temp2_label.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_lcrit.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group dts_margin_temp_attr_group = {
+ .attrs = dts_margin_temp_attrs,
+};
+
+/* Tcontrol temperature */
+static SENSOR_DEVICE_ATTR(temp3_label, 0444, show_label, NULL, L_TCONTROL);
+static SENSOR_DEVICE_ATTR(temp3_input, 0444, show_tcontrol, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp3_crit, 0444, show_tjmax, NULL, 0);
+
+static struct attribute *tcontrol_temp_attrs[] = {
+ &sensor_dev_attr_temp3_label.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group tcontrol_temp_attr_group = {
+ .attrs = tcontrol_temp_attrs,
+};
+
+/* Tthrottle temperature */
+static SENSOR_DEVICE_ATTR(temp4_label, 0444, show_label, NULL, L_TTHROTTLE);
+static SENSOR_DEVICE_ATTR(temp4_input, 0444, show_tthrottle, NULL, 0);
+
+static struct attribute *tthrottle_temp_attrs[] = {
+ &sensor_dev_attr_temp4_label.dev_attr.attr,
+ &sensor_dev_attr_temp4_input.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group tthrottle_temp_attr_group = {
+ .attrs = tthrottle_temp_attrs,
+};
+
+/* CPU info */
+static SENSOR_DEVICE_ATTR(info, 0444, show_info, NULL, 0);
+
+static struct attribute *info_attrs[] = {
+ &sensor_dev_attr_info.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group info_attr_group = {
+ .attrs = info_attrs,
+};
+
+const struct attribute_group *peci_hwmon_attr_groups[] = {
+ &info_attr_group,
+ &die_temp_attr_group,
+ &dts_margin_temp_attr_group,
+ &tcontrol_temp_attr_group,
+ &tthrottle_temp_attr_group,
+ NULL
+};
+
+static ssize_t (*const core_show_fn[CORE_TEMP_ATTRS]) (struct device *dev,
+ struct device_attribute *devattr, char *buf) = {
+ show_core_label,
+ show_core_temp,
+ show_tcontrol,
+ show_tjmax,
+ show_tcontrol_margin,
+};
+
+static const char *const core_suffix[CORE_TEMP_ATTRS] = {
+ "label",
+ "input",
+ "max",
+ "crit",
+ "crit_hyst",
+};
+
+static int create_core_temp_group(struct peci_hwmon *priv, int core_no)
+{
+ int i;
+
+ for (i = 0; i < CORE_TEMP_ATTRS; i++) {
+ snprintf(priv->core.attr_name[core_no][i],
+ ATTR_NAME_LEN, "temp%d_%s",
+ CORE_INDEX_OFFSET + core_no, core_suffix[i]);
+ sysfs_attr_init(
+ &priv->core.sd_attrs[core_no][i].dev_attr.attr);
+ priv->core.sd_attrs[core_no][i].dev_attr.attr.name =
+ priv->core.attr_name[core_no][i];
+ priv->core.sd_attrs[core_no][i].dev_attr.attr.mode = 0444;
+ priv->core.sd_attrs[core_no][i].dev_attr.show = core_show_fn[i];
+ if (i == 0 || i == 1) /* label or temp */
+ priv->core.sd_attrs[core_no][i].index = core_no;
+ priv->core.attrs[core_no][i] =
+ &priv->core.sd_attrs[core_no][i].dev_attr.attr;
+ }
+
+ priv->core.attr_group[core_no].attrs = priv->core.attrs[core_no];
+
+ return sysfs_create_group(&priv->hwmon_dev->kobj,
+ &priv->core.attr_group[core_no]);
+}
+
+static ssize_t (*const dimm_show_fn[DIMM_TEMP_ATTRS]) (struct device *dev,
+ struct device_attribute *devattr, char *buf) = {
+ show_dimm_label,
+ show_dimm_temp,
+};
+
+static const char *const dimm_suffix[DIMM_TEMP_ATTRS] = {
+ "label",
+ "input",
+};
+
+static int create_dimm_temp_group(struct peci_hwmon *priv, int dimm_no)
+{
+ int i;
+
+ for (i = 0; i < DIMM_TEMP_ATTRS; i++) {
+ snprintf(priv->dimm.attr_name[dimm_no][i],
+ ATTR_NAME_LEN, "temp%d_%s",
+ DIMM_INDEX_OFFSET + dimm_no, dimm_suffix[i]);
+ sysfs_attr_init(&priv->dimm.sd_attrs[dimm_no][i].dev_attr.attr);
+ priv->dimm.sd_attrs[dimm_no][i].dev_attr.attr.name =
+ priv->dimm.attr_name[dimm_no][i];
+ priv->dimm.sd_attrs[dimm_no][i].dev_attr.attr.mode = 0444;
+ priv->dimm.sd_attrs[dimm_no][i].dev_attr.show = dimm_show_fn[i];
+ priv->dimm.sd_attrs[dimm_no][i].index = dimm_no;
+ priv->dimm.attrs[dimm_no][i] =
+ &priv->dimm.sd_attrs[dimm_no][i].dev_attr.attr;
+ }
+
+ priv->dimm.attr_group[dimm_no].attrs = priv->dimm.attrs[dimm_no];
+
+ return sysfs_create_group(&priv->hwmon_dev->kobj,
+ &priv->dimm.attr_group[dimm_no]);
+}
+
+static int peci_hwmon_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct peci_hwmon *priv;
+ struct device *hwmon;
+ int rc, i;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, priv);
+ priv->dev = dev;
+
+ rc = of_property_read_u32(np, "cpu-id", &priv->cpu_id);
+ if (rc || priv->cpu_id >= CPU_ID_MAX) {
+ dev_err(dev, "Invalid cpu-id configuration\n");
+ return rc;
+ }
+
+ rc = of_property_read_u32(np, "dimm-nums", &priv->dimm_nums);
+ if (rc || priv->dimm_nums > DIMM_NUMS_MAX) {
+ dev_warn(dev, "Invalid dimm-nums : %u. Use default : %u\n",
+ priv->dimm_nums, OF_DIMM_NUMS_DEFAULT);
+ priv->dimm_nums = OF_DIMM_NUMS_DEFAULT;
+ }
+
+ priv->show_core = of_property_read_bool(np, "show-core");
+
+ priv->groups = peci_hwmon_attr_groups;
+
+ snprintf(priv->name, NAME_MAX, HWMON_NAME ".cpu%d", priv->cpu_id);
+
+ hwmon = devm_hwmon_device_register_with_groups(dev,
+ priv->name,
+ priv, priv->groups);
+
+ rc = PTR_ERR_OR_ZERO(hwmon);
+ if (rc != 0) {
+ dev_err(dev, "Failed to register peci hwmon\n");
+ return rc;
+ }
+
+ priv->hwmon_dev = hwmon;
+
+ for (i = 0; i < priv->dimm_nums; i++) {
+ rc = create_dimm_temp_group(priv, i);
+ if (rc != 0) {
+ dev_err(dev, "Failed to create dimm temp group\n");
+ for (--i; i >= 0; i--) {
+ sysfs_remove_group(&priv->hwmon_dev->kobj,
+ &priv->dimm.attr_group[i]);
+ }
+ return rc;
+ }
+ }
+
+ /*
+ * Try to create core temp group now. It will be created if CPU is
+ * curretnly online or it will be created after the first reading of
+ * cpuinfo from the online CPU otherwise.
+ */
+ if (priv->show_core)
+ (void) get_cpuinfo(priv);
+
+ dev_info(dev, "peci hwmon for CPU#%d registered\n", priv->cpu_id);
+
+ return rc;
+}
+
+static int peci_hwmon_remove(struct platform_device *pdev)
+{
+ struct peci_hwmon *priv = dev_get_drvdata(&pdev->dev);
+ int i;
+
+ if (atomic_read(&priv->core_group_created))
+ for (i = 0; i < priv->core_nums; i++) {
+ sysfs_remove_group(&priv->hwmon_dev->kobj,
+ &priv->core.attr_group[i]);
+ }
+
+ for (i = 0; i < priv->dimm_nums; i++) {
+ sysfs_remove_group(&priv->hwmon_dev->kobj,
+ &priv->dimm.attr_group[i]);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id peci_of_table[] = {
+ { .compatible = "peci-hwmon", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, peci_of_table);
+
+static struct platform_driver peci_hwmon_driver = {
+ .probe = peci_hwmon_probe,
+ .remove = peci_hwmon_remove,
+ .driver = {
+ .name = DEVICE_NAME,
+ .of_match_table = peci_of_table,
+ },
+};
+
+module_platform_driver(peci_hwmon_driver);
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_DESCRIPTION("PECI hwmon driver");
+MODULE_LICENSE("GPL v2");
--
2.7.4
^ permalink raw reply related
* [PATCH linux dev-4.10 5/6] Documentation: hwmon: Add a generic PECI hwmon
From: Jae Hyun Yoo @ 2018-01-09 22:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180109223126.13093-1-jae.hyun.yoo@linux.intel.com>
From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
This commit add a document for a generic PECI hwmon driver.
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
---
Documentation/hwmon/peci-hwmon | 74 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 74 insertions(+)
create mode 100644 Documentation/hwmon/peci-hwmon
diff --git a/Documentation/hwmon/peci-hwmon b/Documentation/hwmon/peci-hwmon
new file mode 100644
index 0000000..e0155b5
--- /dev/null
+++ b/Documentation/hwmon/peci-hwmon
@@ -0,0 +1,74 @@
+Kernel driver peci-hwmon
+===============================
+
+Supported chips:
+ Generic BMC chips provide PECI controller
+
+Author:
+ Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
+
+
+Hardware Interfaces
+-------------------
+
+This driver uses a PECI misc driver as a controller interface so one of PECI
+misc drivers which provides compatible ioctls has to be enabled.
+
+
+Description
+-----------
+
+This driver implements a generic PECI hwmon feature which is running with a PECI
+controller driver supports native PECI Client Command Suite for retrieving
+temperatures of the CPU package, CPU cores and DIMM components.
+
+All temperature values are given in millidegree Celsius and will be measurable
+only when the target CPU is powered on.
+
+
+sysfs files
+-----------
+
+temp1_input Provides current die temperature of the CPU package.
+temp1_max Provides thermal control temperature of the CPU package
+ which is also known as Tcontrol.
+temp1_crit Provides shutdown temperature of the CPU package which
+ is also known as the maximum processor junction
+ temperature, Tjmax or Tprochot.
+temp1_crit_hyst Provides the hysteresis value from Tcontrol to Tjmax of
+ the CPU package.
+
+temp2_input Provides current DTS thermal margin to Tcontrol of the
+ CPU package. Value 0 means it reaches to Tcontrol
+ temperature. Sub-zero value means the die temperature
+ goes across Tconrtol to Tjmax.
+temp2_min Provides the minimum DTS thermal margin to Tcontrol of
+ the CPU package.
+temp2_lcrit Provides the value when the CPU package temperature
+ reaches to Tjmax.
+
+temp3_input Provides current Tcontrol temperature of the CPU
+ package which is also known as Fan Temperature target.
+ Indicates the relative value from thermal monitor trip
+ temperature at which fans should be engaged.
+temp3_crit Provides Tcontrol critical value of the CPU package
+ which is same to Tjmax.
+
+temp4_input Provides current Tthrottle temperature of the CPU
+ package. Used for throttling temperature. If this value
+ is allowed and lower than Tjmax - the throttle will
+ occur and reported at lower than Tjmax.
+
+temp[100-127]_input Provides current core temperature.
+temp[100-127]_max Provides thermal control temperature of the core.
+temp[100-127]_crit Provides shutdown temperature of the core.
+temp[100-127]_crit_hyst Provides the hysteresis value from Tcontrol to Tjmax of
+ the core.
+
+Note:
+ Core temperature group will be appeared when probing the driver if CPU
+ is online or when the first reading on other attr happens because it
+ needs cpu info reading. The number of generated core attrs depends on
+ the number of cores of the cpu package.
+
+temp[200-215]_input Provides current temperature of the DDR DIMM.
--
2.7.4
^ permalink raw reply related
* [PATCH linux dev-4.10 4/6] Documentation: dt-bindings: Add a generic PECI hwmon
From: Jae Hyun Yoo @ 2018-01-09 22:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180109223126.13093-1-jae.hyun.yoo@linux.intel.com>
This commit add a dt-bindings document for a generic PECI hwmon
driver.
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
---
.../devicetree/bindings/hwmon/peci-hwmon.txt | 33 ++++++++++++++++++++++
1 file changed, 33 insertions(+)
create mode 100644 Documentation/devicetree/bindings/hwmon/peci-hwmon.txt
diff --git a/Documentation/devicetree/bindings/hwmon/peci-hwmon.txt b/Documentation/devicetree/bindings/hwmon/peci-hwmon.txt
new file mode 100644
index 0000000..20b86f5
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/peci-hwmon.txt
@@ -0,0 +1,33 @@
+* Generic PECI (Platform Environment Control Interface) hwmon driver.
+
+Dependency:
+- This driver uses a PECI misc driver as a controller interface so one of PECI
+ misc drivers which provides compatible ioctls has to be enabled.
+
+Required properties:
+- compatible: "peci-hwmon"
+- cpu-id: Should contain CPU socket ID
+ - 0 ~ 7
+
+Optional properties:
+- show-core: If this protperty is defined, core tmeperature attrubites will be
+ enumerated.
+- dimm-nums: Should contain the number of DIMM slots that attached to each CPU
+ which is indicated by cpu-id.
+ 0 ~ 16 (default: 16)
+ In case of 0, DIMM temperature attrubites will not be enumerated.
+
+Example:
+ peci-hwmon0 {
+ compatible = "peci-hwmon";
+ cpu-id = <0>;
+ show-core;
+ dimm-nums = <16>;
+ };
+
+ peci-hwmon1 {
+ compatible = "peci-hwmon";
+ cpu-id = <1>;
+ show-core;
+ dimm-nums = <16>;
+ };
--
2.7.4
^ permalink raw reply related
* [PATCH linux dev-4.10 3/6] drivers/misc: Add driver for Aspeed PECI and generic PECI headers
From: Jae Hyun Yoo @ 2018-01-09 22:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180109223126.13093-1-jae.hyun.yoo@linux.intel.com>
This commit adds driver implementation for Aspeed PECI. Also adds
generic peci.h and peci_ioctl.h files to provide compatibility
to peci drivers that can be implemented later e.g. Nuvoton's BMC
SoC family.
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
---
drivers/misc/Kconfig | 9 +
drivers/misc/Makefile | 1 +
drivers/misc/aspeed-peci.c | 1130 +++++++++++++++++++++++++++++++++++++++
include/misc/peci.h | 11 +
include/uapi/linux/Kbuild | 1 +
include/uapi/linux/peci_ioctl.h | 270 ++++++++++
6 files changed, 1422 insertions(+)
create mode 100644 drivers/misc/aspeed-peci.c
create mode 100644 include/misc/peci.h
create mode 100644 include/uapi/linux/peci_ioctl.h
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 02ffdd1..96e1e04 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -782,6 +782,15 @@ config ASPEED_LPC_SNOOP
allows the BMC to listen on and save the data written by
the host to an arbitrary LPC I/O port.
+config ASPEED_PECI
+ tristate "Aspeed AST2400/AST2500 PECI support"
+ select CRC8
+ select REGMAP_MMIO
+ depends on ARCH_ASPEED || COMPILE_TEST
+ help
+ Provides a driver for Platform Environment Control Interface (PECI)
+ controller on Aspeed AST2400/AST2500 SoC.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index ab8af76..8a22455 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_CXL_BASE) += cxl/
obj-$(CONFIG_PANEL) += panel.o
obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o
obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
+obj-$(CONFIG_ASPEED_PECI) += aspeed-peci.o
lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o
lkdtm-$(CONFIG_LKDTM) += lkdtm_bugs.o
diff --git a/drivers/misc/aspeed-peci.c b/drivers/misc/aspeed-peci.c
new file mode 100644
index 0000000..04fb794
--- /dev/null
+++ b/drivers/misc/aspeed-peci.c
@@ -0,0 +1,1130 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2012-2020 ASPEED Technology Inc.
+// Copyright (c) 2017 Intel Corporation
+
+#include <linux/clk.h>
+#include <linux/crc8.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/mfd/syscon.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/peci_ioctl.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/semaphore.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+
+#define SOC_NAME "aspeed"
+#define DEVICE_NAME "peci"
+
+#define DUMP_DEBUG 0
+
+/* Aspeed PECI Registers */
+#define AST_PECI_CTRL 0x00
+#define AST_PECI_TIMING 0x04
+#define AST_PECI_CMD 0x08
+#define AST_PECI_CMD_CTRL 0x0c
+#define AST_PECI_EXP_FCS 0x10
+#define AST_PECI_CAP_FCS 0x14
+#define AST_PECI_INT_CTRL 0x18
+#define AST_PECI_INT_STS 0x1c
+#define AST_PECI_W_DATA0 0x20
+#define AST_PECI_W_DATA1 0x24
+#define AST_PECI_W_DATA2 0x28
+#define AST_PECI_W_DATA3 0x2c
+#define AST_PECI_R_DATA0 0x30
+#define AST_PECI_R_DATA1 0x34
+#define AST_PECI_R_DATA2 0x38
+#define AST_PECI_R_DATA3 0x3c
+#define AST_PECI_W_DATA4 0x40
+#define AST_PECI_W_DATA5 0x44
+#define AST_PECI_W_DATA6 0x48
+#define AST_PECI_W_DATA7 0x4c
+#define AST_PECI_R_DATA4 0x50
+#define AST_PECI_R_DATA5 0x54
+#define AST_PECI_R_DATA6 0x58
+#define AST_PECI_R_DATA7 0x5c
+
+/* AST_PECI_CTRL - 0x00 : Control Register */
+#define PECI_CTRL_SAMPLING_MASK GENMASK(19, 16)
+#define PECI_CTRL_SAMPLING(x) ((x << 16) & PECI_CTRL_SAMPLING_MASK)
+#define PECI_CTRL_SAMPLING_GET(x) ((x & PECI_CTRL_SAMPLING_MASK) >> 16)
+#define PECI_CTRL_READ_MODE_MASK GENMASK(13, 12)
+#define PECI_CTRL_READ_MODE(x) ((x << 12) & PECI_CTRL_READ_MODE_MASK)
+#define PECI_CTRL_READ_MODE_GET(x) ((x & PECI_CTRL_READ_MODE_MASK) >> 12)
+#define PECI_CTRL_READ_MODE_COUNT BIT(12)
+#define PECI_CTRL_READ_MODE_DBG BIT(13)
+#define PECI_CTRL_CLK_SOURCE_MASK BIT(11)
+#define PECI_CTRL_CLK_SOURCE(x) ((x << 11) & PECI_CTRL_CLK_SOURCE_MASK)
+#define PECI_CTRL_CLK_SOURCE_GET(x) ((x & PECI_CTRL_CLK_SOURCE_MASK) >> 11)
+#define PECI_CTRL_CLK_DIV_MASK GENMASK(10, 8)
+#define PECI_CTRL_CLK_DIV(x) ((x << 8) & PECI_CTRL_CLK_DIV_MASK)
+#define PECI_CTRL_CLK_DIV_GET(x) ((x & PECI_CTRL_CLK_DIV_MASK) >> 8)
+#define PECI_CTRL_INVERT_OUT BIT(7)
+#define PECI_CTRL_INVERT_IN BIT(6)
+#define PECI_CTRL_BUS_CONTENT_EN BIT(5)
+#define PECI_CTRL_PECI_EN BIT(4)
+#define PECI_CTRL_PECI_CLK_EN BIT(0)
+
+/* AST_PECI_TIMING - 0x04 : Timing Negotiation Register */
+#define PECI_TIMING_MESSAGE_MASK GENMASK(15, 8)
+#define PECI_TIMING_MESSAGE(x) ((x << 8) & PECI_TIMING_MESSAGE_MASK)
+#define PECI_TIMING_MESSAGE_GET(x) ((x & PECI_TIMING_MESSAGE_MASK) >> 8)
+#define PECI_TIMING_ADDRESS_MASK GENMASK(7, 0)
+#define PECI_TIMING_ADDRESS(x) (x & PECI_TIMING_ADDRESS_MASK)
+#define PECI_TIMING_ADDRESS_GET(x) (x & PECI_TIMING_ADDRESS_MASK)
+
+/* AST_PECI_CMD - 0x08 : Command Register */
+#define PECI_CMD_PIN_MON BIT(31)
+#define PECI_CMD_STS_MASK GENMASK(27, 24)
+#define PECI_CMD_STS_GET(x) ((x & PECI_CMD_STS_MASK) >> 24)
+#define PECI_CMD_FIRE BIT(0)
+
+/* AST_PECI_LEN - 0x0C : Read/Write Length Register */
+#define PECI_AW_FCS_EN BIT(31)
+#define PECI_READ_LEN_MASK GENMASK(23, 16)
+#define PECI_READ_LEN(x) ((x << 16) & PECI_READ_LEN_MASK)
+#define PECI_WRITE_LEN_MASK GENMASK(15, 8)
+#define PECI_WRITE_LEN(x) ((x << 8) & PECI_WRITE_LEN_MASK)
+#define PECI_TAGET_ADDR_MASK GENMASK(7, 0)
+#define PECI_TAGET_ADDR(x) ((x) & PECI_TAGET_ADDR_MASK)
+
+/* AST_PECI_EXP_FCS - 0x10 : Expected FCS Data Register */
+#define PECI_EXPECT_READ_FCS_MASK GENMASK(23, 16)
+#define PECI_EXPECT_READ_FCS_GET(x) ((x & PECI_EXPECT_READ_FCS_MASK) >> 16)
+#define PECI_EXPECT_AW_FCS_AUTO_MASK GENMASK(15, 8)
+#define PECI_EXPECT_AW_FCS_AUTO_GET(x) ((x & PECI_EXPECT_AW_FCS_AUTO_MASK) >> 8)
+#define PECI_EXPECT_WRITE_FCS_MASK GENMASK(7, 0)
+#define PECI_EXPECT_WRITE_FCS_GET(x) (x & PECI_EXPECT_WRITE_FCS_MASK)
+
+/* AST_PECI_CAP_FCS - 0x14 : Captured FCS Data Register */
+#define PECI_CAPTURE_READ_FCS_MASK GENMASK(23, 16)
+#define PECI_CAPTURE_READ_FCS_GET(x) ((x & PECI_CAPTURE_READ_FCS_MASK) >> 16)
+#define PECI_CAPTURE_WRITE_FCS_MASK GENMASK(7, 0)
+#define PECI_CAPTURE_WRITE_FCS_GET(x) (x & PECI_CAPTURE_WRITE_FCS_MASK)
+
+/* AST_PECI_INT_CTRL/STS - 0x18/0x1c : Interrupt Register */
+#define PECI_INT_TIMING_RESULT_MASK GENMASK(31, 30)
+#define PECI_INT_TIMEOUT BIT(4)
+#define PECI_INT_CONNECT BIT(3)
+#define PECI_INT_W_FCS_BAD BIT(2)
+#define PECI_INT_W_FCS_ABORT BIT(1)
+#define PECI_INT_CMD_DONE BIT(0)
+
+struct aspeed_peci {
+ struct miscdevice miscdev;
+ struct device *dev;
+ struct regmap *regmap;
+ atomic_t ref_count;
+ int irq;
+ struct completion xfer_complete;
+ u32 sts;
+ u32 cmd_timeout_ms;
+ bool initialized;
+ bool cmd_support[PECI_CMD_MAX];
+ struct mutex mutex;
+};
+
+#define PECI_INT_MASK (PECI_INT_TIMEOUT | PECI_INT_CONNECT | \
+ PECI_INT_W_FCS_BAD | PECI_INT_W_FCS_ABORT | \
+ PECI_INT_CMD_DONE)
+
+#define PECI_IDLE_CHECK_TIMEOUT 5
+
+#define PECI_RD_SAMPLING_POINT_DEFAULT 8
+#define PECI_RD_SAMPLING_POINT_MAX 15
+#define PECI_CLK_DIV_DEFAULT 0
+#define PECI_CLK_DIV_MAX 7
+#define PECI_MSG_TIMING_NEGO_DEFAULT 1
+#define PECI_MSG_TIMING_NEGO_MAX 255
+#define PECI_ADDR_TIMING_NEGO_DEFAULT 1
+#define PECI_ADDR_TIMING_NEGO_MAX 255
+#define PECI_CMD_TIMEOUT_MS_DEFAULT 1000
+#define PECI_CMD_TIMEOUT_MS_MAX 60000
+
+#define PECI_CRC8_POLYNOMIAL 0x07
+
+DECLARE_CRC8_TABLE(aspeed_peci_crc8_table);
+
+static struct aspeed_peci *aspeed_peci_priv;
+
+
+static u8 compute_aw_fcs(u8 *data, int len)
+{
+ return crc8(aspeed_peci_crc8_table, data, (size_t)len, 0);
+}
+
+static int ioctl_xfer_msg(struct aspeed_peci *priv, void *pmsg)
+{
+ struct peci_xfer_msg *pumsg = pmsg;
+ u32 peci_head;
+ u32 peci_state;
+ u32 rx_data;
+ uint reg;
+ long timeout;
+ int i;
+ int rc = 0;
+
+ reinit_completion(&priv->xfer_complete);
+
+ peci_head = PECI_TAGET_ADDR(pumsg->client_addr) |
+ PECI_WRITE_LEN(pumsg->tx_len) |
+ PECI_READ_LEN(pumsg->rx_len);
+
+ rc = regmap_write(priv->regmap, AST_PECI_CMD_CTRL, peci_head);
+ if (rc)
+ return rc;
+
+ for (i = 0; i < pumsg->tx_len; i += 4) {
+ reg = i < 16 ? AST_PECI_W_DATA0 + i % 16 :
+ AST_PECI_W_DATA4 + i % 16;
+ rc = regmap_write(priv->regmap, reg,
+ (pumsg->tx_buf[i + 3] << 24) |
+ (pumsg->tx_buf[i + 2] << 16) |
+ (pumsg->tx_buf[i + 1] << 8) |
+ pumsg->tx_buf[i + 0]);
+ if (rc)
+ return rc;
+ }
+
+ dev_dbg(priv->dev, "HEAD : 0x%08x\n", peci_head);
+#if DUMP_DEBUG
+ print_hex_dump(KERN_DEBUG, "TX : ", DUMP_PREFIX_NONE, 16, 1,
+ pumsg->tx_buf, pumsg->tx_len, true);
+#endif
+
+ rc = regmap_write(priv->regmap, AST_PECI_CMD, PECI_CMD_FIRE);
+ if (rc)
+ return rc;
+
+ timeout = wait_for_completion_interruptible_timeout(
+ &priv->xfer_complete,
+ msecs_to_jiffies(priv->cmd_timeout_ms));
+
+ dev_dbg(priv->dev, "INT_STS : 0x%08x\n", priv->sts);
+ if (!regmap_read(priv->regmap, AST_PECI_CMD, &peci_state))
+ dev_dbg(priv->dev, "PECI_STATE : 0x%lx\n",
+ PECI_CMD_STS_GET(peci_state));
+ else
+ dev_dbg(priv->dev, "PECI_STATE : read error\n");
+
+ if (timeout <= 0 || !(priv->sts & PECI_INT_CMD_DONE)) {
+ if (timeout <= 0) {
+ dev_dbg(priv->dev, "Timeout waiting for a response!\n");
+ rc = -ETIME;
+ } else {
+ dev_dbg(priv->dev, "No valid response!\n");
+ rc = -EFAULT;
+ }
+ return rc;
+ }
+
+ for (i = 0; i < pumsg->rx_len; i++) {
+ u8 byte_offset = i % 4;
+
+ if (byte_offset == 0) {
+ reg = i < 16 ? AST_PECI_R_DATA0 + i % 16 :
+ AST_PECI_R_DATA4 + i % 16;
+ rc = regmap_read(priv->regmap, reg, &rx_data);
+ if (rc)
+ return rc;
+ }
+
+ pumsg->rx_buf[i] = (u8)(rx_data >> (byte_offset << 3));
+ }
+
+#if DUMP_DEBUG
+ print_hex_dump(KERN_DEBUG, "RX : ", DUMP_PREFIX_NONE, 16, 1,
+ pumsg->rx_buf, pumsg->rx_len, true);
+#endif
+ if (!regmap_read(priv->regmap, AST_PECI_CMD, &peci_state))
+ dev_dbg(priv->dev, "PECI_STATE : 0x%lx\n",
+ PECI_CMD_STS_GET(peci_state));
+ else
+ dev_dbg(priv->dev, "PECI_STATE : read error\n");
+ dev_dbg(priv->dev, "------------------------\n");
+
+ return rc;
+}
+
+static int
+xfer_msg_with_retries(struct aspeed_peci *priv, void *pmsg, bool has_aw_fcs)
+{
+ struct peci_xfer_msg *pumsg = pmsg;
+ uint retries = DEV_PECI_RETRY_ATTEMPTS;
+ int rc = 0;
+
+ /* Per the PECI spec, need to retry any commands that return 0x8x */
+ do {
+ rc = ioctl_xfer_msg(priv, pumsg);
+ if (!(!rc && ((pumsg->rx_buf[0] & DEV_PECI_CC_RETRY_ERR_MASK) ==
+ DEV_PECI_CC_TIMEOUT)))
+ break;
+
+ /* Set the retry bit to indicate a retry attempt */
+ pumsg->tx_buf[1] |= DEV_PECI_RETRY_BIT;
+
+ /* Recalculate the AW FCS if it has one */
+ if (has_aw_fcs)
+ pumsg->tx_buf[pumsg->tx_len - 1] = 0x80 ^
+ compute_aw_fcs((u8 *)pumsg,
+ 2 + pumsg->tx_len);
+
+ /*
+ * Retry for at least 250ms before returning an error.
+ * Retry interval guideline:
+ * No minimum < Retry Interval < No maximum
+ * (recommend 10ms)
+ */
+ usleep_range(DEV_PECI_RETRY_DELAY_MS * 1000,
+ (DEV_PECI_RETRY_DELAY_MS * 1000) + 1000);
+ } while (retries--);
+
+ return rc;
+}
+
+static int initialize(struct aspeed_peci *priv)
+{
+ struct peci_xfer_msg msg;
+ u32 dib;
+ int rc = 0;
+
+ /* Initialize it just once. */
+ if (priv->initialized)
+ return 0;
+
+ /* Update command table just once. */
+ if (priv->cmd_support[PECI_CMD_PING])
+ return 0;
+
+ msg.client_addr = PECI_BASE_ADDR;
+ msg.tx_len = GET_DIB_WR_LEN;
+ msg.rx_len = GET_DIB_RD_LEN;
+ msg.tx_buf[0] = GET_DIB_PECI_CMD;
+
+ rc = ioctl_xfer_msg(priv, &msg);
+ if (rc < 0) {
+ dev_dbg(priv->dev, "PECI xfer error, rc : %d\n", rc);
+ return rc;
+ }
+
+ dib = msg.rx_buf[0] | (msg.rx_buf[1] << 8) |
+ (msg.rx_buf[2] << 16) | (msg.rx_buf[3] << 24);
+
+ /* Check special case for Get DIB command */
+ if (dib == 0x00) {
+ dev_dbg(priv->dev, "DIB read as 0x00\n");
+ return -1;
+ }
+
+ if (!rc) {
+ /*
+ * setting up the supporting commands based on minor rev#
+ * see PECI Spec Table 3-1
+ */
+ priv->cmd_support[PECI_CMD_PING] = true;
+ priv->cmd_support[PECI_CMD_GET_TEMP] = true;
+ priv->cmd_support[PECI_CMD_GET_DIB] = true;
+
+ /* get minor rev# */
+ dib = (dib >> 8) & 0xF;
+
+ if (dib >= 0x1) {
+ priv->cmd_support[PECI_CMD_RD_PKG_CFG] = true;
+ priv->cmd_support[PECI_CMD_WR_PKG_CFG] = true;
+ }
+
+ if (dib >= 0x2)
+ priv->cmd_support[PECI_CMD_RD_IA_MSR] = true;
+
+ if (dib >= 0x3) {
+ priv->cmd_support[PECI_CMD_RD_PCI_CFG_LOCAL] = true;
+ priv->cmd_support[PECI_CMD_WR_PCI_CFG_LOCAL] = true;
+ }
+
+ if (dib >= 0x4)
+ priv->cmd_support[PECI_CMD_RD_PCI_CFG] = true;
+
+ if (dib >= 0x5)
+ priv->cmd_support[PECI_CMD_WR_PCI_CFG] = true;
+
+ if (dib >= 0x6)
+ priv->cmd_support[PECI_CMD_WR_IA_MSR] = true;
+
+ priv->initialized = true;
+ } else {
+ dev_dbg(priv->dev, "Error reading DIB, rc : %d\n", rc);
+ }
+
+ return rc;
+}
+
+static int ioctl_ping(struct aspeed_peci *priv, void *pmsg)
+{
+ struct peci_ping_msg *pumsg = pmsg;
+ struct peci_xfer_msg msg;
+ int rc;
+
+ if (!priv->initialized && initialize(priv) < 0) {
+ dev_dbg(priv->dev, "Failed to initialize peci\n");
+ return -EIO;
+ }
+
+ if (!priv->cmd_support[PECI_CMD_PING]) {
+ dev_dbg(priv->dev, "Command is not supported\n");
+ return -EBADRQC;
+ }
+
+ msg.client_addr = pumsg->target;
+ msg.tx_len = 0;
+ msg.rx_len = 0;
+
+ rc = ioctl_xfer_msg(priv, &msg);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+static int ioctl_get_dib(struct aspeed_peci *priv, void *pmsg)
+{
+ struct peci_get_dib_msg *pumsg = pmsg;
+ struct peci_xfer_msg msg;
+ int rc;
+
+ if (!priv->initialized && initialize(priv) < 0) {
+ dev_dbg(priv->dev, "Failed to initialize peci\n");
+ return -EIO;
+ }
+
+ if (!priv->cmd_support[PECI_CMD_GET_DIB]) {
+ dev_dbg(priv->dev, "Command is not supported\n");
+ return -EBADRQC;
+ }
+
+ msg.client_addr = pumsg->target;
+ msg.tx_len = GET_DIB_WR_LEN;
+ msg.rx_len = GET_DIB_RD_LEN;
+ msg.tx_buf[0] = GET_DIB_PECI_CMD;
+
+ rc = ioctl_xfer_msg(priv, &msg);
+ if (rc < 0)
+ return rc;
+
+ pumsg->dib = msg.rx_buf[0] | (msg.rx_buf[1] << 8) |
+ (msg.rx_buf[2] << 16) | (msg.rx_buf[3] << 24);
+
+ return 0;
+}
+
+static int ioctl_get_temp(struct aspeed_peci *priv, void *pmsg)
+{
+ struct peci_get_temp_msg *pumsg = pmsg;
+ struct peci_xfer_msg msg;
+ int rc;
+
+ if (!priv->initialized && initialize(priv) < 0) {
+ dev_dbg(priv->dev, "Failed to initialize peci\n");
+ return -EIO;
+ }
+
+ if (!priv->cmd_support[PECI_CMD_GET_TEMP]) {
+ dev_dbg(priv->dev, "Command is not supported\n");
+ return -EBADRQC;
+ }
+
+ msg.client_addr = pumsg->target;
+ msg.tx_len = GET_TEMP_WR_LEN;
+ msg.rx_len = GET_TEMP_RD_LEN;
+ msg.tx_buf[0] = GET_TEMP_PECI_CMD;
+
+ rc = ioctl_xfer_msg(priv, &msg);
+ if (rc < 0)
+ return rc;
+
+ pumsg->temp_raw = (signed short)(msg.rx_buf[0] | (msg.rx_buf[1] << 8));
+
+ return 0;
+}
+
+static int ioctl_rd_pkg_cfg(struct aspeed_peci *priv, void *pmsg)
+{
+ struct peci_rd_pkg_cfg_msg *pumsg = pmsg;
+ struct peci_xfer_msg msg;
+ int rc = 0;
+
+ /* Per the PECI spec, the read length must be a byte, word, or dword */
+ if (pumsg->rx_len != 1 && pumsg->rx_len != 2 && pumsg->rx_len != 4) {
+ dev_dbg(priv->dev, "Invalid read length, rx_len: %d\n",
+ pumsg->rx_len);
+ return -EINVAL;
+ }
+
+ if (!priv->initialized && initialize(priv) < 0) {
+ dev_dbg(priv->dev, "Failed to initialize peci\n");
+ return -EIO;
+ }
+
+ if (!priv->cmd_support[PECI_CMD_RD_PKG_CFG]) {
+ dev_dbg(priv->dev, "Command is not supported\n");
+ return -EBADRQC;
+ }
+
+ msg.client_addr = pumsg->target;
+ msg.tx_len = RDPKGCFG_WRITE_LEN;
+ /* read lengths of 1 and 2 result in an error, so only use 4 for now */
+ msg.rx_len = RDPKGCFG_READ_LEN_BASE + pumsg->rx_len;
+ msg.tx_buf[0] = RDPKGCFG_PECI_CMD;
+ msg.tx_buf[1] = 0x00; /* request byte for Host ID / Retry bit */
+ /* Host ID is 0 for PECI 3.0 */
+ msg.tx_buf[2] = pumsg->index; /* RdPkgConfig index */
+ msg.tx_buf[3] = (u8)pumsg->param; /* LSB - Config parameter */
+ msg.tx_buf[4] = (u8)(pumsg->param >> 8); /* MSB - Config parameter */
+
+ rc = xfer_msg_with_retries(priv, &msg, false);
+ if (rc || msg.rx_buf[0] != DEV_PECI_CC_SUCCESS) {
+ dev_dbg(priv->dev, "ioctl error, rc : %d\n", rc);
+ return -EIO;
+ }
+
+ memcpy(pumsg->pkg_config, &msg.rx_buf[1], pumsg->rx_len);
+
+ return rc;
+}
+
+static int ioctl_wr_pkg_cfg(struct aspeed_peci *priv, void *pmsg)
+{
+ struct peci_wr_pkg_cfg_msg *pumsg = pmsg;
+ struct peci_xfer_msg msg;
+ int rc = 0, i;
+
+ /* Per the PECI spec, the write length must be a dword */
+ if (pumsg->tx_len != 4) {
+ dev_dbg(priv->dev, "Invalid write length, tx_len: %d\n",
+ pumsg->tx_len);
+ return -EINVAL;
+ }
+
+ if (!priv->initialized && initialize(priv) < 0) {
+ dev_dbg(priv->dev, "Failed to initialize peci\n");
+ return -EIO;
+ }
+
+ if (!priv->cmd_support[PECI_CMD_WR_PKG_CFG]) {
+ dev_dbg(priv->dev, "Command is not supported\n");
+ return -EBADRQC;
+ }
+
+ msg.client_addr = pumsg->target;
+ msg.tx_len = WRPKGCFG_WRITE_LEN_BASE + pumsg->tx_len;
+ /* read lengths of 1 and 2 result in an error, so only use 4 for now */
+ msg.rx_len = WRPKGCFG_READ_LEN;
+ msg.tx_buf[0] = WRPKGCFG_PECI_CMD;
+ msg.tx_buf[1] = 0x00; /* request byte for Host ID / Retry bit */
+ /* Host ID is 0 for PECI 3.0 */
+ msg.tx_buf[2] = pumsg->index; /* RdPkgConfig index */
+ msg.tx_buf[3] = (u8)pumsg->param; /* LSB - Config parameter */
+ msg.tx_buf[4] = (u8)(pumsg->param >> 8); /* MSB - Config parameter */
+ for (i = 0; i < pumsg->tx_len; i++)
+ msg.tx_buf[5 + i] = ((u8 *)&pumsg->value)[i];
+
+ /* Add an Assure Write Frame Check Sequence byte */
+ msg.tx_buf[5 + i] = 0x80 ^
+ compute_aw_fcs((u8 *)&msg, 8 + pumsg->tx_len);
+
+ rc = xfer_msg_with_retries(priv, &msg, true);
+ if (rc || msg.rx_buf[0] != DEV_PECI_CC_SUCCESS) {
+ dev_dbg(priv->dev, "ioctl error, rc : %d\n", rc);
+ return -EIO;
+ }
+
+ return rc;
+}
+
+static int ioctl_rd_ia_msr(struct aspeed_peci *priv, void *pmsg)
+{
+ struct peci_rd_ia_msr_msg *pumsg = pmsg;
+ struct peci_xfer_msg msg;
+ int rc = 0;
+
+ if (!priv->initialized && initialize(priv) < 0) {
+ dev_dbg(priv->dev, "Failed to initialize peci\n");
+ return -EIO;
+ }
+
+ if (!priv->cmd_support[PECI_CMD_RD_IA_MSR]) {
+ dev_dbg(priv->dev, "Command is not supported\n");
+ return -EBADRQC;
+ }
+
+ msg.client_addr = pumsg->target;
+ msg.tx_len = RDIAMSR_WRITE_LEN;
+ msg.rx_len = RDIAMSR_READ_LEN;
+ msg.tx_buf[0] = RDIAMSR_PECI_CMD;
+ msg.tx_buf[1] = 0x00;
+ msg.tx_buf[2] = pumsg->thread_id;
+ msg.tx_buf[3] = (u8)pumsg->address;
+ msg.tx_buf[4] = (u8)(pumsg->address >> 8);
+
+ rc = xfer_msg_with_retries(priv, &msg, false);
+ if (rc || msg.rx_buf[0] != DEV_PECI_CC_SUCCESS) {
+ dev_dbg(priv->dev, "ioctl error, rc : %d\n", rc);
+ return -EIO;
+ }
+
+ memcpy(&pumsg->value, &msg.rx_buf[1], sizeof(uint64_t));
+
+ return rc;
+}
+
+static int ioctl_rd_pci_cfg(struct aspeed_peci *priv, void *pmsg)
+{
+ struct peci_rd_pci_cfg_msg *pumsg = pmsg;
+ struct peci_xfer_msg msg;
+ u32 address;
+ int rc = 0;
+
+ if (!priv->initialized && initialize(priv) < 0) {
+ dev_dbg(priv->dev, "Failed to initialize peci\n");
+ return -EIO;
+ }
+
+ if (!priv->cmd_support[PECI_CMD_RD_PCI_CFG]) {
+ dev_dbg(priv->dev, "Command is not supported\n");
+ return -EBADRQC;
+ }
+
+ address = pumsg->reg; /* [11:0] - Register */
+ address |= (u32)pumsg->function << 12; /* [14:12] - Function */
+ address |= (u32)pumsg->device << 15; /* [19:15] - Device */
+ address |= (u32)pumsg->bus << 20; /* [27:20] - Bus */
+ /* [31:28] - Reserved */
+ msg.client_addr = pumsg->target;
+ msg.tx_len = RDPCICFG_WRITE_LEN;
+ msg.rx_len = RDPCICFG_READ_LEN;
+ msg.tx_buf[0] = RDPCICFG_PECI_CMD;
+ msg.tx_buf[1] = 0x00; /* request byte for Host ID / Retry bit */
+ /* Host ID is 0 for PECI 3.0 */
+ msg.tx_buf[2] = (u8)address; /* LSB - PCI Config Address */
+ msg.tx_buf[3] = (u8)(address >> 8); /* PCI Config Address */
+ msg.tx_buf[4] = (u8)(address >> 16); /* PCI Config Address */
+ msg.tx_buf[5] = (u8)(address >> 24); /* MSB - PCI Config Address */
+
+ rc = xfer_msg_with_retries(priv, &msg, false);
+ if (rc || msg.rx_buf[0] != DEV_PECI_CC_SUCCESS) {
+ dev_dbg(priv->dev, "ioctl error, rc : %d\n", rc);
+ return -EIO;
+ }
+
+ memcpy(pumsg->pci_config, &msg.rx_buf[1], 4);
+
+ return rc;
+}
+
+static int ioctl_rd_pci_cfg_local(struct aspeed_peci *priv, void *pmsg)
+{
+ struct peci_rd_pci_cfg_local_msg *pumsg = pmsg;
+ struct peci_xfer_msg msg;
+ u32 address;
+ int rc = 0;
+
+ /* Per the PECI spec, the read length must be a byte, word, or dword */
+ if (pumsg->rx_len != 1 && pumsg->rx_len != 2 && pumsg->rx_len != 4) {
+ dev_dbg(priv->dev, "Invalid read length, rx_len: %d\n",
+ pumsg->rx_len);
+ return -EINVAL;
+ }
+
+ if (!priv->initialized && initialize(priv) < 0) {
+ dev_dbg(priv->dev, "Failed to initialize peci\n");
+ return -EIO;
+ }
+
+ if (!priv->cmd_support[PECI_CMD_RD_PCI_CFG_LOCAL]) {
+ dev_dbg(priv->dev, "Command is not supported\n");
+ return -EBADRQC;
+ }
+
+ address = pumsg->reg; /* [11:0] - Register */
+ address |= (u32)pumsg->function << 12; /* [14:12] - Function */
+ address |= (u32)pumsg->device << 15; /* [19:15] - Device */
+ address |= (u32)pumsg->bus << 20; /* [23:20] - Bus */
+
+ msg.client_addr = pumsg->target;
+ msg.tx_len = RDPCICFGLOCAL_WRITE_LEN;
+ msg.rx_len = RDPCICFGLOCAL_READ_LEN_BASE + pumsg->rx_len;
+ msg.tx_buf[0] = RDPCICFGLOCAL_PECI_CMD;
+ msg.tx_buf[1] = 0x00; /* request byte for Host ID / Retry bit */
+ /* Host ID is 0 for PECI 3.0 */
+ msg.tx_buf[2] = (u8)address; /* LSB - PCI Configuration Address */
+ msg.tx_buf[3] = (u8)(address >> 8); /* PCI Configuration Address */
+ msg.tx_buf[4] = (u8)(address >> 16); /* PCI Configuration Address */
+
+ rc = xfer_msg_with_retries(priv, &msg, false);
+ if (rc || msg.rx_buf[0] != DEV_PECI_CC_SUCCESS) {
+ dev_dbg(priv->dev, "ioctl error, rc : %d\n", rc);
+ return -EIO;
+ }
+
+ memcpy(pumsg->pci_config, &msg.rx_buf[1], pumsg->rx_len);
+
+ return rc;
+}
+
+static int ioctl_wr_pci_cfg_local(struct aspeed_peci *priv, void *pmsg)
+{
+ struct peci_wr_pci_cfg_local_msg *pumsg = pmsg;
+ struct peci_xfer_msg msg;
+ u32 address;
+ int rc = 0, i;
+
+ /* Per the PECI spec, the write length must be a byte, word, or dword */
+ if (pumsg->tx_len != 1 && pumsg->tx_len != 2 && pumsg->tx_len != 4) {
+ dev_dbg(priv->dev, "Invalid write length, tx_len: %d\n",
+ pumsg->tx_len);
+ return -EINVAL;
+ }
+
+ if (!priv->initialized && initialize(priv) < 0) {
+ dev_dbg(priv->dev, "Failed to initialize peci\n");
+ return -EIO;
+ }
+
+ if (!priv->cmd_support[PECI_CMD_RD_PCI_CFG_LOCAL]) {
+ dev_dbg(priv->dev, "Command is not supported\n");
+ return -EBADRQC;
+ }
+
+ address = pumsg->reg; /* [11:0] - Register */
+ address |= (u32)pumsg->function << 12; /* [14:12] - Function */
+ address |= (u32)pumsg->device << 15; /* [19:15] - Device */
+ address |= (u32)pumsg->bus << 20; /* [23:20] - Bus */
+
+ msg.client_addr = pumsg->target;
+ msg.tx_len = WRPCICFGLOCAL_WRITE_LEN_BASE + pumsg->tx_len;
+ msg.rx_len = WRPCICFGLOCAL_READ_LEN;
+ msg.tx_buf[0] = WRPCICFGLOCAL_PECI_CMD;
+ msg.tx_buf[1] = 0x00; /* request byte for Host ID / Retry bit */
+ /* Host ID is 0 for PECI 3.0 */
+ msg.tx_buf[2] = (u8)address; /* LSB - PCI Configuration Address */
+ msg.tx_buf[3] = (u8)(address >> 8); /* PCI Configuration Address */
+ msg.tx_buf[4] = (u8)(address >> 16); /* PCI Configuration Address */
+ for (i = 0; i < pumsg->tx_len; i++)
+ msg.tx_buf[5 + i] = ((u8 *)&pumsg->value)[i];
+
+ /* Add an Assure Write Frame Check Sequence byte */
+ msg.tx_buf[5 + i] = 0x80 ^
+ compute_aw_fcs((u8 *)&msg, 8 + pumsg->tx_len);
+
+ rc = xfer_msg_with_retries(priv, &msg, true);
+ if (rc || msg.rx_buf[0] != DEV_PECI_CC_SUCCESS) {
+ dev_dbg(priv->dev, "ioctl error, rc : %d\n", rc);
+ return -EIO;
+ }
+
+ return rc;
+}
+
+
+typedef int (*ioctl_fn)(struct aspeed_peci *, void *);
+
+static ioctl_fn peci_ioctl_fn[PECI_CMD_MAX] = {
+ ioctl_xfer_msg,
+ ioctl_ping,
+ ioctl_get_dib,
+ ioctl_get_temp,
+ ioctl_rd_pkg_cfg,
+ ioctl_wr_pkg_cfg,
+ ioctl_rd_ia_msr,
+ NULL, /* Reserved */
+ ioctl_rd_pci_cfg,
+ NULL, /* Reserved */
+ ioctl_rd_pci_cfg_local,
+ ioctl_wr_pci_cfg_local,
+};
+
+
+long peci_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct aspeed_peci *priv;
+ long ret = 0;
+ void __user *argp = (void __user *)arg;
+ int timeout = PECI_IDLE_CHECK_TIMEOUT;
+ u8 msg[sizeof(struct peci_xfer_msg)];
+ unsigned int peci_cmd, msg_size;
+ u32 cmd_sts;
+
+ /*
+ * Treat it as an inter module call when filp is null but only in case
+ * the private data is initialized.
+ */
+ if (filp)
+ priv = container_of(filp->private_data,
+ struct aspeed_peci, miscdev);
+ else
+ priv = aspeed_peci_priv;
+
+ if (!priv)
+ return -ENXIO;
+
+ switch (cmd) {
+ case PECI_IOC_XFER:
+ case PECI_IOC_PING:
+ case PECI_IOC_GET_DIB:
+ case PECI_IOC_GET_TEMP:
+ case PECI_IOC_RD_PKG_CFG:
+ case PECI_IOC_WR_PKG_CFG:
+ case PECI_IOC_RD_IA_MSR:
+ case PECI_IOC_RD_PCI_CFG:
+ case PECI_IOC_RD_PCI_CFG_LOCAL:
+ case PECI_IOC_WR_PCI_CFG_LOCAL:
+ peci_cmd = _IOC_TYPE(cmd) - PECI_IOC_BASE;
+ msg_size = _IOC_SIZE(cmd);
+ break;
+
+ default:
+ dev_dbg(priv->dev, "Invalid ioctl cmd : 0x%08x\n", cmd);
+ return -EINVAL;
+ }
+
+ if (!peci_ioctl_fn[peci_cmd])
+ return -EPERM;
+
+ mutex_lock(&priv->mutex);
+
+ dev_dbg(priv->dev, "CMD : 0x%08x, peci_cmd : %d, msg_size : %d\n",
+ cmd, peci_cmd, msg_size);
+
+ /* Check command sts and bus idle state */
+ while (!regmap_read(priv->regmap, AST_PECI_CMD, &cmd_sts)
+ && (cmd_sts & (PECI_CMD_STS_MASK | PECI_CMD_PIN_MON))) {
+ if (timeout-- < 0) {
+ dev_dbg(priv->dev, "Timeout waiting for idle state!\n");
+ ret = -ETIME;
+ goto out;
+ }
+ usleep_range(10000, 11000);
+ };
+
+ if (msg_size &&
+ (filp ? copy_from_user(&msg, argp, msg_size) :
+ memcpy(&msg, (const void *)arg, msg_size) != &msg)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ ret = peci_ioctl_fn[peci_cmd](priv, &msg);
+
+ if (ret == 0 && msg_size &&
+ (filp ? copy_to_user(argp, &msg, msg_size) :
+ memcpy((void *)arg, &msg, msg_size) != (void *)arg))
+ ret = -EFAULT;
+
+out:
+ mutex_unlock(&priv->mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(peci_ioctl);
+
+static int aspeed_peci_open(struct inode *inode, struct file *filp)
+{
+ struct aspeed_peci *priv =
+ container_of(filp->private_data, struct aspeed_peci, miscdev);
+
+ atomic_inc(&priv->ref_count);
+
+ dev_dbg(priv->dev, "ref_count : %d\n", atomic_read(&priv->ref_count));
+
+ return 0;
+}
+
+static int aspeed_peci_release(struct inode *inode, struct file *filp)
+{
+ struct aspeed_peci *priv =
+ container_of(filp->private_data, struct aspeed_peci, miscdev);
+
+ atomic_dec(&priv->ref_count);
+
+ dev_dbg(priv->dev, "ref_count : %d\n", atomic_read(&priv->ref_count));
+
+ return 0;
+}
+
+static irqreturn_t aspeed_peci_irq_handler(int irq, void *arg)
+{
+ struct aspeed_peci *priv = arg;
+ bool valid_irq = true;
+
+ if (regmap_read(priv->regmap, AST_PECI_INT_STS, &priv->sts))
+ return IRQ_NONE;
+
+ switch (priv->sts & PECI_INT_MASK) {
+ case PECI_INT_TIMEOUT:
+ dev_dbg(priv->dev, "PECI_INT_TIMEOUT\n");
+ if (regmap_write(priv->regmap, AST_PECI_INT_STS,
+ PECI_INT_TIMEOUT))
+ return IRQ_NONE;
+ break;
+ case PECI_INT_CONNECT:
+ dev_dbg(priv->dev, "PECI_INT_CONNECT\n");
+ if (regmap_write(priv->regmap, AST_PECI_INT_STS,
+ PECI_INT_CONNECT))
+ return IRQ_NONE;
+ break;
+ case PECI_INT_W_FCS_BAD:
+ dev_dbg(priv->dev, "PECI_INT_W_FCS_BAD\n");
+ if (regmap_write(priv->regmap, AST_PECI_INT_STS,
+ PECI_INT_W_FCS_BAD))
+ return IRQ_NONE;
+ break;
+ case PECI_INT_W_FCS_ABORT:
+ dev_dbg(priv->dev, "PECI_INT_W_FCS_ABORT\n");
+ if (regmap_write(priv->regmap, AST_PECI_INT_STS,
+ PECI_INT_W_FCS_ABORT))
+ return IRQ_NONE;
+ break;
+ case PECI_INT_CMD_DONE:
+ dev_dbg(priv->dev, "PECI_INT_CMD_DONE\n");
+ if (regmap_write(priv->regmap, AST_PECI_INT_STS,
+ PECI_INT_CMD_DONE) ||
+ regmap_write(priv->regmap, AST_PECI_CMD, 0))
+ return IRQ_NONE;
+ break;
+ default:
+ dev_dbg(priv->dev, "Unknown PECI interrupt : 0x%08x\n",
+ priv->sts);
+ if (regmap_write(priv->regmap, AST_PECI_INT_STS, priv->sts))
+ return IRQ_NONE;
+ valid_irq = false;
+ break;
+ }
+
+ if (valid_irq)
+ complete(&priv->xfer_complete);
+
+ return IRQ_HANDLED;
+}
+
+static int aspeed_peci_init_ctrl(struct aspeed_peci *priv)
+{
+ struct clk *clkin;
+ u32 clk_freq, clk_divisor, clk_div_val = 0;
+ u32 msg_timing_nego, addr_timing_nego, rd_sampling_point;
+ int ret;
+
+ clkin = devm_clk_get(priv->dev, NULL);
+ if (IS_ERR(clkin)) {
+ dev_err(priv->dev, "Failed to get clk source.\n");
+ return PTR_ERR(clkin);
+ }
+
+ ret = of_property_read_u32(priv->dev->of_node, "clock-frequency",
+ &clk_freq);
+ if (ret < 0) {
+ dev_err(priv->dev,
+ "Could not read clock-frequency property.\n");
+ return ret;
+ }
+
+ clk_divisor = clk_get_rate(clkin) / clk_freq;
+ devm_clk_put(priv->dev, clkin);
+
+ while ((clk_divisor >> 1) && (clk_div_val < PECI_CLK_DIV_MAX))
+ clk_div_val++;
+
+ ret = of_property_read_u32(priv->dev->of_node, "msg-timing-nego",
+ &msg_timing_nego);
+ if (ret || msg_timing_nego > PECI_MSG_TIMING_NEGO_MAX) {
+ dev_warn(priv->dev,
+ "Invalid msg-timing-nego : %u, Use default : %u\n",
+ msg_timing_nego, PECI_MSG_TIMING_NEGO_DEFAULT);
+ msg_timing_nego = PECI_MSG_TIMING_NEGO_DEFAULT;
+ }
+
+ ret = of_property_read_u32(priv->dev->of_node, "addr-timing-nego",
+ &addr_timing_nego);
+ if (ret || addr_timing_nego > PECI_ADDR_TIMING_NEGO_MAX) {
+ dev_warn(priv->dev,
+ "Invalid addr-timing-nego : %u, Use default : %u\n",
+ addr_timing_nego, PECI_ADDR_TIMING_NEGO_DEFAULT);
+ addr_timing_nego = PECI_ADDR_TIMING_NEGO_DEFAULT;
+ }
+
+ ret = of_property_read_u32(priv->dev->of_node, "rd-sampling-point",
+ &rd_sampling_point);
+ if (ret || rd_sampling_point > PECI_RD_SAMPLING_POINT_MAX) {
+ dev_warn(priv->dev,
+ "Invalid rd-sampling-point : %u. Use default : %u\n",
+ rd_sampling_point,
+ PECI_RD_SAMPLING_POINT_DEFAULT);
+ rd_sampling_point = PECI_RD_SAMPLING_POINT_DEFAULT;
+ }
+
+ ret = of_property_read_u32(priv->dev->of_node, "cmd-timeout-ms",
+ &priv->cmd_timeout_ms);
+ if (ret || priv->cmd_timeout_ms > PECI_CMD_TIMEOUT_MS_MAX ||
+ priv->cmd_timeout_ms == 0) {
+ dev_warn(priv->dev,
+ "Invalid cmd-timeout-ms : %u. Use default : %u\n",
+ priv->cmd_timeout_ms,
+ PECI_CMD_TIMEOUT_MS_DEFAULT);
+ priv->cmd_timeout_ms = PECI_CMD_TIMEOUT_MS_DEFAULT;
+ }
+
+ ret = regmap_write(priv->regmap, AST_PECI_CTRL,
+ PECI_CTRL_CLK_DIV(PECI_CLK_DIV_DEFAULT) |
+ PECI_CTRL_PECI_CLK_EN);
+ if (ret)
+ return ret;
+
+ usleep_range(1000, 5000);
+
+ /*
+ * Timing negotiation period setting.
+ * The unit of the programmed value is 4 times of PECI clock period.
+ */
+ ret = regmap_write(priv->regmap, AST_PECI_TIMING,
+ PECI_TIMING_MESSAGE(msg_timing_nego) |
+ PECI_TIMING_ADDRESS(addr_timing_nego));
+ if (ret)
+ return ret;
+
+ /* Clear interrupts. */
+ ret = regmap_write(priv->regmap, AST_PECI_INT_STS, PECI_INT_MASK);
+ if (ret)
+ return ret;
+
+ /* Enable interrupts. */
+ ret = regmap_write(priv->regmap, AST_PECI_INT_CTRL, PECI_INT_MASK);
+ if (ret)
+ return ret;
+
+ /* Read sampling point and clock speed setting. */
+ ret = regmap_write(priv->regmap, AST_PECI_CTRL,
+ PECI_CTRL_SAMPLING(rd_sampling_point) |
+ PECI_CTRL_CLK_DIV(clk_div_val) |
+ PECI_CTRL_PECI_EN | PECI_CTRL_PECI_CLK_EN);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct regmap_config aspeed_peci_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = AST_PECI_R_DATA7,
+ .fast_io = true,
+};
+
+static const struct file_operations aspeed_peci_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .unlocked_ioctl = peci_ioctl,
+ .open = aspeed_peci_open,
+ .release = aspeed_peci_release,
+};
+
+static int __init aspeed_peci_probe(struct platform_device *pdev)
+{
+ struct aspeed_peci *priv;
+ struct device *dev;
+ struct resource *res;
+ void __iomem *base;
+ int ret = 0;
+
+ dev = &pdev->dev;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, priv);
+ priv->dev = dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ priv->regmap = devm_regmap_init_mmio(dev, base,
+ &aspeed_peci_regmap_config);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ priv->irq = platform_get_irq(pdev, 0);
+ if (!priv->irq)
+ return -ENODEV;
+
+ ret = devm_request_irq(dev, priv->irq, aspeed_peci_irq_handler,
+ IRQF_SHARED,
+ SOC_NAME "-" DEVICE_NAME "-irq",
+ priv);
+ if (ret < 0)
+ return ret;
+
+ priv->miscdev.minor = MISC_DYNAMIC_MINOR;
+ priv->miscdev.name = DEVICE_NAME;
+ priv->miscdev.parent = dev;
+ priv->miscdev.fops = &aspeed_peci_fops;
+
+ ret = misc_register(&priv->miscdev);
+ if (ret) {
+ dev_err(dev, "Failed to request interrupt.\n");
+ return ret;
+ }
+
+ mutex_init(&priv->mutex);
+ init_completion(&priv->xfer_complete);
+
+ crc8_populate_msb(aspeed_peci_crc8_table, PECI_CRC8_POLYNOMIAL);
+
+ ret = aspeed_peci_init_ctrl(priv);
+ if (ret < 0)
+ return ret;
+
+ aspeed_peci_priv = priv;
+
+ dev_info(dev, "peci registered, IRQ %d\n", priv->irq);
+
+ return 0;
+}
+
+static int aspeed_peci_remove(struct platform_device *pdev)
+{
+ struct aspeed_peci *priv = dev_get_drvdata(&pdev->dev);
+
+ aspeed_peci_priv = NULL;
+ dev_set_drvdata(&pdev->dev, NULL);
+ misc_deregister(&priv->miscdev);
+
+ return 0;
+}
+
+static const struct of_device_id aspeed_peci_of_table[] = {
+ { .compatible = "aspeed,ast2400-peci", },
+ { .compatible = "aspeed,ast2500-peci", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, aspeed_peci_of_table);
+
+static struct platform_driver aspeed_peci_driver = {
+ .probe = aspeed_peci_probe,
+ .remove = aspeed_peci_remove,
+ .driver = {
+ .name = SOC_NAME "-" DEVICE_NAME,
+ .of_match_table = aspeed_peci_of_table,
+ },
+};
+module_platform_driver(aspeed_peci_driver);
+
+MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
+MODULE_DESCRIPTION("Aspeed PECI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/misc/peci.h b/include/misc/peci.h
new file mode 100644
index 0000000..66322c6
--- /dev/null
+++ b/include/misc/peci.h
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2017 Intel Corporation
+
+#ifndef __PECI_H
+#define __PECI_H
+
+#include <linux/peci_ioctl.h>
+
+long peci_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
+
+#endif /* __PECI_H */
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index f330ba4..b34960b 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -327,6 +327,7 @@ header-y += packet_diag.h
header-y += param.h
header-y += parport.h
header-y += patchkey.h
+header-y += peci_ioctl.h
header-y += pci.h
header-y += pci_regs.h
header-y += perf_event.h
diff --git a/include/uapi/linux/peci_ioctl.h b/include/uapi/linux/peci_ioctl.h
new file mode 100644
index 0000000..8386848
--- /dev/null
+++ b/include/uapi/linux/peci_ioctl.h
@@ -0,0 +1,270 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2017 Intel Corporation
+
+#ifndef __PECI_IOCTL_H
+#define __PECI_IOCTL_H
+
+#include <linux/ioctl.h>
+
+/* Base Address of 48d */
+#define PECI_BASE_ADDR 0x30 /* The PECI client's default address of 0x30 */
+#define PECI_OFFSET_MAX 8 /* Max numver of CPU clients */
+
+/* PCI Access */
+#define MAX_PCI_READ_LEN 24 /* Number of bytes of the PCI Space read */
+
+#define PCI_BUS0_CPU0 0x00
+#define PCI_BUS0_CPU1 0x80
+#define PCI_CPUBUSNO_BUS 0x00
+#define PCI_CPUBUSNO_DEV 0x08
+#define PCI_CPUBUSNO_FUNC 0x02
+#define PCI_CPUBUSNO 0xcc
+#define PCI_CPUBUSNO_1 0xd0
+#define PCI_CPUBUSNO_VALID 0xd4
+
+/* Package Identifier Read Parameter Value */
+#define PKG_ID_CPU_ID 0x0000 /* 0 - CPUID Info */
+#define PKG_ID_PLATFORM_ID 0x0001 /* 1 - Platform ID */
+#define PKG_ID_UNCORE_ID 0x0002 /* 2 - Uncore Device ID */
+#define PKG_ID_MAX_THREAD_ID 0x0003 /* 3 - Max Thread ID */
+#define PKG_ID_MICROCODE_REV 0x0004 /* 4 - CPU Microcode Update Revision */
+#define PKG_ID_MACHINE_CHECK_STATUS 0x0005 /* 5 - Machine Check Status */
+
+/* RdPkgConfig Index */
+#define MBX_INDEX_CPU_ID 0 /* Package Identifier Read */
+#define MBX_INDEX_VR_DEBUG 1 /* VR Debug */
+#define MBX_INDEX_PKG_TEMP_READ 2 /* Package Temperature Read */
+#define MBX_INDEX_ENERGY_COUNTER 3 /* Energy counter */
+#define MBX_INDEX_ENERGY_STATUS 4 /* DDR Energy Status */
+#define MBX_INDEX_WAKE_MODE_BIT 5 /* "Wake on PECI" Mode bit */
+#define MBX_INDEX_EPI 6 /* Efficient Performance Indication */
+#define MBX_INDEX_PKG_RAPL_PERF 8 /* Package RAPL Performance Status Read */
+#define MBX_INDEX_PER_CORE_DTS_TEMP 9 /* Per Core DTS Temperature Read */
+#define MBX_INDEX_DTS_MARGIN 10 /* DTS thermal margin */
+#define MBX_INDEX_SKT_PWR_THRTL_DUR 11 /* Socket Power Throttled Duration */
+#define MBX_INDEX_CFG_TDP_CONTROL 12 /* TDP Config Control */
+#define MBX_INDEX_CFG_TDP_LEVELS 13 /* TDP Config Levels */
+#define MBX_INDEX_DDR_DIMM_TEMP 14 /* DDR DIMM Temperature */
+#define MBX_INDEX_CFG_ICCMAX 15 /* Configurable ICCMAX */
+#define MBX_INDEX_TEMP_TARGET 16 /* Temperature Target Read */
+#define MBX_INDEX_CURR_CFG_LIMIT 17 /* Current Config Limit */
+#define MBX_INDEX_DIMM_TEMP_READ 20 /* Package Thermal Status Read */
+#define MBX_INDEX_DRAM_IMC_TMP_READ 22 /* DRAM IMC Temperature Read */
+#define MBX_INDEX_DDR_CH_THERM_STAT 23 /* DDR Channel Thermal Status */
+#define MBX_INDEX_PKG_POWER_LIMIT1 26 /* Package Power Limit1 */
+#define MBX_INDEX_PKG_POWER_LIMIT2 27 /* Package Power Limit2 */
+#define MBX_INDEX_TDP 28 /* Thermal design power minimum */
+#define MBX_INDEX_TDP_HIGH 29 /* Thermal design power maximum */
+#define MBX_INDEX_TDP_UNITS 30 /* Units for power and energy registers */
+#define MBX_INDEX_RUN_TIME 31 /* Accumulated Run Time */
+#define MBX_INDEX_CONSTRAINED_TIME 32 /* Thermally Constrained Time Read */
+#define MBX_INDEX_TURBO_RATIO 33 /* Turbo Activation Ratio */
+#define MBX_INDEX_DDR_RAPL_PL1 34 /* DDR RAPL PL1 */
+#define MBX_INDEX_DDR_PWR_INFO_HIGH 35 /* DRAM Power Info Read (high) */
+#define MBX_INDEX_DDR_PWR_INFO_LOW 36 /* DRAM Power Info Read (low) */
+#define MBX_INDEX_DDR_RAPL_PL2 37 /* DDR RAPL PL2 */
+#define MBX_INDEX_DDR_RAPL_STATUS 38 /* DDR RAPL Performance Status */
+#define MBX_INDEX_DDR_HOT_ABSOLUTE 43 /* DDR Hottest Dimm Absolute Temperature */
+#define MBX_INDEX_DDR_HOT_RELATIVE 44 /* DDR Hottest Dimm Relative Temperature */
+#define MBX_INDEX_DDR_THROTTLE_TIME 45 /* DDR Throttle Time */
+#define MBX_INDEX_DDR_THERM_STATUS 46 /* DDR Thermal Status */
+#define MBX_INDEX_TIME_AVG_TEMP 47 /* Package time-averaged temperature */
+#define MBX_INDEX_TURBO_RATIO_LIMIT 49 /* Turbo Ratio Limit Read */
+#define MBX_INDEX_HWP_AUTO_OOB 53 /* HWP Autonomous Out-of-band */
+#define MBX_INDEX_DDR_WARM_BUDGET 55 /* DDR Warm Power Budget */
+#define MBX_INDEX_DDR_HOT_BUDGET 56 /* DDR Hot Power Budget */
+#define MBX_INDEX_PKG_PSYS_PWR_LIM3 57 /* Package/Psys Power Limit3 */
+#define MBX_INDEX_PKG_PSYS_PWR_LIM1 58 /* Package/Psys Power Limit1 */
+#define MBX_INDEX_PKG_PSYS_PWR_LIM2 59 /* Package/Psys Power Limit2 */
+#define MBX_INDEX_PKG_PSYS_PWR_LIM4 60 /* Package/Psys Power Limit4 */
+#define MBX_INDEX_PERF_LIMIT_REASON 65 /* Performance Limit Reasons */
+
+/* WrPkgConfig Index */
+#define MBX_INDEX_DIMM_ABIENT 19
+#define MBX_INDEX_DIMM_TEMP 24
+
+/* Device Specific Completion Code (CC) Definition */
+#define DEV_PECI_CC_RETRY_ERR_MASK 0xf0
+#define DEV_PECI_CC_SUCCESS 0x40
+#define DEV_PECI_CC_TIMEOUT 0x80
+#define DEV_PECI_CC_OUT_OF_RESOURCE 0x81
+#define DEV_PECI_CC_INVALID_REQ 0x90
+
+/* Skylake EDS says to retry for 250ms */
+#define DEV_PECI_RETRY_ATTEMPTS 25
+#define DEV_PECI_RETRY_DELAY_MS 10
+#define DEV_PECI_RETRY_BIT 0x01
+
+#define GET_TEMP_WR_LEN 1
+#define GET_TEMP_RD_LEN 2
+#define GET_TEMP_PECI_CMD 0x01
+
+#define GET_DIB_WR_LEN 1
+#define GET_DIB_RD_LEN 8
+#define GET_DIB_PECI_CMD 0xf7
+
+#define RDPKGCFG_WRITE_LEN 5
+#define RDPKGCFG_READ_LEN_BASE 1
+#define RDPKGCFG_PECI_CMD 0xa1
+
+#define WRPKGCFG_WRITE_LEN_BASE 6
+#define WRPKGCFG_READ_LEN 1
+#define WRPKGCFG_PECI_CMD 0xa5
+
+#define RDIAMSR_WRITE_LEN 5
+#define RDIAMSR_READ_LEN 9
+#define RDIAMSR_PECI_CMD 0xb1
+
+#define WRIAMSR_PECI_CMD 0xb5
+
+#define RDPCICFG_WRITE_LEN 6
+#define RDPCICFG_READ_LEN 5
+#define RDPCICFG_PECI_CMD 0x61
+
+#define WRPCICFG_PECI_CMD 0x65
+
+#define RDPCICFGLOCAL_WRITE_LEN 5
+#define RDPCICFGLOCAL_READ_LEN_BASE 1
+#define RDPCICFGLOCAL_PECI_CMD 0xe1
+
+#define WRPCICFGLOCAL_WRITE_LEN_BASE 6
+#define WRPCICFGLOCAL_READ_LEN 1
+#define WRPCICFGLOCAL_PECI_CMD 0xe5
+
+enum PECI_CMD {
+ PECI_CMD_XFER = 0,
+ PECI_CMD_PING,
+ PECI_CMD_GET_DIB,
+ PECI_CMD_GET_TEMP,
+ PECI_CMD_RD_PKG_CFG,
+ PECI_CMD_WR_PKG_CFG,
+ PECI_CMD_RD_IA_MSR,
+ PECI_CMD_WR_IA_MSR,
+ PECI_CMD_RD_PCI_CFG,
+ PECI_CMD_WR_PCI_CFG,
+ PECI_CMD_RD_PCI_CFG_LOCAL,
+ PECI_CMD_WR_PCI_CFG_LOCAL,
+ PECI_CMD_MAX,
+};
+
+#define MAX_BUFFER_SIZE 32
+
+#pragma pack(push, 1)
+struct peci_xfer_msg {
+ unsigned char client_addr;
+ unsigned char tx_len;
+ unsigned char rx_len;
+ unsigned char tx_buf[MAX_BUFFER_SIZE];
+ unsigned char rx_buf[MAX_BUFFER_SIZE];
+};
+#pragma pack(pop)
+
+struct peci_ping_msg {
+ unsigned char target;
+};
+
+struct peci_get_dib_msg {
+ unsigned char target;
+ unsigned int dib;
+};
+
+struct peci_get_temp_msg {
+ unsigned char target;
+ signed short temp_raw;
+};
+
+struct peci_rd_pkg_cfg_msg {
+ unsigned char target;
+ unsigned char index;
+ unsigned short param;
+ unsigned char rx_len;
+ unsigned char pkg_config[4];
+};
+
+struct peci_wr_pkg_cfg_msg {
+ unsigned char target;
+ unsigned char index;
+ unsigned short param;
+ unsigned char tx_len;
+ unsigned int value;
+};
+
+struct peci_rd_ia_msr_msg {
+ unsigned char target;
+ unsigned char thread_id;
+ unsigned short address;
+ unsigned long value;
+};
+
+struct peci_rd_pci_cfg_msg {
+ unsigned char target;
+ unsigned char bus;
+ unsigned char device;
+ unsigned char function;
+ unsigned short reg;
+ unsigned char pci_config[4];
+};
+
+struct peci_rd_pci_cfg_local_msg {
+ unsigned char target;
+ unsigned char bus;
+ unsigned char device;
+ unsigned char function;
+ unsigned short reg;
+ unsigned char rx_len;
+ unsigned char pci_config[4];
+};
+
+struct peci_wr_pci_cfg_local_msg {
+ unsigned char target;
+ unsigned char bus;
+ unsigned char device;
+ unsigned char function;
+ unsigned short reg;
+ unsigned char tx_len;
+ unsigned int value;
+};
+
+#define PECI_IOC_BASE 'P'
+
+#define PECI_IOC_XFER \
+ _IOWR(PECI_IOC_BASE + PECI_CMD_XFER, 0, \
+ struct peci_xfer_msg)
+
+#define PECI_IOC_PING \
+ _IOWR(PECI_IOC_BASE + PECI_CMD_PING, 0, \
+ struct peci_ping_msg)
+
+#define PECI_IOC_GET_DIB \
+ _IOWR(PECI_IOC_BASE + PECI_CMD_GET_DIB, 0, \
+ struct peci_get_dib_msg)
+
+#define PECI_IOC_GET_TEMP \
+ _IOWR(PECI_IOC_BASE + PECI_CMD_GET_TEMP, 0, \
+ struct peci_get_temp_msg)
+
+#define PECI_IOC_RD_PKG_CFG \
+ _IOWR(PECI_IOC_BASE + PECI_CMD_RD_PKG_CFG, 0, \
+ struct peci_rd_pkg_cfg_msg)
+
+#define PECI_IOC_WR_PKG_CFG \
+ _IOWR(PECI_IOC_BASE + PECI_CMD_WR_PKG_CFG, 0, \
+ struct peci_wr_pkg_cfg_msg)
+
+#define PECI_IOC_RD_IA_MSR \
+ _IOWR(PECI_IOC_BASE + PECI_CMD_RD_IA_MSR, 0, \
+ struct peci_rd_ia_msr_msg)
+
+#define PECI_IOC_RD_PCI_CFG \
+ _IOWR(PECI_IOC_BASE + PECI_CMD_RD_PCI_CFG, 0, \
+ struct peci_rd_pci_cfg_msg)
+
+#define PECI_IOC_RD_PCI_CFG_LOCAL \
+ _IOWR(PECI_IOC_BASE + PECI_CMD_RD_PCI_CFG_LOCAL, 0, \
+ struct peci_rd_pci_cfg_local_msg)
+
+#define PECI_IOC_WR_PCI_CFG_LOCAL \
+ _IOWR(PECI_IOC_BASE + PECI_CMD_WR_PCI_CFG_LOCAL, 0, \
+ struct peci_wr_pci_cfg_local_msg)
+
+#endif /* __PECI_IOCTL_H */
--
2.7.4
^ permalink raw reply related
* [PATCH linux dev-4.10 2/6] ARM: dts: aspeed: peci: Add Aspeed PECI
From: Jae Hyun Yoo @ 2018-01-09 22:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180109223126.13093-1-jae.hyun.yoo@linux.intel.com>
This commit adds Aspeed PECI node into aspeed-g4 and aspeed-g5.
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
---
arch/arm/boot/dts/aspeed-g4.dtsi | 14 ++++++++++++++
arch/arm/boot/dts/aspeed-g5.dtsi | 14 ++++++++++++++
2 files changed, 28 insertions(+)
diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi
index b82ebef..7ecc7b2 100644
--- a/arch/arm/boot/dts/aspeed-g4.dtsi
+++ b/arch/arm/boot/dts/aspeed-g4.dtsi
@@ -238,6 +238,20 @@
clocks = <&clk_hpll>;
};
+ peci: peci at 1e78b000 {
+ compatible = "aspeed,ast2400-peci";
+ reg = <0x1e78b000 0x60>;
+ interrupt-controller;
+ interrupts = <15>;
+ clocks = <&clk_clkin>;
+ clock-frequency = <24000000>;
+ msg-timing-nego = <1>;
+ addr-timing-nego = <1>;
+ rd-sampling-point = <8>;
+ cmd-timeout-ms = <1000>;
+ status = "disabled";
+ };
+
sgpio: gpio at 0x1e780200 {
#gpio-cells = <2>;
gpio-controller;
diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi
index ba3607c..b4e8d51 100644
--- a/arch/arm/boot/dts/aspeed-g5.dtsi
+++ b/arch/arm/boot/dts/aspeed-g5.dtsi
@@ -289,6 +289,20 @@
clocks = <&clk_hpll>;
};
+ peci: peci at 1e78b000 {
+ compatible = "aspeed,ast2500-peci";
+ reg = <0x1e78b000 0x60>;
+ interrupt-controller;
+ interrupts = <15>;
+ clocks = <&clk_clkin>;
+ clock-frequency = <24000000>;
+ msg-timing-nego = <1>;
+ addr-timing-nego = <1>;
+ rd-sampling-point = <8>;
+ cmd-timeout-ms = <1000>;
+ status = "disabled";
+ };
+
timer: timer at 1e782000 {
compatible = "aspeed,ast2400-timer";
reg = <0x1e782000 0x90>;
--
2.7.4
^ permalink raw reply related
* [PATCH linux dev-4.10 1/6] Documentation: dt-bindings: Add Aspeed PECI
From: Jae Hyun Yoo @ 2018-01-09 22:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180109223126.13093-1-jae.hyun.yoo@linux.intel.com>
This commit adds a dt-bindings document for Aspeed PECI.
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
---
.../devicetree/bindings/misc/aspeed-peci.txt | 55 ++++++++++++++++++++++
1 file changed, 55 insertions(+)
create mode 100644 Documentation/devicetree/bindings/misc/aspeed-peci.txt
diff --git a/Documentation/devicetree/bindings/misc/aspeed-peci.txt b/Documentation/devicetree/bindings/misc/aspeed-peci.txt
new file mode 100644
index 0000000..d277c73
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/aspeed-peci.txt
@@ -0,0 +1,55 @@
+* ASPEED PECI (Platform Environment Control Interface) misc driver.
+
+Hardware Interfaces:
+- This driver implements support for the ASPEED AST2400/2500 PECI which has the
+ following features:
+ - Directly connected to APB bus
+ - Intel PECI 3.1 compliant (PECI 3.0 for AST2400)
+ - Maximum packet length is 256 bytes (Baseline transmission unit)
+ - Support up to 8 CPUs and 2 domains per CPU
+ - Integrate PECI compliant I/O buffers, can connect to PECI bus directly
+ - Transmit buffer 32 bytes and receive buffer 32 bytes
+
+Required properties:
+- compatible: "aspeed,ast2400-peci" or "aspeed,ast2500-peci"
+ - aspeed,ast2400-peci: Aspeed AST2400 family PECI control interface
+ - aspeed,ast2500-peci: Aspeed AST2500 family PECI control interface
+- reg: Should contain PECI registers location and length
+- interrupts: Should contain PECI interrupt
+- clocks: Should contain clock source. = <&clk_clkin>;
+- clock_frequency: Should contain the operation frequency of PECI controller.
+ 187500 ~ 24000000
+
+Optional properties:
+- msg-timing-nego: Message timing negotiation period.
+ This value will determine the period of message timing negotiation to be
+ issued by PECI controller. The unit of the programmed value is four
+ times of PECI clock period.
+ 0 ~ 255 (default: 1)
+- addr-timing-nego: Address timing negotiation period.
+ This value will determine the period of address timing negotiation to be
+ issued by PECI controller. The unit of the programmed value is four
+ times of PECI clock period.
+ 0 ~ 255 (default: 1)
+- rd-sampling-point: Read sampling point selection.
+ The whole period of a bit time will be divided into 16 time frames.
+ This value will determine which time frame this controller will sample
+ PECI signal for data read back. Usually in the middle of a bit time is
+ the best.
+ 0 ~ 15 (default: 8)
+- cmd_timeout_ms: Command timeout in units of ms
+ 1 ~ 60000 (default: 1000)
+
+Example:
+ peci: peci at 1e78b000 {
+ compatible = "aspeed,ast2500-peci";
+ reg = <0x1e78b000 0x60>;
+ interrupt-controller;
+ interrupts = <15>;
+ clocks = <&clk_clkin>;
+ clock-frequency = <24000000>;
+ msg-timing-nego = <1>;
+ addr-timing-nego = <1>;
+ rd-sampling-point = <8>;
+ cmd-timeout-ms = <1000>;
+ };
--
2.7.4
^ permalink raw reply related
* [PATCH linux dev-4.10 0/6] Add support PECI and PECI hwmon drivers
From: Jae Hyun Yoo @ 2018-01-09 22:31 UTC (permalink / raw)
To: linux-arm-kernel
From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
Hello,
This patch set provides support for PECI of AST2400/2500 which can give us PECI
functionalities such as temperature monitoring, platform manageability,
processor diagnostics and failure analysis. Also provides generic peci.h and
peci_ioctl.h headers to provide compatibility to peci drivers that can be
implemented later e.g. Nuvoton's BMC SoC family.
The misc peci driver can be used as a multi-purpose PECI controller driver which
serializes all PECI transactions that coming from user space and from other
kernel modules. This misc peci driver could be replaced with other BMC chipsets'
implementation if the implementation provide compatible 'peci_ioctl'
inter-module call and ioctl scheme defined in peci.h and peci_ioctl.h files.
The hwmon peci driver implements a generic PECI hwmon feature which is running
with a PECI misc driver supports compatible native PECI command suite for
retrieving temperatures of the CPU package, CPU cores and DIMM components.
Please review.
-Jae
Jae Hyun Yoo (6):
Documentation: dt-bindings: Add Aspeed PECI
ARM: dts: aspeed: peci: Add Aspeed PECI
drivers/misc: Add driver for Aspeed PECI and generic PECI headers
Documentation: dt-bindings: Add a generic PECI hwmon
Documentation: hwmon: Add a generic PECI hwmon
drivers/hwmon: Add a driver for a generic PECI hwmon
.../devicetree/bindings/hwmon/peci-hwmon.txt | 33 +
.../devicetree/bindings/misc/aspeed-peci.txt | 55 +
Documentation/hwmon/peci-hwmon | 74 ++
arch/arm/boot/dts/aspeed-g4.dtsi | 14 +
arch/arm/boot/dts/aspeed-g5.dtsi | 14 +
drivers/hwmon/Kconfig | 6 +
drivers/hwmon/Makefile | 1 +
drivers/hwmon/peci-hwmon.c | 953 +++++++++++++++++
drivers/misc/Kconfig | 9 +
drivers/misc/Makefile | 1 +
drivers/misc/aspeed-peci.c | 1130 ++++++++++++++++++++
include/misc/peci.h | 11 +
include/uapi/linux/Kbuild | 1 +
include/uapi/linux/peci_ioctl.h | 270 +++++
14 files changed, 2572 insertions(+)
create mode 100644 Documentation/devicetree/bindings/hwmon/peci-hwmon.txt
create mode 100644 Documentation/devicetree/bindings/misc/aspeed-peci.txt
create mode 100644 Documentation/hwmon/peci-hwmon
create mode 100644 drivers/hwmon/peci-hwmon.c
create mode 100644 drivers/misc/aspeed-peci.c
create mode 100644 include/misc/peci.h
create mode 100644 include/uapi/linux/peci_ioctl.h
--
2.7.4
^ permalink raw reply
* [PATCH 1/7] ARM: imx: add timer stop flag to ARM power off state
From: Stefan Agner @ 2018-01-09 22:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1515506669.12538.31.camel@pengutronix.de>
On 2018-01-09 15:04, Lucas Stach wrote:
> Am Dienstag, den 09.01.2018, 14:37 +0100 schrieb Stefan Agner:
>> On 2018-01-09 11:13, Lucas Stach wrote:
>> > Am Dienstag, den 09.01.2018, 09:25 +0000 schrieb Anson Huang:
>> > >
>> > > Best Regards!
>> > > Anson Huang
>> > >
>> > >
>> > > > -----Original Message-----
>> > > > From: Dong Aisheng [mailto:dongas86 at gmail.com]
>> > > > Sent: 2018-01-09 5:23 PM
>> > > > To: Stefan Agner <stefan@agner.ch>
>> > > > Cc: shawnguo at kernel.org; kernel at pengutronix.de; Fabio Estevam
>> > > > <fabio.estevam@nxp.com>; robh+dt at kernel.org; mark.rutland at arm.c
>> > > > om;
>> > > > linux-arm-kernel at lists.infradead.org; devicetree at vger.kernel.or
>> > > > g;
>> > > > linux-
>> > > > kernel at vger.kernel.org; Anson Huang <anson.huang@nxp.com>; dl-
>> > > > linux-imx
>> > > > <linux-imx@nxp.com>
>> > > > Subject: Re: [PATCH 1/7] ARM: imx: add timer stop flag to ARM
>> > > > power
>> > > > off state
>> > > >
>> > > > On Tue, Jan 02, 2018 at 05:42:17PM +0100, Stefan Agner wrote:
>> > > > > When the CPU is in ARM power off state the ARM architected
>> > > > > timers
>> > > > > are
>> > > > > stopped. The flag is already present in the higher power WAIT
>> > > > > mode.
>> > > > >
>> > > > > This allows to use the ARM generic timer on i.MX 6UL/6ULL
>> > > > > SoC.
>> > > > > Without the flag the kernel freezes when the timer enters the
>> > > > > first
>> > > > > time ARM power off mode.
>> > > > >
>> > > > > Cc: Anson Huang <anson.huang@nxp.com>
>> > > > > Signed-off-by: Stefan Agner <stefan@agner.ch>
>> > > >
>> > > > It seems ok at my side.
>> > > > Did you meet the real issue? If yes, how to reproduce?
>> > > >
>> > > > Both mx6sx and mx6ul are using GPT which do not need that flag,
>> > > > suppose we
>> > > > should remove it, right?
>> > > > Anson can help confirm it.
>> > >
>> > > For UP system like i.MX6SX, we do NOT enable "cortex-a9-twd-
>> > > timer",
>> > > so local
>> > > timer is NOT used, GPT is used instead, GPT's clock is NOT
>> > > disabled
>> > > when cpuidle,
>> > > so I think we should remove all these Timer stop flag for 6SX
>> > > CPUIDLE.
>> >
>> > It's correct to set the flag even on UP systems, as the flag means
>> > the
>> > CPU _local_ timer is stopped in this sleep mode. Also there are
>> > systems
>> > out there which are using the TWD on UP, as it operates at a higher
>> > frequency leading to better wakeup granularity.
>>
>> Documentation/devicetree/bindings/arm/twd.txt states that TWD
>> provides
>> "per-cpu local timer". But as far as I can see TWD still uses SPI
>> interrupts, routed through GIC, so is this the differentiation?
>
> Maybe what I wrote wasn't entirely clear. I completely agree with this
> patch.
>
> The TWD on Cortex-A9 is a CPU local timer, same as the architected
> timer in later cores. It doesn't provide all the benefits of the
> architected timer (the clock frequency varies with CPU core clock and
> it's not virt capable), but some systems still prefer it over the i.MX
> GPT, as it provides much better wakeup granularity.
>
> So annotating the CPU idle states with the timer stop flag is the right
> thing to do. This flag has nothing to with the usage of GPT or TWD on a
> specific system.
Can I take that as an Acked-by?
--
Stefan
^ permalink raw reply
* [PATCH v2 3/7] PCI: aardvark: set host and device to the same MAX payload size
From: Bjorn Helgaas @ 2018-01-09 22:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180109163918.5e46c1a1@windsurf.lan>
[+cc Lorenzo, who maintains this area now]
On Tue, Jan 09, 2018 at 04:39:18PM +0100, Thomas Petazzoni wrote:
> Hello Bjorn,
>
> On Thu, 5 Oct 2017 12:31:02 -0500, Bjorn Helgaas wrote:
> > On Thu, Sep 28, 2017 at 02:58:34PM +0200, Thomas Petazzoni wrote:
> > > From: Victor Gu <xigu@marvell.com>
> > >
> > > Since the Aardvark does not implement a PCIe root bus,
> >
> > What exactly do you mean by "does not implement a PCIe root bus"? I
> > assume there is still a hierarchy of PCI buses, and I assume the
> > hierarchy has a top-most ("root") bus.
> >
> > Maybe there's no Root Port? There are other systems that don't have
> > Root Ports, and we've made changes to accommodate that, e.g.,
> >
> > http://git.kernel.org/cgit/linux/kernel/git/helgaas/pci.git/commit/?id=1b8a6079015f
>
> I'm trying to get back (finally) to this topic. Unfortunately, your
> branch has been rebased, and this commit no longer exists. Do you have
> an updated pointer about what you suggest to use for systems that don't
> have Root Ports ?
Sorry, about that; here's the upstream commit, FWIW:
http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=ee8bdfb6568d
If the OS sees no Root Port (I haven't seen the full lspci or kernel
enumeration log, so I don't know what the topology actually is), I
assume you probably have some Endpoints that have valid Link
Capabilities, Control, and Status registers. Those refer to the
downstream end of the Link, and the Root Port would normally have
corresponding registers that refer to the upstream end.
The lack of the Root Port means we can't do any management of those
top-level Links, so no ASPM, no MPS, no link width/speed management,
etc.
I see that advk_pcie_probe() calls pcie_bus_configure_settings() like
all other drivers, and ideally we would try to make that work just
like it does on other platforms. The code is:
pci_scan_root_bus_bridge(bridge);
bus = bridge->bus;
list_for_each_entry(child, &bus->children, node)
pcie_bus_configure_settings(child);
This MPS setting is all strictly in the PCIe domain (it's not in the
Aardvark domain and shouldn't have any Aardvark dependencies), so I
would expect the core code to just work, modulo some possible
confusion if it expects to find a Root Port but doesn't.
Can you collect "lspci -vv" output and details about what currently
goes wrong? Then we'd have something more concrete to talk about.
Bjorn
^ permalink raw reply
* [PATCH net-next 1/2 v10] net: ethernet: Add DT bindings for the Gemini ethernet
From: Linus Walleij @ 2018-01-09 22:10 UTC (permalink / raw)
To: linux-arm-kernel
This adds the device tree bindings for the Gemini ethernet
controller. It is pretty straight-forward, using standard
bindings and modelling the two child ports as child devices
under the parent ethernet controller device.
Cc: devicetree at vger.kernel.org
Cc: Tobias Waldvogel <tobias.waldvogel@gmail.com>
Cc: Micha? Miros?aw <mirq-linux@rere.qmqm.pl>
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
ChangeLog v9->v10:
- Resend with the driver.
ChangeLog v8->v9:
- Collect Rob's ACK.
ChangeLog v7->v8:
- Use ethernet-port at 0 and ethernet-port at 1 with unit names
and following OF graph requirements.
---
.../bindings/net/cortina,gemini-ethernet.txt | 92 ++++++++++++++++++++++
1 file changed, 92 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/cortina,gemini-ethernet.txt
diff --git a/Documentation/devicetree/bindings/net/cortina,gemini-ethernet.txt b/Documentation/devicetree/bindings/net/cortina,gemini-ethernet.txt
new file mode 100644
index 000000000000..6c559981d110
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/cortina,gemini-ethernet.txt
@@ -0,0 +1,92 @@
+Cortina Systems Gemini Ethernet Controller
+==========================================
+
+This ethernet controller is found in the Gemini SoC family:
+StorLink SL3512 and SL3516, also known as Cortina Systems
+CS3512 and CS3516.
+
+Required properties:
+- compatible: must be "cortina,gemini-ethernet"
+- reg: must contain the global registers and the V-bit and A-bit
+ memory areas, in total three register sets.
+- syscon: a phandle to the system controller
+- #address-cells: must be specified, must be <1>
+- #size-cells: must be specified, must be <1>
+- ranges: should be state like this giving a 1:1 address translation
+ for the subnodes
+
+The subnodes represents the two ethernet ports in this device.
+They are not independent of each other since they share resources
+in the parent node, and are thus children.
+
+Required subnodes:
+- port0: contains the resources for ethernet port 0
+- port1: contains the resources for ethernet port 1
+
+Required subnode properties:
+- compatible: must be "cortina,gemini-ethernet-port"
+- reg: must contain two register areas: the DMA/TOE memory and
+ the GMAC memory area of the port
+- interrupts: should contain the interrupt line of the port.
+ this is nominally a level interrupt active high.
+- resets: this must provide an SoC-integrated reset line for
+ the port.
+- clocks: this should contain a handle to the PCLK clock for
+ clocking the silicon in this port
+- clock-names: must be "PCLK"
+
+Optional subnode properties:
+- phy-mode: see ethernet.txt
+- phy-handle: see ethernet.txt
+
+Example:
+
+mdio-bus {
+ (...)
+ phy0: ethernet-phy at 1 {
+ reg = <1>;
+ device_type = "ethernet-phy";
+ };
+ phy1: ethernet-phy at 3 {
+ reg = <3>;
+ device_type = "ethernet-phy";
+ };
+};
+
+
+ethernet at 60000000 {
+ compatible = "cortina,gemini-ethernet";
+ reg = <0x60000000 0x4000>, /* Global registers, queue */
+ <0x60004000 0x2000>, /* V-bit */
+ <0x60006000 0x2000>; /* A-bit */
+ syscon = <&syscon>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ gmac0: ethernet-port at 0 {
+ compatible = "cortina,gemini-ethernet-port";
+ reg = <0x60008000 0x2000>, /* Port 0 DMA/TOE */
+ <0x6000a000 0x2000>; /* Port 0 GMAC */
+ interrupt-parent = <&intcon>;
+ interrupts = <1 IRQ_TYPE_LEVEL_HIGH>;
+ resets = <&syscon GEMINI_RESET_GMAC0>;
+ clocks = <&syscon GEMINI_CLK_GATE_GMAC0>;
+ clock-names = "PCLK";
+ phy-mode = "rgmii";
+ phy-handle = <&phy0>;
+ };
+
+ gmac1: ethernet-port at 1 {
+ compatible = "cortina,gemini-ethernet-port";
+ reg = <0x6000c000 0x2000>, /* Port 1 DMA/TOE */
+ <0x6000e000 0x2000>; /* Port 1 GMAC */
+ interrupt-parent = <&intcon>;
+ interrupts = <2 IRQ_TYPE_LEVEL_HIGH>;
+ resets = <&syscon GEMINI_RESET_GMAC1>;
+ clocks = <&syscon GEMINI_CLK_GATE_GMAC1>;
+ clock-names = "PCLK";
+ phy-mode = "rgmii";
+ phy-handle = <&phy1>;
+ };
+};
--
2.14.3
^ permalink raw reply related
* EDAC driver for ARMv8 L1/L2 cache
From: Borislav Petkov @ 2018-01-09 22:00 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <VI1PR04MB20788AC2E71A3CFFC37B85BB9A100@VI1PR04MB2078.eurprd04.prod.outlook.com>
On Tue, Jan 09, 2018 at 09:51:32PM +0000, York Sun wrote:
> On 01/09/2018 01:43 PM, Borislav Petkov wrote:
> > Adding some more people to CC.
> >
> > On Tue, Jan 09, 2018 at 08:48:43PM +0000, York Sun wrote:
> >> Borislav,
> >>
> >> Are you aware of any existing (or in development) EDAC driver for ARMv8
> >> L1/L2 cache? I am thinking to write one if not available yet.
> >
> > no I'm not but I see two EDAC drivers for ARM64: thunderx and xgene.
> >
> > Please synchronize with their authors and ARM people what would be the
> > best thing to do and try extracting shared functionality from them
> > into a common compilation unit instead of duplicating it. I don't want
> > separate drivers per functional unit.
> >
>
> Thanks for the pointer. Thunderx and xgene's drivers have different
> implementation on the hardware. I found one patch closer to what I
> expect, https://patchwork.kernel.org/patch/7513231/. I don't see
> activities after 2015. I will reach out to the author.
He's probably busy with SME/SEV right now.
+ Marc.
--
Regards/Gruss,
Boris.
Good mailing practices for 400: avoid top-posting and trim the reply.
^ permalink raw reply
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