* [PATCH 1/2] dt-bindings: rtc: Add Spreadtrum SC27xx RTC documentation
From: Baolin Wang @ 2017-11-07 11:34 UTC (permalink / raw)
To: a.zummo, alexandre.belloni, robh+dt, mark.rutland
Cc: devicetree, linux-kernel, linux-rtc, broonie, baolin.wang,
baolin.wang
This patch adds the binding documentation for Spreadtrum SC27xx series
RTC device.
Signed-off-by: Baolin Wang <baolin.wang@spreadtrum.com>
---
.../devicetree/bindings/rtc/sprd,sc27xx-rtc.txt | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
create mode 100644 Documentation/devicetree/bindings/rtc/sprd,sc27xx-rtc.txt
diff --git a/Documentation/devicetree/bindings/rtc/sprd,sc27xx-rtc.txt b/Documentation/devicetree/bindings/rtc/sprd,sc27xx-rtc.txt
new file mode 100644
index 0000000..971d3a2
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/sprd,sc27xx-rtc.txt
@@ -0,0 +1,16 @@
+Spreadtrum SC27xx Real Time Clock
+
+Required properties:
+- compatible: should be "sprd,sc27xx-rtc".
+- reg: address offset of rtc register.
+- interrupt-parent: phandle for the interrupt controller.
+- interrupts: rtc alarm interrupt.
+
+Example:
+
+ rtc: rtc@280 {
+ compatible = "sprd,sc27xx-rtc";
+ reg = <0x280>;
+ interrupt-parent = <&pmic>;
+ interrupts = <2 IRQ_TYPE_LEVEL_HIGH>;
+ };
--
1.7.9.5
^ permalink raw reply related
* Re: [PATCH V3] rtc: add support for NXP PCF85363 real-time clock
From: Eric Nelson @ 2017-11-06 14:32 UTC (permalink / raw)
To: Alexandre Belloni
Cc: linux-rtc, robh, a.zummo, mark.rutland, devicetree,
otavio.salvador
In-Reply-To: <20171104160836.bah5su2ld5ckycr3@piout.net>
On 11/04/2017 09:08 AM, Alexandre Belloni wrote:
> On 01/11/2017 at 08:01:20 -0700, Eric Nelson wrote:
<snip>
>>
>> V3 adds interrupts as an optional property in device tree bindings
>>
>> drivers/rtc/Kconfig | 13 ++
>> drivers/rtc/Makefile | 1 +
>> drivers/rtc/rtc-pcf85363.c | 221 +++++++++++++++++++++
>> 4 files changed, 252 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/rtc/pcf85363.txt
>> create mode 100644 drivers/rtc/rtc-pcf85363.c
>>
>
> I've tested it (thanks to Fabio, I received one a few weeks ago)
>
> Applied with the following change:
>
> --- a/drivers/rtc/rtc-pcf85363.c
> +++ b/drivers/rtc/rtc-pcf85363.c
> @@ -194,7 +194,6 @@ static int pcf85363_probe(struct i2c_client *client,
> pcf85363->nvmem_cfg.reg_write = pcf85363_nvram_write;
> pcf85363->nvmem_cfg.priv = pcf85363;
> pcf85363->rtc->nvmem_config = &pcf85363->nvmem_cfg;
> - pcf85363->rtc->nvram_old_abi = true;
> pcf85363->rtc->ops = &rtc_ops;
>
> return rtc_register_device(pcf85363->rtc);
>
> nvram_old_abi must not be used for new drivers and will generate warning
> when the (undocumented) ABI is used anyway.
>
Thanks Alexandre (and Fabio).
^ permalink raw reply
* Re: [PATCH] rtc: omap: Support scratch registers
From: Sekhar Nori @ 2017-11-06 7:02 UTC (permalink / raw)
To: Keerthy, Alexandre Belloni, linux-rtc; +Cc: linux-kernel, Linux OMAP List
In-Reply-To: <4741d4c7-d6f7-ecdb-1eed-1293175f38d9@ti.com>
On Monday 06 November 2017 12:29 PM, Keerthy wrote:
>
>
> On Monday 06 November 2017 12:25 PM, Sekhar Nori wrote:
>> + linux omap list
>>
>> On Tuesday 31 October 2017 09:57 PM, Alexandre Belloni wrote:
>>> Register an nvmem device to expose the 3 scratch registers (total of 12
>>> bytes) to both userspace and kernel space.
>>>
>>> Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
>>
>> Looks good to me.
>>
>> Reviewed-by: Sekhar Nori <nsekhar@ti.com>
>>
>> Curious on what you are using these registers for.
>
> This is in response to this:
> https://patchwork.kernel.org/patch/9684955/
Ah, okay. Makes sense then :)
Regards,
Sekhar
^ permalink raw reply
* Re: [PATCH] rtc: omap: Support scratch registers
From: Keerthy @ 2017-11-06 6:59 UTC (permalink / raw)
To: Sekhar Nori, Alexandre Belloni, linux-rtc; +Cc: linux-kernel, Linux OMAP List
In-Reply-To: <600fd106-9f79-0f5a-a2fd-e6b067785c17@ti.com>
On Monday 06 November 2017 12:25 PM, Sekhar Nori wrote:
> + linux omap list
>
> On Tuesday 31 October 2017 09:57 PM, Alexandre Belloni wrote:
>> Register an nvmem device to expose the 3 scratch registers (total of 12
>> bytes) to both userspace and kernel space.
>>
>> Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
>
> Looks good to me.
>
> Reviewed-by: Sekhar Nori <nsekhar@ti.com>
>
> Curious on what you are using these registers for.
This is in response to this:
https://patchwork.kernel.org/patch/9684955/
>
> Thanks,
> Sekhar
>
>> ---
>> drivers/rtc/rtc-omap.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 45 insertions(+)
>>
>> diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c
>> index d56d937966dc..1d666ac9ef70 100644
>> --- a/drivers/rtc/rtc-omap.c
>> +++ b/drivers/rtc/rtc-omap.c
>> @@ -70,6 +70,10 @@
>> #define OMAP_RTC_COMP_MSB_REG 0x50
>> #define OMAP_RTC_OSC_REG 0x54
>>
>> +#define OMAP_RTC_SCRATCH0_REG 0x60
>> +#define OMAP_RTC_SCRATCH1_REG 0x64
>> +#define OMAP_RTC_SCRATCH2_REG 0x68
>> +
>> #define OMAP_RTC_KICK0_REG 0x6c
>> #define OMAP_RTC_KICK1_REG 0x70
>>
>> @@ -667,6 +671,45 @@ static struct pinctrl_desc rtc_pinctrl_desc = {
>> .owner = THIS_MODULE,
>> };
>>
>> +static int omap_rtc_scratch_read(void *priv, unsigned int offset, void *_val,
>> + size_t bytes)
>> +{
>> + struct omap_rtc *rtc = priv;
>> + u32 *val = _val;
>> + int i;
>> +
>> + for (i = 0; i < bytes / 4; i++)
>> + val[i] = rtc_readl(rtc,
>> + OMAP_RTC_SCRATCH0_REG + offset + (i * 4));
>> +
>> + return 0;
>> +}
>> +
>> +static int omap_rtc_scratch_write(void *priv, unsigned int offset, void *_val,
>> + size_t bytes)
>> +{
>> + struct omap_rtc *rtc = priv;
>> + u32 *val = _val;
>> + int i;
>> +
>> + rtc->type->unlock(rtc);
>> + for (i = 0; i < bytes / 4; i++)
>> + rtc_writel(rtc,
>> + OMAP_RTC_SCRATCH0_REG + offset + (i * 4), val[i]);
>> + rtc->type->lock(rtc);
>> +
>> + return 0;
>> +}
>> +
>> +static struct nvmem_config omap_rtc_nvmem_config = {
>> + .name = "omap_rtc_scratch",
>> + .word_size = 4,
>> + .stride = 4,
>> + .size = OMAP_RTC_KICK0_REG - OMAP_RTC_SCRATCH0_REG,
>> + .reg_read = omap_rtc_scratch_read,
>> + .reg_write = omap_rtc_scratch_write,
>> +};
>> +
>> static int omap_rtc_probe(struct platform_device *pdev)
>> {
>> struct omap_rtc *rtc;
>> @@ -804,6 +847,8 @@ static int omap_rtc_probe(struct platform_device *pdev)
>> }
>>
>> rtc->rtc->ops = &omap_rtc_ops;
>> + omap_rtc_nvmem_config.priv = rtc;
>> + rtc->rtc->nvmem_config = &omap_rtc_nvmem_config;
>>
>> /* handle periodic and alarm irqs */
>> ret = devm_request_irq(&pdev->dev, rtc->irq_timer, rtc_irq, 0,
>> --
>> 2.15.0.rc2
>>
>
^ permalink raw reply
* Re: [PATCH] rtc: omap: Support scratch registers
From: Sekhar Nori @ 2017-11-06 6:55 UTC (permalink / raw)
To: Alexandre Belloni, linux-rtc; +Cc: Keerthy, linux-kernel, Linux OMAP List
In-Reply-To: <20171031162731.27019-1-alexandre.belloni@free-electrons.com>
+ linux omap list
On Tuesday 31 October 2017 09:57 PM, Alexandre Belloni wrote:
> Register an nvmem device to expose the 3 scratch registers (total of 12
> bytes) to both userspace and kernel space.
>
> Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
Looks good to me.
Reviewed-by: Sekhar Nori <nsekhar@ti.com>
Curious on what you are using these registers for.
Thanks,
Sekhar
> ---
> drivers/rtc/rtc-omap.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 45 insertions(+)
>
> diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c
> index d56d937966dc..1d666ac9ef70 100644
> --- a/drivers/rtc/rtc-omap.c
> +++ b/drivers/rtc/rtc-omap.c
> @@ -70,6 +70,10 @@
> #define OMAP_RTC_COMP_MSB_REG 0x50
> #define OMAP_RTC_OSC_REG 0x54
>
> +#define OMAP_RTC_SCRATCH0_REG 0x60
> +#define OMAP_RTC_SCRATCH1_REG 0x64
> +#define OMAP_RTC_SCRATCH2_REG 0x68
> +
> #define OMAP_RTC_KICK0_REG 0x6c
> #define OMAP_RTC_KICK1_REG 0x70
>
> @@ -667,6 +671,45 @@ static struct pinctrl_desc rtc_pinctrl_desc = {
> .owner = THIS_MODULE,
> };
>
> +static int omap_rtc_scratch_read(void *priv, unsigned int offset, void *_val,
> + size_t bytes)
> +{
> + struct omap_rtc *rtc = priv;
> + u32 *val = _val;
> + int i;
> +
> + for (i = 0; i < bytes / 4; i++)
> + val[i] = rtc_readl(rtc,
> + OMAP_RTC_SCRATCH0_REG + offset + (i * 4));
> +
> + return 0;
> +}
> +
> +static int omap_rtc_scratch_write(void *priv, unsigned int offset, void *_val,
> + size_t bytes)
> +{
> + struct omap_rtc *rtc = priv;
> + u32 *val = _val;
> + int i;
> +
> + rtc->type->unlock(rtc);
> + for (i = 0; i < bytes / 4; i++)
> + rtc_writel(rtc,
> + OMAP_RTC_SCRATCH0_REG + offset + (i * 4), val[i]);
> + rtc->type->lock(rtc);
> +
> + return 0;
> +}
> +
> +static struct nvmem_config omap_rtc_nvmem_config = {
> + .name = "omap_rtc_scratch",
> + .word_size = 4,
> + .stride = 4,
> + .size = OMAP_RTC_KICK0_REG - OMAP_RTC_SCRATCH0_REG,
> + .reg_read = omap_rtc_scratch_read,
> + .reg_write = omap_rtc_scratch_write,
> +};
> +
> static int omap_rtc_probe(struct platform_device *pdev)
> {
> struct omap_rtc *rtc;
> @@ -804,6 +847,8 @@ static int omap_rtc_probe(struct platform_device *pdev)
> }
>
> rtc->rtc->ops = &omap_rtc_ops;
> + omap_rtc_nvmem_config.priv = rtc;
> + rtc->rtc->nvmem_config = &omap_rtc_nvmem_config;
>
> /* handle periodic and alarm irqs */
> ret = devm_request_irq(&pdev->dev, rtc->irq_timer, rtc_irq, 0,
> --
> 2.15.0.rc2
>
^ permalink raw reply
* Re: [PATCH V3] rtc: add support for NXP PCF85363 real-time clock
From: Alexandre Belloni @ 2017-11-04 16:08 UTC (permalink / raw)
To: Eric Nelson
Cc: linux-rtc, robh, a.zummo, mark.rutland, devicetree,
otavio.salvador
In-Reply-To: <1509548480-29472-1-git-send-email-eric@nelint.com>
Hi,
On 01/11/2017 at 08:01:20 -0700, Eric Nelson wrote:
> Note that alarms are not currently implemented.
>
> 64 bytes of nvmem is supported and exposed in
> sysfs (# is the instance number, starting with 0):
>
> /sys/bus/nvmem/devices/pcf85363-#/nvmem
>
> Signed-off-by: Eric Nelson <eric@nelint.com>
> Reviewed-by: Fabio Estevam <fabio.estevam@nxp.com>
> ---
> V2 addresses a couple of issues highlighted by Fabio Estevam
> 1. Kconfig updated to select REGMAP_I2C
> 2. Switch to of_device_id from i2c_device_id for driver matching
>
> V3 adds interrupts as an optional property in device tree bindings
>
> drivers/rtc/Kconfig | 13 ++
> drivers/rtc/Makefile | 1 +
> drivers/rtc/rtc-pcf85363.c | 221 +++++++++++++++++++++
> 4 files changed, 252 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/rtc/pcf85363.txt
> create mode 100644 drivers/rtc/rtc-pcf85363.c
>
I've tested it (thanks to Fabio, I received one a few weeks ago)
Applied with the following change:
--- a/drivers/rtc/rtc-pcf85363.c
+++ b/drivers/rtc/rtc-pcf85363.c
@@ -194,7 +194,6 @@ static int pcf85363_probe(struct i2c_client *client,
pcf85363->nvmem_cfg.reg_write = pcf85363_nvram_write;
pcf85363->nvmem_cfg.priv = pcf85363;
pcf85363->rtc->nvmem_config = &pcf85363->nvmem_cfg;
- pcf85363->rtc->nvram_old_abi = true;
pcf85363->rtc->ops = &rtc_ops;
return rtc_register_device(pcf85363->rtc);
nvram_old_abi must not be used for new drivers and will generate warning
when the (undocumented) ABI is used anyway.
--
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
^ permalink raw reply
* Re: Introduce clock precision to help time travelers was Re: Extreme time jitter with suspend/resume cycles
From: Alexandre Belloni @ 2017-11-04 15:34 UTC (permalink / raw)
To: Pavel Machek
Cc: Alan Cox, Gabriel Beddingfield, LKML, Stephen Boyd,
Thomas Gleixner, John Stultz, Alessandro Zummo, linux-rtc,
Guy Erb, hharte
In-Reply-To: <20171018215638.GA7487@amd>
On 18/10/2017 at 23:56:38 +0200, Pavel Machek wrote:
> > Some RTCs will tell you when they lost time/time accuracy and this
> > should be properly reported by the driver. If not, this has to be
> > implemented.
>
> How is it reported to the userspace?
>
Userspace will get -EINVAL when using the RTC_RD_TIME ioctl. The kernel
also get the same error when reading the time from the RTC.
> > For anything else, it is probably the job of userspace to try to be
> > clever.
>
> Userspace would be fine with me, but as far as I can tell, there's no
> good way to do it in userspace.
>
> My proposal would be: kernel keeps accuracy for timeofday.
>
> If RTC says time is bad, accuracy is set to ~0.
>
> settimeofday sets accuracy to 0 (completely accurate).
>
> new_settimeofday gets new argument, accuracy.
>
> new_gettimofday returns accuracy, too.
>
> Does that sound sane? I'm not sure what other interfaces need to be extended.
>
> Best regards,
> Pavel
>
> --
> (english) http://www.livejournal.com/~pavelmachek
> (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
--
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
^ permalink raw reply
* [PATCH 3/3] rtc: rx8010: Fix for incorrect return value
From: Akshay Bhat @ 2017-11-03 17:32 UTC (permalink / raw)
To: a.zummo, alexandre.belloni; +Cc: linux-rtc, linux-kernel, Akshay Bhat
In-Reply-To: <1509730361-23905-1-git-send-email-akshay.bhat@timesys.com>
The err variable is not being reset after a successful read. Explicitly
reset err variable to account for all return paths.
Reported-by: Jens-Peter Oswald <oswald@lre.de>
Signed-off-by: Akshay Bhat <akshay.bhat@timesys.com>
---
drivers/rtc/rtc-rx8010.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/rtc/rtc-rx8010.c b/drivers/rtc/rtc-rx8010.c
index 2e06e5f..1ce2078 100644
--- a/drivers/rtc/rtc-rx8010.c
+++ b/drivers/rtc/rtc-rx8010.c
@@ -223,6 +223,7 @@ static int rx8010_init_client(struct i2c_client *client)
2, ctrl);
if (err != 2)
return err < 0 ? err : -EIO;
+ err = 0;
if (ctrl[0] & RX8010_FLAG_VLF)
dev_warn(&client->dev, "Frequency stop was detected\n");
@@ -261,6 +262,7 @@ static int rx8010_read_alarm(struct device *dev, struct rtc_wkalrm *t)
err = i2c_smbus_read_i2c_block_data(client, RX8010_ALMIN, 3, alarmvals);
if (err != 3)
return err < 0 ? err : -EIO;
+ err = 0;
flagreg = i2c_smbus_read_byte_data(client, RX8010_FLAG);
if (flagreg < 0)
--
2.7.4
^ permalink raw reply related
* [PATCH 2/3] rtc: rx8010: Specify correct address for RX8010_RESV31
From: Akshay Bhat @ 2017-11-03 17:32 UTC (permalink / raw)
To: a.zummo, alexandre.belloni; +Cc: linux-rtc, linux-kernel, Akshay Bhat
In-Reply-To: <1509730361-23905-1-git-send-email-akshay.bhat@timesys.com>
Define for reserved register 31 had the incorrect address. Specify
the correct address.
Reported-by: Jens-Peter Oswald <oswald@lre.de>
Signed-off-by: Akshay Bhat <akshay.bhat@timesys.com>
---
drivers/rtc/rtc-rx8010.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/rtc/rtc-rx8010.c b/drivers/rtc/rtc-rx8010.c
index f948f75..2e06e5f 100644
--- a/drivers/rtc/rtc-rx8010.c
+++ b/drivers/rtc/rtc-rx8010.c
@@ -35,7 +35,7 @@
#define RX8010_CTRL 0x1F
/* 0x20 to 0x2F are user registers */
#define RX8010_RESV30 0x30
-#define RX8010_RESV31 0x32
+#define RX8010_RESV31 0x31
#define RX8010_IRQ 0x32
#define RX8010_EXT_WADA BIT(3)
--
2.7.4
^ permalink raw reply related
* [PATCH 1/3] rtc: rx8010: Remove duplicate define
From: Akshay Bhat @ 2017-11-03 17:32 UTC (permalink / raw)
To: a.zummo, alexandre.belloni; +Cc: linux-rtc, linux-kernel, Akshay Bhat
Remove duplicate define for RX8010_YEAR
Signed-off-by: Akshay Bhat <akshay.bhat@timesys.com>
---
drivers/rtc/rtc-rx8010.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/drivers/rtc/rtc-rx8010.c b/drivers/rtc/rtc-rx8010.c
index 1ed3403..f948f75 100644
--- a/drivers/rtc/rtc-rx8010.c
+++ b/drivers/rtc/rtc-rx8010.c
@@ -24,7 +24,6 @@
#define RX8010_MDAY 0x14
#define RX8010_MONTH 0x15
#define RX8010_YEAR 0x16
-#define RX8010_YEAR 0x16
#define RX8010_RESV17 0x17
#define RX8010_ALMIN 0x18
#define RX8010_ALHOUR 0x19
--
2.7.4
^ permalink raw reply related
* Re: [RFC v8 1/7] platform/x86: intel_punit_ipc: Fix resource ioremap warning
From: Kuppuswamy, Sathyanarayanan @ 2017-11-03 15:31 UTC (permalink / raw)
To: Andy Shevchenko, Kuppuswamy Sathyanarayanan
Cc: Alessandro Zummo, x86@kernel.org, Wim Van Sebroeck, Ingo Molnar,
Alexandre Belloni, Zha Qipeng, H. Peter Anvin,
dvhart@infradead.org, Thomas Gleixner, Lee Jones, Andy Shevchenko,
Souvik Kumar Chakravarty, linux-rtc, linux-watchdog,
linux-kernel@vger.kernel.org, Platform Driver, Andy Shevchenko
In-Reply-To: <CAHp75VffZCTgsbgm3RU-vAL4Xf1A60NtDaFwaLQRp5UqtztfuQ@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 567 bytes --]
Hi Andy,
On 11/3/2017 5:13 AM, Andy Shevchenko wrote:
> I think I already told that this one had been pushed to my review and
> testing queue, thanks!
I thought you specifically asked me to include this patch in next set.
Following is your previous comment. Patch 3 in your reply points to this
patch.
>> I have applied this patch with some modifications to my review-andy branch.
>> Please, take it from there (and probably patch 3) and resend new
>> version of the series based on our testing branch.
--
-
Sathyanarayan Kuppuswamy
Linux kernel developer
[-- Attachment #2: Type: text/html, Size: 3787 bytes --]
^ permalink raw reply
* Re: [RFC v8 1/7] platform/x86: intel_punit_ipc: Fix resource ioremap warning
From: Andy Shevchenko @ 2017-11-03 12:13 UTC (permalink / raw)
To: Kuppuswamy Sathyanarayanan
Cc: Alessandro Zummo, x86@kernel.org, Wim Van Sebroeck, Ingo Molnar,
Alexandre Belloni, Zha Qipeng, H. Peter Anvin,
dvhart@infradead.org, Thomas Gleixner, Lee Jones, Andy Shevchenko,
Souvik Kumar Chakravarty, linux-rtc, linux-watchdog,
linux-kernel@vger.kernel.org, Platform Driver,
Sathyanarayanan Kuppuswamy Natarajan, Andy Shevchenko
In-Reply-To: <0ed06c9aabd0f6d707f3ab8024282a764ffe0318.1509268570.git.sathyanarayanan.kuppuswamy@linux.intel.com>
On Sun, Oct 29, 2017 at 11:49 AM,
<sathyanarayanan.kuppuswamy@linux.intel.com> wrote:
> From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
>
> For PUNIT device, ISPDRIVER_IPC and GTDDRIVER_IPC resources are not
> mandatory. So when PMC IPC driver creates a PUNIT device, if these
> resources are not available then it creates dummy resource entries for
> these missing resources. But during PUNIT device probe, doing ioremap on
> these dummy resources generates following warning messages.
>
> intel_punit_ipc: can't request region for resource [mem 0x00000000]
> intel_punit_ipc: can't request region for resource [mem 0x00000000]
> intel_punit_ipc: can't request region for resource [mem 0x00000000]
> intel_punit_ipc: can't request region for resource [mem 0x00000000]
>
> This patch fixes this issue by adding extra check for resource size
> before performing ioremap operation.
I think I already told that this one had been pushed to my review and
testing queue, thanks!
>
> Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
> Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> ---
> drivers/platform/x86/intel_punit_ipc.c | 8 ++++----
> 1 file changed, 4 insertions(+), 4 deletions(-)
>
> Changes since v7:
> * None
>
> Changes since v6:
> * None
>
> Changes since v5:
> * None
>
> Changes since v4:
> * None
>
> diff --git a/drivers/platform/x86/intel_punit_ipc.c b/drivers/platform/x86/intel_punit_ipc.c
> index a47a41f..b5b8901 100644
> --- a/drivers/platform/x86/intel_punit_ipc.c
> +++ b/drivers/platform/x86/intel_punit_ipc.c
> @@ -252,28 +252,28 @@ static int intel_punit_get_bars(struct platform_device *pdev)
> * - GTDRIVER_IPC BASE_IFACE
> */
> res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
> - if (res) {
> + if (res && resource_size(res) > 1) {
> addr = devm_ioremap_resource(&pdev->dev, res);
> if (!IS_ERR(addr))
> punit_ipcdev->base[ISPDRIVER_IPC][BASE_DATA] = addr;
> }
>
> res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
> - if (res) {
> + if (res && resource_size(res) > 1) {
> addr = devm_ioremap_resource(&pdev->dev, res);
> if (!IS_ERR(addr))
> punit_ipcdev->base[ISPDRIVER_IPC][BASE_IFACE] = addr;
> }
>
> res = platform_get_resource(pdev, IORESOURCE_MEM, 4);
> - if (res) {
> + if (res && resource_size(res) > 1) {
> addr = devm_ioremap_resource(&pdev->dev, res);
> if (!IS_ERR(addr))
> punit_ipcdev->base[GTDRIVER_IPC][BASE_DATA] = addr;
> }
>
> res = platform_get_resource(pdev, IORESOURCE_MEM, 5);
> - if (res) {
> + if (res && resource_size(res) > 1) {
> addr = devm_ioremap_resource(&pdev->dev, res);
> if (!IS_ERR(addr))
> punit_ipcdev->base[GTDRIVER_IPC][BASE_IFACE] = addr;
> --
> 2.7.4
>
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* [PATCH v1 1/5] rtc: m41t80: m41t80_sqw_set_rate should return 0 on success
From: Troy Kisky @ 2017-11-03 1:58 UTC (permalink / raw)
To: a.zummo, alexandre.belloni; +Cc: gary.bisson, linux-rtc, Troy Kisky
Previously it was returning -EINVAL upon success.
Signed-off-by: Troy Kisky <troy.kisky@boundarydevices.com>
---
drivers/rtc/rtc-m41t80.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c
index f4c070ea8384..8f5843169dc2 100644
--- a/drivers/rtc/rtc-m41t80.c
+++ b/drivers/rtc/rtc-m41t80.c
@@ -510,10 +510,7 @@ static int m41t80_sqw_set_rate(struct clk_hw *hw, unsigned long rate,
reg = (reg & 0x0f) | (val << 4);
ret = i2c_smbus_write_byte_data(client, reg_sqw, reg);
- if (ret < 0)
- return ret;
-
- return -EINVAL;
+ return ret;
}
static int m41t80_sqw_control(struct clk_hw *hw, bool enable)
--
2.11.0
^ permalink raw reply related
* [PATCH v1 4/5] rtc: m41t80: avoid i2c read in m41t80_sqw_is_prepared
From: Troy Kisky @ 2017-11-03 1:58 UTC (permalink / raw)
To: a.zummo, alexandre.belloni; +Cc: gary.bisson, linux-rtc, Troy Kisky
In-Reply-To: <20171103015816.16972-1-troy.kisky@boundarydevices.com>
This is a little more efficient and avoids the warning
WARNING: possible circular locking dependency detected
4.14.0-rc7-00010 #16 Not tainted
------------------------------------------------------
kworker/2:1/70 is trying to acquire lock:
(prepare_lock){+.+.}, at: [<c049300c>] clk_prepare_lock+0x80/0xf4
but task is already holding lock:
(i2c_register_adapter){+.+.}, at: [<c0690b04>]
i2c_adapter_lock_bus+0x14/0x18
which lock already depends on the new lock.
the existing dependency chain (in reverse order) is:
-> #1 (i2c_register_adapter){+.+.}:
rt_mutex_lock+0x44/0x5c
i2c_adapter_lock_bus+0x14/0x18
i2c_transfer+0xa8/0xbc
i2c_smbus_xfer+0x20c/0x5d8
i2c_smbus_read_byte_data+0x38/0x48
m41t80_sqw_is_prepared+0x18/0x28
Signed-off-by: Troy Kisky <troy.kisky@boundarydevices.com>
---
drivers/rtc/rtc-m41t80.c | 15 ++++++---------
1 file changed, 6 insertions(+), 9 deletions(-)
diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c
index f44dcf628c87..96a606d5f6e6 100644
--- a/drivers/rtc/rtc-m41t80.c
+++ b/drivers/rtc/rtc-m41t80.c
@@ -155,6 +155,7 @@ struct m41t80_data {
#ifdef CONFIG_COMMON_CLK
struct clk_hw sqw;
unsigned long freq;
+ unsigned int sqwe;
#endif
};
@@ -527,7 +528,10 @@ static int m41t80_sqw_control(struct clk_hw *hw, bool enable)
else
ret &= ~M41T80_ALMON_SQWE;
- return i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, ret);
+ ret = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, ret);
+ if (!ret)
+ m41t80->sqwe = enable;
+ return ret;
}
static int m41t80_sqw_prepare(struct clk_hw *hw)
@@ -542,14 +546,7 @@ static void m41t80_sqw_unprepare(struct clk_hw *hw)
static int m41t80_sqw_is_prepared(struct clk_hw *hw)
{
- struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw);
- struct i2c_client *client = m41t80->client;
- int ret = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
-
- if (ret < 0)
- return ret;
-
- return !!(ret & M41T80_ALMON_SQWE);
+ return sqw_to_m41t80_data(hw)->sqwe;
}
static const struct clk_ops m41t80_sqw_ops = {
--
2.11.0
^ permalink raw reply related
* [PATCH v1 2/5] rtc: m41t80: fix m41t80_sqw_round_rate return value
From: Troy Kisky @ 2017-11-03 1:58 UTC (permalink / raw)
To: a.zummo, alexandre.belloni; +Cc: gary.bisson, linux-rtc, Troy Kisky
In-Reply-To: <20171103015816.16972-1-troy.kisky@boundarydevices.com>
Previously it was returning the best of
32768, 8192, 1024, 64, 2, 0
Now, best of
32768, 8192, 4096, 2048, 1024, 512, 256, 128,
64, 32, 16, 8, 4, 2, 1, 0
Signed-off-by: Troy Kisky <troy.kisky@boundarydevices.com>
---
drivers/rtc/rtc-m41t80.c | 19 +++++++------------
1 file changed, 7 insertions(+), 12 deletions(-)
diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c
index 8f5843169dc2..42fc735a5446 100644
--- a/drivers/rtc/rtc-m41t80.c
+++ b/drivers/rtc/rtc-m41t80.c
@@ -468,18 +468,13 @@ static unsigned long m41t80_sqw_recalc_rate(struct clk_hw *hw,
static long m41t80_sqw_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
- int i, freq = M41T80_SQW_MAX_FREQ;
-
- if (freq <= rate)
- return freq;
-
- for (i = 2; i <= ilog2(M41T80_SQW_MAX_FREQ); i++) {
- freq /= 1 << i;
- if (freq <= rate)
- return freq;
- }
-
- return 0;
+ if (rate >= M41T80_SQW_MAX_FREQ)
+ return M41T80_SQW_MAX_FREQ;
+ if (rate >= M41T80_SQW_MAX_FREQ / 4)
+ return M41T80_SQW_MAX_FREQ / 4;
+ if (!rate)
+ return 0;
+ return 1 << ilog2(rate);
}
static int m41t80_sqw_set_rate(struct clk_hw *hw, unsigned long rate,
--
2.11.0
^ permalink raw reply related
* [PATCH v1 3/5] rtc: m41t80: avoid i2c read in m41t80_sqw_recalc_rate
From: Troy Kisky @ 2017-11-03 1:58 UTC (permalink / raw)
To: a.zummo, alexandre.belloni; +Cc: gary.bisson, linux-rtc, Troy Kisky
In-Reply-To: <20171103015816.16972-1-troy.kisky@boundarydevices.com>
This is a little more efficient, and avoids the warning
WARNING: possible circular locking dependency detected
4.14.0-rc7-00007 #14 Not tainted
------------------------------------------------------
alsactl/330 is trying to acquire lock:
(prepare_lock){+.+.}, at: [<c049300c>] clk_prepare_lock+0x80/0xf4
but task is already holding lock:
(i2c_register_adapter){+.+.}, at: [<c0690ae0>]
i2c_adapter_lock_bus+0x14/0x18
which lock already depends on the new lock.
the existing dependency chain (in reverse order) is:
-> #1 (i2c_register_adapter){+.+.}:
rt_mutex_lock+0x44/0x5c
i2c_adapter_lock_bus+0x14/0x18
i2c_transfer+0xa8/0xbc
i2c_smbus_xfer+0x20c/0x5d8
i2c_smbus_read_byte_data+0x38/0x48
m41t80_sqw_recalc_rate+0x24/0x58
Signed-off-by: Troy Kisky <troy.kisky@boundarydevices.com>
---
drivers/rtc/rtc-m41t80.c | 28 +++++++++++++++++-----------
1 file changed, 17 insertions(+), 11 deletions(-)
diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c
index 42fc735a5446..f44dcf628c87 100644
--- a/drivers/rtc/rtc-m41t80.c
+++ b/drivers/rtc/rtc-m41t80.c
@@ -154,6 +154,7 @@ struct m41t80_data {
struct rtc_device *rtc;
#ifdef CONFIG_COMMON_CLK
struct clk_hw sqw;
+ unsigned long freq;
#endif
};
@@ -443,26 +444,28 @@ static SIMPLE_DEV_PM_OPS(m41t80_pm, m41t80_suspend, m41t80_resume);
#ifdef CONFIG_COMMON_CLK
#define sqw_to_m41t80_data(_hw) container_of(_hw, struct m41t80_data, sqw)
-static unsigned long m41t80_sqw_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
+static unsigned long m41t80_decode_freq(int setting)
+{
+ return (setting == 0) ? 0 : (setting == 1) ? M41T80_SQW_MAX_FREQ :
+ M41T80_SQW_MAX_FREQ >> setting;
+}
+
+static unsigned long m41t80_get_freq(struct m41t80_data *m41t80)
{
- struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw);
struct i2c_client *client = m41t80->client;
int reg_sqw = (m41t80->features & M41T80_FEATURE_SQ_ALT) ?
M41T80_REG_WDAY : M41T80_REG_SQW;
int ret = i2c_smbus_read_byte_data(client, reg_sqw);
- unsigned long val = M41T80_SQW_MAX_FREQ;
if (ret < 0)
return 0;
+ return m41t80_decode_freq(ret >> 4);
+}
- ret >>= 4;
- if (ret == 0)
- val = 0;
- else if (ret > 1)
- val = val / (1 << ret);
-
- return val;
+static unsigned long m41t80_sqw_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return sqw_to_m41t80_data(hw)->freq;
}
static long m41t80_sqw_round_rate(struct clk_hw *hw, unsigned long rate,
@@ -505,6 +508,8 @@ static int m41t80_sqw_set_rate(struct clk_hw *hw, unsigned long rate,
reg = (reg & 0x0f) | (val << 4);
ret = i2c_smbus_write_byte_data(client, reg_sqw, reg);
+ if (!ret)
+ m41t80->freq = m41t80_decode_freq(val);
return ret;
}
@@ -579,6 +584,7 @@ static struct clk *m41t80_sqw_register_clk(struct m41t80_data *m41t80)
init.parent_names = NULL;
init.num_parents = 0;
m41t80->sqw.init = &init;
+ m41t80->freq = m41t80_get_freq(m41t80);
/* optional override of the clockname */
of_property_read_string(node, "clock-output-names", &init.name);
--
2.11.0
^ permalink raw reply related
* [PATCH v1 5/5] rtc: m41t80: remove unneeded checks from m41t80_sqw_set_rate
From: Troy Kisky @ 2017-11-03 1:58 UTC (permalink / raw)
To: a.zummo, alexandre.belloni; +Cc: gary.bisson, linux-rtc, Troy Kisky
In-Reply-To: <20171103015816.16972-1-troy.kisky@boundarydevices.com>
m41t80_sqw_set_rate will be called with the result from
m41t80_sqw_round_rate, so might as well make
m41t80_sqw_set_rate(n) same as
m41t80_sqw_set_rate(m41t80_sqw_round_rate(n))
As Russell King wrote[1],
"clk_round_rate() is supposed to tell you what you end up with if you
ask clk_set_rate() to set the exact same value you passed in - but
clk_round_rate() won't modify the hardware."
[1]
http://lists.infradead.org/pipermail/linux-arm-kernel/2012-January/080175.html
Signed-off-by: Troy Kisky <troy.kisky@boundarydevices.com>
---
drivers/rtc/rtc-m41t80.c | 17 ++++++-----------
1 file changed, 6 insertions(+), 11 deletions(-)
diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c
index 96a606d5f6e6..c90fba3ed861 100644
--- a/drivers/rtc/rtc-m41t80.c
+++ b/drivers/rtc/rtc-m41t80.c
@@ -490,17 +490,12 @@ static int m41t80_sqw_set_rate(struct clk_hw *hw, unsigned long rate,
M41T80_REG_WDAY : M41T80_REG_SQW;
int reg, ret, val = 0;
- if (rate) {
- if (!is_power_of_2(rate))
- return -EINVAL;
- val = ilog2(rate);
- if (val == ilog2(M41T80_SQW_MAX_FREQ))
- val = 1;
- else if (val < (ilog2(M41T80_SQW_MAX_FREQ) - 1))
- val = ilog2(M41T80_SQW_MAX_FREQ) - val;
- else
- return -EINVAL;
- }
+ if (rate >= M41T80_SQW_MAX_FREQ)
+ val = 1;
+ else if (rate >= M41T80_SQW_MAX_FREQ / 4)
+ val = 2;
+ else if (rate)
+ val = 15 - ilog2(rate);
reg = i2c_smbus_read_byte_data(client, reg_sqw);
if (reg < 0)
--
2.11.0
^ permalink raw reply related
* [PATCH] rtc-ds1302: Use common error handling code in ds1302_probe()
From: SF Markus Elfring @ 2017-11-02 9:50 UTC (permalink / raw)
To: linux-rtc, Alessandro Zummo, Alexandre Belloni; +Cc: LKML, kernel-janitors
From: Markus Elfring <elfring@users.sourceforge.net>
Date: Thu, 2 Nov 2017 10:45:08 +0100
* Add a jump target so that a specific error message is stored only once
at the end of this function implementation.
* Replace two calls of the function "dev_err" by goto statements.
* Adjust two condition checks.
This issue was detected by using the Coccinelle software.
Signed-off-by: Markus Elfring <elfring@users.sourceforge.net>
---
drivers/rtc/rtc-ds1302.c | 18 ++++++++----------
1 file changed, 8 insertions(+), 10 deletions(-)
diff --git a/drivers/rtc/rtc-ds1302.c b/drivers/rtc/rtc-ds1302.c
index 0ec4be62322b..06ef64736ac3 100644
--- a/drivers/rtc/rtc-ds1302.c
+++ b/drivers/rtc/rtc-ds1302.c
@@ -132,19 +132,13 @@ static int ds1302_probe(struct spi_device *spi)
addr = RTC_ADDR_CTRL << 1 | RTC_CMD_READ;
status = spi_write_then_read(spi, &addr, sizeof(addr), buf, 1);
- if (status < 0) {
- dev_err(&spi->dev, "control register read error %d\n",
- status);
- return status;
- }
+ if (status)
+ goto report_read_failure;
if ((buf[0] & ~RTC_CMD_WRITE_DISABLE) != 0) {
status = spi_write_then_read(spi, &addr, sizeof(addr), buf, 1);
- if (status < 0) {
- dev_err(&spi->dev, "control register read error %d\n",
- status);
- return status;
- }
+ if (status)
+ goto report_read_failure;
if ((buf[0] & ~RTC_CMD_WRITE_DISABLE) != 0) {
dev_err(&spi->dev, "junk in control register\n");
@@ -189,6 +183,10 @@ static int ds1302_probe(struct spi_device *spi)
}
return 0;
+
+report_read_failure:
+ dev_err(&spi->dev, "control register read error %d\n", status);
+ return status;
}
static int ds1302_remove(struct spi_device *spi)
--
2.14.3
^ permalink raw reply related
* [PATCH] rtc-da9063: Use common error handling code in da9063_rtc_probe()
From: SF Markus Elfring @ 2017-11-02 9:16 UTC (permalink / raw)
To: linux-rtc, support.opensource, Alessandro Zummo,
Alexandre Belloni
Cc: LKML, kernel-janitors
From: Markus Elfring <elfring@users.sourceforge.net>
Date: Thu, 2 Nov 2017 09:45:46 +0100
* Add a jump target so that a specific error message is stored only once
at the end of this function implementation.
* Replace two calls of the function "dev_err" by goto statements.
* Adjust two condition checks.
This issue was detected by using the Coccinelle software.
Signed-off-by: Markus Elfring <elfring@users.sourceforge.net>
---
drivers/rtc/rtc-da9063.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/drivers/rtc/rtc-da9063.c b/drivers/rtc/rtc-da9063.c
index f85cae240f12..c5cbd40666aa 100644
--- a/drivers/rtc/rtc-da9063.c
+++ b/drivers/rtc/rtc-da9063.c
@@ -436,19 +436,15 @@ static int da9063_rtc_probe(struct platform_device *pdev)
config->rtc_alarm_secs_reg,
config->rtc_alarm_status_mask,
0);
- if (ret < 0) {
- dev_err(&pdev->dev, "Failed to access RTC alarm register\n");
- return ret;
- }
+ if (ret)
+ goto report_access_failure;
ret = regmap_update_bits(rtc->regmap,
config->rtc_alarm_secs_reg,
DA9063_ALARM_STATUS_ALARM,
DA9063_ALARM_STATUS_ALARM);
- if (ret < 0) {
- dev_err(&pdev->dev, "Failed to access RTC alarm register\n");
- return ret;
- }
+ if (ret)
+ goto report_access_failure;
ret = regmap_update_bits(rtc->regmap,
config->rtc_alarm_year_reg,
@@ -490,6 +486,10 @@ static int da9063_rtc_probe(struct platform_device *pdev)
irq_alarm, ret);
return ret;
+
+report_access_failure:
+ dev_err(&pdev->dev, "Failed to access RTC alarm register\n");
+ return ret;
}
static struct platform_driver da9063_rtc_driver = {
--
2.14.3
^ permalink raw reply related
* Re: [PATCH V3] rtc: add support for NXP PCF85363 real-time clock
From: Rob Herring @ 2017-11-01 17:45 UTC (permalink / raw)
To: Eric Nelson
Cc: linux-rtc, Alessandro Zummo, Alexandre Belloni, Mark Rutland,
devicetree@vger.kernel.org, Otavio Salvador
In-Reply-To: <1509548480-29472-1-git-send-email-eric@nelint.com>
On Wed, Nov 1, 2017 at 10:01 AM, Eric Nelson <eric@nelint.com> wrote:
> Note that alarms are not currently implemented.
>
> 64 bytes of nvmem is supported and exposed in
> sysfs (# is the instance number, starting with 0):
>
> /sys/bus/nvmem/devices/pcf85363-#/nvmem
>
> Signed-off-by: Eric Nelson <eric@nelint.com>
> Reviewed-by: Fabio Estevam <fabio.estevam@nxp.com>
> ---
> V2 addresses a couple of issues highlighted by Fabio Estevam
> 1. Kconfig updated to select REGMAP_I2C
> 2. Switch to of_device_id from i2c_device_id for driver matching
>
> V3 adds interrupts as an optional property in device tree bindings
>
> drivers/rtc/Kconfig | 13 ++
> drivers/rtc/Makefile | 1 +
> drivers/rtc/rtc-pcf85363.c | 221 +++++++++++++++++++++
> 4 files changed, 252 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/rtc/pcf85363.txt
> create mode 100644 drivers/rtc/rtc-pcf85363.c
For the binding:
Acked-by: Rob Herring <robh@kernel.org>
^ permalink raw reply
* [PATCH V3] rtc: add support for NXP PCF85363 real-time clock
From: Eric Nelson @ 2017-11-01 15:01 UTC (permalink / raw)
To: linux-rtc
Cc: robh, a.zummo, alexandre.belloni, mark.rutland, devicetree,
otavio.salvador, Eric Nelson
In-Reply-To: <CAL_Jsq+QQK=NTMBwk=h0uszLW0uSiEd1fmF09USRNFM-2VgiSQ@mail.gmail.com>
Note that alarms are not currently implemented.
64 bytes of nvmem is supported and exposed in
sysfs (# is the instance number, starting with 0):
/sys/bus/nvmem/devices/pcf85363-#/nvmem
Signed-off-by: Eric Nelson <eric@nelint.com>
Reviewed-by: Fabio Estevam <fabio.estevam@nxp.com>
---
V2 addresses a couple of issues highlighted by Fabio Estevam
1. Kconfig updated to select REGMAP_I2C
2. Switch to of_device_id from i2c_device_id for driver matching
V3 adds interrupts as an optional property in device tree bindings
drivers/rtc/Kconfig | 13 ++
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-pcf85363.c | 221 +++++++++++++++++++++
4 files changed, 252 insertions(+)
create mode 100644 Documentation/devicetree/bindings/rtc/pcf85363.txt
create mode 100644 drivers/rtc/rtc-pcf85363.c
diff --git a/Documentation/devicetree/bindings/rtc/pcf85363.txt b/Documentation/devicetree/bindings/rtc/pcf85363.txt
new file mode 100644
index 0000000..76fdabc
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/pcf85363.txt
@@ -0,0 +1,17 @@
+NXP PCF85363 Real Time Clock
+============================
+
+Required properties:
+- compatible: Should contain "nxp,pcf85363".
+- reg: I2C address for chip.
+
+Optional properties:
+- interrupts: IRQ line for the RTC (not implemented).
+
+Example:
+
+pcf85363: pcf85363@51 {
+ compatible = "nxp,pcf85363";
+ reg = <0x51>;
+};
+
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index e0e58f3..15306ed 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -433,6 +433,19 @@ config RTC_DRV_PCF85063
This driver can also be built as a module. If so, the module
will be called rtc-pcf85063.
+config RTC_DRV_PCF85363
+ tristate "NXP PCF85363"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ If you say yes here you get support for the PCF85363 RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-pcf85363.
+
+ The nvmem interface will be named pcf85363-#, where # is the
+ zero-based instance number.
+
config RTC_DRV_PCF8563
tristate "Philips PCF8563/Epson RTC8564"
help
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 7230014..934a9dd 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -113,6 +113,7 @@ obj-$(CONFIG_RTC_DRV_PCF2123) += rtc-pcf2123.o
obj-$(CONFIG_RTC_DRV_PCF2127) += rtc-pcf2127.o
obj-$(CONFIG_RTC_DRV_PCF50633) += rtc-pcf50633.o
obj-$(CONFIG_RTC_DRV_PCF85063) += rtc-pcf85063.o
+obj-$(CONFIG_RTC_DRV_PCF85363) += rtc-pcf85363.o
obj-$(CONFIG_RTC_DRV_PCF8523) += rtc-pcf8523.o
obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o
obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o
diff --git a/drivers/rtc/rtc-pcf85363.c b/drivers/rtc/rtc-pcf85363.c
new file mode 100644
index 0000000..cf1e70c
--- /dev/null
+++ b/drivers/rtc/rtc-pcf85363.c
@@ -0,0 +1,221 @@
+/*
+ * drivers/rtc/rtc-pcf85363.c
+ *
+ * Driver for NXP PCF85363 real-time clock.
+ *
+ * Copyright (C) 2017 Eric Nelson
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Based loosely on rtc-8583 by Russell King, Wolfram Sang and Juergen Beisert
+ */
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/rtc.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/bcd.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+
+/*
+ * Date/Time registers
+ */
+#define DT_100THS 0x00
+#define DT_SECS 0x01
+#define DT_MINUTES 0x02
+#define DT_HOURS 0x03
+#define DT_DAYS 0x04
+#define DT_WEEKDAYS 0x05
+#define DT_MONTHS 0x06
+#define DT_YEARS 0x07
+
+/*
+ * Alarm registers
+ */
+#define DT_SECOND_ALM1 0x08
+#define DT_MINUTE_ALM1 0x09
+#define DT_HOUR_ALM1 0x0a
+#define DT_DAY_ALM1 0x0b
+#define DT_MONTH_ALM1 0x0c
+#define DT_MINUTE_ALM2 0x0d
+#define DT_HOUR_ALM2 0x0e
+#define DT_WEEKDAY_ALM2 0x0f
+#define DT_ALARM_EN 0x10
+
+/*
+ * Time stamp registers
+ */
+#define DT_TIMESTAMP1 0x11
+#define DT_TIMESTAMP2 0x17
+#define DT_TIMESTAMP3 0x1d
+#define DT_TS_MODE 0x23
+
+/*
+ * control registers
+ */
+#define CTRL_OFFSET 0x24
+#define CTRL_OSCILLATOR 0x25
+#define CTRL_BATTERY 0x26
+#define CTRL_PIN_IO 0x27
+#define CTRL_FUNCTION 0x28
+#define CTRL_INTA_EN 0x29
+#define CTRL_INTB_EN 0x2a
+#define CTRL_FLAGS 0x2b
+#define CTRL_RAMBYTE 0x2c
+#define CTRL_WDOG 0x2d
+#define CTRL_STOP_EN 0x2e
+#define CTRL_RESETS 0x2f
+#define CTRL_RAM 0x40
+
+#define NVRAM_SIZE 0x40
+
+static struct i2c_driver pcf85363_driver;
+
+struct pcf85363 {
+ struct device *dev;
+ struct rtc_device *rtc;
+ struct nvmem_config nvmem_cfg;
+ struct regmap *regmap;
+};
+
+static int pcf85363_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct pcf85363 *pcf85363 = dev_get_drvdata(dev);
+ unsigned char buf[DT_YEARS + 1];
+ int ret, len = sizeof(buf);
+
+ /* read the RTC date and time registers all at once */
+ ret = regmap_bulk_read(pcf85363->regmap, DT_100THS, buf, len);
+ if (ret) {
+ dev_err(dev, "%s: error %d\n", __func__, ret);
+ return ret;
+ }
+
+ tm->tm_year = bcd2bin(buf[DT_YEARS]);
+ /* adjust for 1900 base of rtc_time */
+ tm->tm_year += 100;
+
+ tm->tm_wday = buf[DT_WEEKDAYS] & 7;
+ buf[DT_SECS] &= 0x7F;
+ tm->tm_sec = bcd2bin(buf[DT_SECS]);
+ buf[DT_MINUTES] &= 0x7F;
+ tm->tm_min = bcd2bin(buf[DT_MINUTES]);
+ tm->tm_hour = bcd2bin(buf[DT_HOURS]);
+ tm->tm_mday = bcd2bin(buf[DT_DAYS]);
+ tm->tm_mon = bcd2bin(buf[DT_MONTHS]) - 1;
+
+ return 0;
+}
+
+static int pcf85363_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct pcf85363 *pcf85363 = dev_get_drvdata(dev);
+ unsigned char buf[DT_YEARS + 1];
+ int len = sizeof(buf);
+
+ buf[DT_100THS] = 0;
+ buf[DT_SECS] = bin2bcd(tm->tm_sec);
+ buf[DT_MINUTES] = bin2bcd(tm->tm_min);
+ buf[DT_HOURS] = bin2bcd(tm->tm_hour);
+ buf[DT_DAYS] = bin2bcd(tm->tm_mday);
+ buf[DT_WEEKDAYS] = tm->tm_wday;
+ buf[DT_MONTHS] = bin2bcd(tm->tm_mon + 1);
+ buf[DT_YEARS] = bin2bcd(tm->tm_year % 100);
+
+ return regmap_bulk_write(pcf85363->regmap, DT_100THS,
+ buf, len);
+}
+
+static const struct rtc_class_ops rtc_ops = {
+ .read_time = pcf85363_rtc_read_time,
+ .set_time = pcf85363_rtc_set_time,
+};
+
+static int pcf85363_nvram_read(void *priv, unsigned int offset, void *val,
+ size_t bytes)
+{
+ struct pcf85363 *pcf85363 = priv;
+
+ return regmap_bulk_read(pcf85363->regmap, CTRL_RAM + offset,
+ val, bytes);
+}
+
+static int pcf85363_nvram_write(void *priv, unsigned int offset, void *val,
+ size_t bytes)
+{
+ struct pcf85363 *pcf85363 = priv;
+
+ return regmap_bulk_write(pcf85363->regmap, CTRL_RAM + offset,
+ val, bytes);
+}
+
+static const struct regmap_config regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int pcf85363_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pcf85363 *pcf85363;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ pcf85363 = devm_kzalloc(&client->dev, sizeof(struct pcf85363),
+ GFP_KERNEL);
+ if (!pcf85363)
+ return -ENOMEM;
+
+ pcf85363->regmap = devm_regmap_init_i2c(client, ®map_config);
+ if (IS_ERR(pcf85363->regmap)) {
+ dev_err(&client->dev, "regmap allocation failed\n");
+ return PTR_ERR(pcf85363->regmap);
+ }
+
+ pcf85363->dev = &client->dev;
+ i2c_set_clientdata(client, pcf85363);
+
+ pcf85363->rtc = devm_rtc_allocate_device(pcf85363->dev);
+ if (IS_ERR(pcf85363->rtc))
+ return PTR_ERR(pcf85363->rtc);
+
+ pcf85363->nvmem_cfg.name = "pcf85363-";
+ pcf85363->nvmem_cfg.word_size = 1;
+ pcf85363->nvmem_cfg.stride = 1;
+ pcf85363->nvmem_cfg.size = NVRAM_SIZE;
+ pcf85363->nvmem_cfg.reg_read = pcf85363_nvram_read;
+ pcf85363->nvmem_cfg.reg_write = pcf85363_nvram_write;
+ pcf85363->nvmem_cfg.priv = pcf85363;
+ pcf85363->rtc->nvmem_config = &pcf85363->nvmem_cfg;
+ pcf85363->rtc->nvram_old_abi = true;
+ pcf85363->rtc->ops = &rtc_ops;
+
+ return rtc_register_device(pcf85363->rtc);
+}
+
+static const struct of_device_id dev_ids[] = {
+ { .compatible = "nxp,pcf85363" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, dev_ids);
+
+static struct i2c_driver pcf85363_driver = {
+ .driver = {
+ .name = "pcf85363",
+ .of_match_table = of_match_ptr(dev_ids),
+ },
+ .probe = pcf85363_probe,
+};
+
+module_i2c_driver(pcf85363_driver);
+
+MODULE_AUTHOR("Eric Nelson");
+MODULE_DESCRIPTION("pcf85363 I2C RTC driver");
+MODULE_LICENSE("GPL");
--
2.7.4
^ permalink raw reply related
* [PATCH] rtc: omap: Support scratch registers
From: Alexandre Belloni @ 2017-10-31 16:27 UTC (permalink / raw)
To: linux-rtc; +Cc: Keerthy, linux-kernel, Alexandre Belloni
Register an nvmem device to expose the 3 scratch registers (total of 12
bytes) to both userspace and kernel space.
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
---
drivers/rtc/rtc-omap.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c
index d56d937966dc..1d666ac9ef70 100644
--- a/drivers/rtc/rtc-omap.c
+++ b/drivers/rtc/rtc-omap.c
@@ -70,6 +70,10 @@
#define OMAP_RTC_COMP_MSB_REG 0x50
#define OMAP_RTC_OSC_REG 0x54
+#define OMAP_RTC_SCRATCH0_REG 0x60
+#define OMAP_RTC_SCRATCH1_REG 0x64
+#define OMAP_RTC_SCRATCH2_REG 0x68
+
#define OMAP_RTC_KICK0_REG 0x6c
#define OMAP_RTC_KICK1_REG 0x70
@@ -667,6 +671,45 @@ static struct pinctrl_desc rtc_pinctrl_desc = {
.owner = THIS_MODULE,
};
+static int omap_rtc_scratch_read(void *priv, unsigned int offset, void *_val,
+ size_t bytes)
+{
+ struct omap_rtc *rtc = priv;
+ u32 *val = _val;
+ int i;
+
+ for (i = 0; i < bytes / 4; i++)
+ val[i] = rtc_readl(rtc,
+ OMAP_RTC_SCRATCH0_REG + offset + (i * 4));
+
+ return 0;
+}
+
+static int omap_rtc_scratch_write(void *priv, unsigned int offset, void *_val,
+ size_t bytes)
+{
+ struct omap_rtc *rtc = priv;
+ u32 *val = _val;
+ int i;
+
+ rtc->type->unlock(rtc);
+ for (i = 0; i < bytes / 4; i++)
+ rtc_writel(rtc,
+ OMAP_RTC_SCRATCH0_REG + offset + (i * 4), val[i]);
+ rtc->type->lock(rtc);
+
+ return 0;
+}
+
+static struct nvmem_config omap_rtc_nvmem_config = {
+ .name = "omap_rtc_scratch",
+ .word_size = 4,
+ .stride = 4,
+ .size = OMAP_RTC_KICK0_REG - OMAP_RTC_SCRATCH0_REG,
+ .reg_read = omap_rtc_scratch_read,
+ .reg_write = omap_rtc_scratch_write,
+};
+
static int omap_rtc_probe(struct platform_device *pdev)
{
struct omap_rtc *rtc;
@@ -804,6 +847,8 @@ static int omap_rtc_probe(struct platform_device *pdev)
}
rtc->rtc->ops = &omap_rtc_ops;
+ omap_rtc_nvmem_config.priv = rtc;
+ rtc->rtc->nvmem_config = &omap_rtc_nvmem_config;
/* handle periodic and alarm irqs */
ret = devm_request_irq(&pdev->dev, rtc->irq_timer, rtc_irq, 0,
--
2.15.0.rc2
^ permalink raw reply related
* [RFC v8 7/7] platform/x86: intel_scu_ipc: Use generic Intel IPC device calls
From: sathyanarayanan.kuppuswamy @ 2017-10-29 9:50 UTC (permalink / raw)
To: a.zummo, x86, wim, mingo, alexandre.belloni, qipeng.zha, hpa,
dvhart, tglx, lee.jones, andy, souvik.k.chakravarty
Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86,
sathyaosid, Kuppuswamy Sathyanarayanan
In-Reply-To: <cover.1509268570.git.sathyanarayanan.kuppuswamy@linux.intel.com>
From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
Removed redundant IPC helper functions and refactored the driver to use
generic IPC device driver APIs.
This patch also cleans-up SCU IPC user drivers(rtc-mrst.c, intel-mid_wdt.c,
intel-mid_wdt.c, intel-mid.c) to use APIs provided by generic IPC driver.
Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
Acked-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
---
arch/x86/include/asm/intel_scu_ipc.h | 10 +-
arch/x86/platform/intel-mid/intel-mid.c | 15 +-
drivers/platform/x86/Kconfig | 1 +
drivers/platform/x86/intel_scu_ipc.c | 488 ++++++++++++++------------------
drivers/rtc/rtc-mrst.c | 16 +-
drivers/watchdog/intel-mid_wdt.c | 18 +-
drivers/watchdog/intel_scu_watchdog.c | 23 +-
7 files changed, 267 insertions(+), 304 deletions(-)
Changes since v7:
* Fixed some style issues.
Changes since v6:
* Fixed some style issues.
Changes since v5:
* Adapted to change in arguments of ipc_dev_cmd() and ipc_dev_raw_cmd() APIs.
Changes since v4:
* None
Changes since v3:
* Added intel_ipc_dev_put() support.
diff --git a/arch/x86/include/asm/intel_scu_ipc.h b/arch/x86/include/asm/intel_scu_ipc.h
index 81d3d87..68b5c77 100644
--- a/arch/x86/include/asm/intel_scu_ipc.h
+++ b/arch/x86/include/asm/intel_scu_ipc.h
@@ -18,6 +18,9 @@
#define IPC_CMD_VRTC_SETTIME 1 /* Set time */
#define IPC_CMD_VRTC_SETALARM 2 /* Set alarm */
+#define INTEL_SCU_IPC_DEV "intel_scu_ipc"
+#define SCU_PARAM_LEN 2
+
/* Read single register */
int intel_scu_ipc_ioread8(u16 addr, u8 *data);
@@ -45,13 +48,6 @@ int intel_scu_ipc_writev(u16 *addr, u8 *data, int len);
/* Update single register based on the mask */
int intel_scu_ipc_update_register(u16 addr, u8 data, u8 mask);
-/* Issue commands to the SCU with or without data */
-int intel_scu_ipc_simple_command(int cmd, int sub);
-int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
- u32 *out, int outlen);
-int intel_scu_ipc_raw_command(int cmd, int sub, u8 *in, int inlen,
- u32 *out, int outlen, u32 dptr, u32 sptr);
-
/* I2C control api */
int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data);
diff --git a/arch/x86/platform/intel-mid/intel-mid.c b/arch/x86/platform/intel-mid/intel-mid.c
index 86676ce..23b3ab3 100644
--- a/arch/x86/platform/intel-mid/intel-mid.c
+++ b/arch/x86/platform/intel-mid/intel-mid.c
@@ -22,6 +22,7 @@
#include <linux/irq.h>
#include <linux/export.h>
#include <linux/notifier.h>
+#include <linux/platform_data/x86/intel_ipc_dev.h>
#include <asm/setup.h>
#include <asm/mpspec_def.h>
@@ -70,16 +71,26 @@ EXPORT_SYMBOL_GPL(__intel_mid_cpu_chip);
static void intel_mid_power_off(void)
{
+ struct intel_ipc_dev *ipc_dev;
+ u32 cmds[SCU_PARAM_LEN] = {IPCMSG_COLD_OFF, 1};
+
/* Shut down South Complex via PWRMU */
intel_mid_pwr_power_off();
+ ipc_dev = intel_ipc_dev_get(INTEL_SCU_IPC_DEV);
/* Only for Tangier, the rest will ignore this command */
- intel_scu_ipc_simple_command(IPCMSG_COLD_OFF, 1);
+ ipc_dev_simple_cmd(ipc_dev, cmds, SCU_PARAM_LEN);
+ intel_ipc_dev_put(ipc_dev);
};
static void intel_mid_reboot(void)
{
- intel_scu_ipc_simple_command(IPCMSG_COLD_BOOT, 0);
+ struct intel_ipc_dev *ipc_dev;
+ u32 cmds[SCU_PARAM_LEN] = {IPCMSG_COLD_BOOT, 0};
+
+ ipc_dev = intel_ipc_dev_get(INTEL_SCU_IPC_DEV);
+ ipc_dev_simple_cmd(ipc_dev, cmds, SCU_PARAM_LEN);
+ intel_ipc_dev_put(ipc_dev);
}
static unsigned long __init intel_mid_calibrate_tsc(void)
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 82479ca..e4e5822 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -850,6 +850,7 @@ config INTEL_VBTN
config INTEL_SCU_IPC
bool "Intel SCU IPC Support"
depends on X86_INTEL_MID
+ select REGMAP_MMIO
default y
---help---
IPC is used to bridge the communications between kernel and SCU on
diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c
index 2c85f75..21607e0 100644
--- a/drivers/platform/x86/intel_scu_ipc.c
+++ b/drivers/platform/x86/intel_scu_ipc.c
@@ -24,6 +24,8 @@
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/sfi.h>
+#include <linux/regmap.h>
+#include <linux/platform_data/x86/intel_ipc_dev.h>
#include <asm/intel-mid.h>
#include <asm/intel_scu_ipc.h>
@@ -39,6 +41,25 @@
#define IPC_CMD_PCNTRL_R 1 /* Register read */
#define IPC_CMD_PCNTRL_M 2 /* Register read-modify-write */
+/* IPC dev register offsets */
+/*
+ * IPC Read Buffer (Read Only):
+ * 16 byte buffer for receiving data from SCU, if IPC command
+ * processing results in response data
+ */
+#define IPC_DEV_SCU_RBUF_OFFSET 0x90
+#define IPC_DEV_SCU_WRBUF_OFFSET 0x80
+#define IPC_DEV_SCU_SPTR_OFFSET 0x08
+#define IPC_DEV_SCU_DPTR_OFFSET 0x0C
+#define IPC_DEV_SCU_STATUS_OFFSET 0x04
+
+/* IPC dev commands */
+/* IPC command register IOC bit */
+#define IPC_DEV_SCU_CMD_MSI BIT(8)
+#define IPC_DEV_SCU_CMD_STATUS_ERR BIT(1)
+#define IPC_DEV_SCU_CMD_STATUS_ERR_MASK GENMASK(7, 0)
+#define IPC_DEV_SCU_CMD_STATUS_BUSY BIT(0)
+
/*
* IPC register summary
*
@@ -59,6 +80,11 @@
#define IPC_WWBUF_SIZE 20 /* IPC Write buffer Size */
#define IPC_RWBUF_SIZE 20 /* IPC Read buffer Size */
#define IPC_IOC 0x100 /* IPC command register IOC bit */
+#define IPC_CMD_SIZE 16
+#define IPC_CMD_SUBCMD 12
+#define IPC_RWBUF_SIZE_DWORD 5
+#define IPC_WWBUF_SIZE_DWORD 5
+
#define PCI_DEVICE_ID_LINCROFT 0x082a
#define PCI_DEVICE_ID_PENWELL 0x080e
@@ -93,120 +119,50 @@ static const struct intel_scu_ipc_pdata_t intel_scu_ipc_tangier_pdata = {
struct intel_scu_ipc_dev {
struct device *dev;
+ struct intel_ipc_dev *ipc_dev;
void __iomem *ipc_base;
void __iomem *i2c_base;
- struct completion cmd_complete;
+ struct regmap *ipc_regs;
+ struct regmap *i2c_regs;
u8 irq_mode;
};
-static struct intel_scu_ipc_dev ipcdev; /* Only one for now */
+static struct regmap_config ipc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+};
-/*
- * IPC Read Buffer (Read Only):
- * 16 byte buffer for receiving data from SCU, if IPC command
- * processing results in response data
- */
-#define IPC_READ_BUFFER 0x90
+static struct regmap_config i2c_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .fast_io = true,
+};
+
+static struct intel_scu_ipc_dev ipcdev; /* Only one for now */
#define IPC_I2C_CNTRL_ADDR 0
#define I2C_DATA_ADDR 0x04
-static DEFINE_MUTEX(ipclock); /* lock used to prevent multiple call to SCU */
-
-/*
- * Send ipc command
- * Command Register (Write Only):
- * A write to this register results in an interrupt to the SCU core processor
- * Format:
- * |rfu2(8) | size(8) | command id(4) | rfu1(3) | ioc(1) | command(8)|
- */
-static inline void ipc_command(struct intel_scu_ipc_dev *scu, u32 cmd)
+static int scu_ipc_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen, u32 *out,
+ u32 outlen)
{
- if (scu->irq_mode) {
- reinit_completion(&scu->cmd_complete);
- writel(cmd | IPC_IOC, scu->ipc_base);
- }
- writel(cmd, scu->ipc_base);
-}
-
-/*
- * Write ipc data
- * IPC Write Buffer (Write Only):
- * 16-byte buffer for sending data associated with IPC command to
- * SCU. Size of the data is specified in the IPC_COMMAND_REG register
- */
-static inline void ipc_data_writel(struct intel_scu_ipc_dev *scu, u32 data, u32 offset)
-{
- writel(data, scu->ipc_base + 0x80 + offset);
-}
-
-/*
- * Status Register (Read Only):
- * Driver will read this register to get the ready/busy status of the IPC
- * block and error status of the IPC command that was just processed by SCU
- * Format:
- * |rfu3(8)|error code(8)|initiator id(8)|cmd id(4)|rfu1(2)|error(1)|busy(1)|
- */
-static inline u8 ipc_read_status(struct intel_scu_ipc_dev *scu)
-{
- return __raw_readl(scu->ipc_base + 0x04);
-}
-
-/* Read ipc byte data */
-static inline u8 ipc_data_readb(struct intel_scu_ipc_dev *scu, u32 offset)
-{
- return readb(scu->ipc_base + IPC_READ_BUFFER + offset);
-}
-
-/* Read ipc u32 data */
-static inline u32 ipc_data_readl(struct intel_scu_ipc_dev *scu, u32 offset)
-{
- return readl(scu->ipc_base + IPC_READ_BUFFER + offset);
-}
-
-/* Wait till scu status is busy */
-static inline int busy_loop(struct intel_scu_ipc_dev *scu)
-{
- u32 status = ipc_read_status(scu);
- u32 loop_count = 100000;
-
- /* break if scu doesn't reset busy bit after huge retry */
- while ((status & BIT(0)) && --loop_count) {
- udelay(1); /* scu processing time is in few u secods */
- status = ipc_read_status(scu);
- }
-
- if (status & BIT(0)) {
- dev_err(scu->dev, "IPC timed out");
- return -ETIMEDOUT;
- }
-
- if (status & BIT(1))
- return -EIO;
-
- return 0;
-}
-
-/* Wait till ipc ioc interrupt is received or timeout in 3 HZ */
-static inline int ipc_wait_for_interrupt(struct intel_scu_ipc_dev *scu)
-{
- int status;
+ struct intel_scu_ipc_dev *scu = &ipcdev;
+ struct intel_ipc_raw_cmd ipc_raw_cmd = {0};
+ u32 cmd_list[SCU_PARAM_LEN] = {0};
- if (!wait_for_completion_timeout(&scu->cmd_complete, 3 * HZ)) {
- dev_err(scu->dev, "IPC timed out\n");
- return -ETIMEDOUT;
- }
+ cmd_list[0] = cmd;
+ cmd_list[1] = sub;
- status = ipc_read_status(scu);
- if (status & BIT(1))
- return -EIO;
-
- return 0;
-}
+ ipc_raw_cmd.cmd_list = cmd_list;
+ ipc_raw_cmd.cmdlen = SCU_PARAM_LEN;
+ ipc_raw_cmd.in = in;
+ ipc_raw_cmd.inlen = inlen;
+ ipc_raw_cmd.out = out;
+ ipc_raw_cmd.outlen = outlen;
-static int intel_scu_ipc_check_status(struct intel_scu_ipc_dev *scu)
-{
- return scu->irq_mode ? ipc_wait_for_interrupt(scu) : busy_loop(scu);
+ return ipc_dev_raw_cmd(scu->ipc_dev, &ipc_raw_cmd);
}
/* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */
@@ -215,49 +171,42 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
struct intel_scu_ipc_dev *scu = &ipcdev;
int nc;
u32 offset = 0;
- int err;
+ int err = -EIO;
u8 cbuf[IPC_WWBUF_SIZE];
u32 *wbuf = (u32 *)&cbuf;
+ /* max rbuf size is 20 bytes */
+ u8 rbuf[IPC_RWBUF_SIZE] = {0};
+ u32 rbuflen = DIV_ROUND_UP(count, 4);
memset(cbuf, 0, sizeof(cbuf));
- mutex_lock(&ipclock);
-
- if (scu->dev == NULL) {
- mutex_unlock(&ipclock);
- return -ENODEV;
- }
-
for (nc = 0; nc < count; nc++, offset += 2) {
cbuf[offset] = addr[nc];
cbuf[offset + 1] = addr[nc] >> 8;
}
if (id == IPC_CMD_PCNTRL_R) {
- for (nc = 0, offset = 0; nc < count; nc++, offset += 4)
- ipc_data_writel(scu, wbuf[nc], offset);
- ipc_command(scu, (count * 2) << 16 | id << 12 | 0 << 8 | op);
+ err = scu_ipc_cmd(op, id, (u8 *)wbuf, count * 2,
+ (u32 *)rbuf, IPC_RWBUF_SIZE_DWORD);
} else if (id == IPC_CMD_PCNTRL_W) {
for (nc = 0; nc < count; nc++, offset += 1)
cbuf[offset] = data[nc];
- for (nc = 0, offset = 0; nc < count; nc++, offset += 4)
- ipc_data_writel(scu, wbuf[nc], offset);
- ipc_command(scu, (count * 3) << 16 | id << 12 | 0 << 8 | op);
+ err = scu_ipc_cmd(op, id, (u8 *)wbuf, count * 3, NULL, 0);
+
} else if (id == IPC_CMD_PCNTRL_M) {
cbuf[offset] = data[0];
cbuf[offset + 1] = data[1];
- ipc_data_writel(scu, wbuf[0], 0); /* Write wbuff */
- ipc_command(scu, 4 << 16 | id << 12 | 0 << 8 | op);
+ err = scu_ipc_cmd(op, id, (u8 *)wbuf, 4, NULL, 0);
}
- err = intel_scu_ipc_check_status(scu);
if (!err && id == IPC_CMD_PCNTRL_R) { /* Read rbuf */
/* Workaround: values are read as 0 without memcpy_fromio */
memcpy_fromio(cbuf, scu->ipc_base + 0x90, 16);
- for (nc = 0; nc < count; nc++)
- data[nc] = ipc_data_readb(scu, nc);
+ regmap_bulk_read(scu->ipc_regs, IPC_DEV_SCU_RBUF_OFFSET, rbuf,
+ rbuflen);
+ memcpy(data, rbuf, count);
}
- mutex_unlock(&ipclock);
+
return err;
}
@@ -422,138 +371,6 @@ int intel_scu_ipc_update_register(u16 addr, u8 bits, u8 mask)
}
EXPORT_SYMBOL(intel_scu_ipc_update_register);
-/**
- * intel_scu_ipc_simple_command - send a simple command
- * @cmd: command
- * @sub: sub type
- *
- * Issue a simple command to the SCU. Do not use this interface if
- * you must then access data as any data values may be overwritten
- * by another SCU access by the time this function returns.
- *
- * This function may sleep. Locking for SCU accesses is handled for
- * the caller.
- */
-int intel_scu_ipc_simple_command(int cmd, int sub)
-{
- struct intel_scu_ipc_dev *scu = &ipcdev;
- int err;
-
- mutex_lock(&ipclock);
- if (scu->dev == NULL) {
- mutex_unlock(&ipclock);
- return -ENODEV;
- }
- ipc_command(scu, sub << 12 | cmd);
- err = intel_scu_ipc_check_status(scu);
- mutex_unlock(&ipclock);
- return err;
-}
-EXPORT_SYMBOL(intel_scu_ipc_simple_command);
-
-/**
- * intel_scu_ipc_command - command with data
- * @cmd: command
- * @sub: sub type
- * @in: input data
- * @inlen: input length in dwords
- * @out: output data
- * @outlein: output length in dwords
- *
- * Issue a command to the SCU which involves data transfers. Do the
- * data copies under the lock but leave it for the caller to interpret
- */
-int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
- u32 *out, int outlen)
-{
- struct intel_scu_ipc_dev *scu = &ipcdev;
- int i, err;
-
- mutex_lock(&ipclock);
- if (scu->dev == NULL) {
- mutex_unlock(&ipclock);
- return -ENODEV;
- }
-
- for (i = 0; i < inlen; i++)
- ipc_data_writel(scu, *in++, 4 * i);
-
- ipc_command(scu, (inlen << 16) | (sub << 12) | cmd);
- err = intel_scu_ipc_check_status(scu);
-
- if (!err) {
- for (i = 0; i < outlen; i++)
- *out++ = ipc_data_readl(scu, 4 * i);
- }
-
- mutex_unlock(&ipclock);
- return err;
-}
-EXPORT_SYMBOL(intel_scu_ipc_command);
-
-#define IPC_SPTR 0x08
-#define IPC_DPTR 0x0C
-
-/**
- * intel_scu_ipc_raw_command() - IPC command with data and pointers
- * @cmd: IPC command code.
- * @sub: IPC command sub type.
- * @in: input data of this IPC command.
- * @inlen: input data length in dwords.
- * @out: output data of this IPC command.
- * @outlen: output data length in dwords.
- * @sptr: data writing to SPTR register.
- * @dptr: data writing to DPTR register.
- *
- * Send an IPC command to SCU with input/output data and source/dest pointers.
- *
- * Return: an IPC error code or 0 on success.
- */
-int intel_scu_ipc_raw_command(int cmd, int sub, u8 *in, int inlen,
- u32 *out, int outlen, u32 dptr, u32 sptr)
-{
- struct intel_scu_ipc_dev *scu = &ipcdev;
- int inbuflen = DIV_ROUND_UP(inlen, 4);
- u32 inbuf[4];
- int i, err;
-
- /* Up to 16 bytes */
- if (inbuflen > 4)
- return -EINVAL;
-
- mutex_lock(&ipclock);
- if (scu->dev == NULL) {
- mutex_unlock(&ipclock);
- return -ENODEV;
- }
-
- writel(dptr, scu->ipc_base + IPC_DPTR);
- writel(sptr, scu->ipc_base + IPC_SPTR);
-
- /*
- * SRAM controller doesn't support 8-bit writes, it only
- * supports 32-bit writes, so we have to copy input data into
- * the temporary buffer, and SCU FW will use the inlen to
- * determine the actual input data length in the temporary
- * buffer.
- */
- memcpy(inbuf, in, inlen);
-
- for (i = 0; i < inbuflen; i++)
- ipc_data_writel(scu, inbuf[i], 4 * i);
-
- ipc_command(scu, (inlen << 16) | (sub << 12) | cmd);
- err = intel_scu_ipc_check_status(scu);
- if (!err) {
- for (i = 0; i < outlen; i++)
- *out++ = ipc_data_readl(scu, 4 * i);
- }
-
- mutex_unlock(&ipclock);
- return err;
-}
-EXPORT_SYMBOL_GPL(intel_scu_ipc_raw_command);
-
/* I2C commands */
#define IPC_I2C_WRITE 1 /* I2C Write command */
#define IPC_I2C_READ 2 /* I2C Read command */
@@ -575,48 +392,143 @@ int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data)
struct intel_scu_ipc_dev *scu = &ipcdev;
u32 cmd = 0;
- mutex_lock(&ipclock);
- if (scu->dev == NULL) {
- mutex_unlock(&ipclock);
- return -ENODEV;
- }
cmd = (addr >> 24) & 0xFF;
if (cmd == IPC_I2C_READ) {
- writel(addr, scu->i2c_base + IPC_I2C_CNTRL_ADDR);
+ regmap_write(scu->i2c_regs, IPC_I2C_CNTRL_ADDR, addr);
/* Write not getting updated without delay */
mdelay(1);
- *data = readl(scu->i2c_base + I2C_DATA_ADDR);
+ regmap_read(scu->i2c_regs, I2C_DATA_ADDR, data);
} else if (cmd == IPC_I2C_WRITE) {
- writel(*data, scu->i2c_base + I2C_DATA_ADDR);
+ regmap_write(scu->i2c_regs, I2C_DATA_ADDR, *data);
mdelay(1);
- writel(addr, scu->i2c_base + IPC_I2C_CNTRL_ADDR);
+ regmap_write(scu->i2c_regs, IPC_I2C_CNTRL_ADDR, addr);
} else {
dev_err(scu->dev,
"intel_scu_ipc: I2C INVALID_CMD = 0x%x\n", cmd);
- mutex_unlock(&ipclock);
return -EIO;
}
- mutex_unlock(&ipclock);
return 0;
}
EXPORT_SYMBOL(intel_scu_ipc_i2c_cntrl);
-/*
- * Interrupt handler gets called when ioc bit of IPC_COMMAND_REG set to 1
- * When ioc bit is set to 1, caller api must wait for interrupt handler called
- * which in turn unlocks the caller api. Currently this is not used
- *
- * This is edge triggered so we need take no action to clear anything
- */
-static irqreturn_t ioc(int irq, void *dev_id)
+static int pre_simple_cmd_fn(u32 *cmd_list, u32 cmdlen)
{
- struct intel_scu_ipc_dev *scu = dev_id;
+ if (!cmd_list || cmdlen != SCU_PARAM_LEN)
+ return -EINVAL;
- if (scu->irq_mode)
- complete(&scu->cmd_complete);
+ cmd_list[0] |= (cmd_list[1] << IPC_CMD_SUBCMD);
+
+ return 0;
+}
+
+static int pre_cmd_fn(struct intel_ipc_cmd *ipc_cmd)
+{
+ int ret;
- return IRQ_HANDLED;
+ if (ipc_cmd->inlen > IPC_WWBUF_SIZE_DWORD ||
+ ipc_cmd->outlen > IPC_RWBUF_SIZE_DWORD)
+ return -EINVAL;
+
+ ret = pre_simple_cmd_fn(ipc_cmd->cmd_list, ipc_cmd->cmdlen);
+ if (ret < 0)
+ return ret;
+
+ ipc_cmd->cmd_list[0] |= (ipc_cmd->inlen << IPC_CMD_SIZE);
+
+ return 0;
+}
+
+static int pre_raw_cmd_fn(struct intel_ipc_raw_cmd *ipc_raw_cmd)
+{
+ int ret;
+
+ if (ipc_raw_cmd->inlen > IPC_WWBUF_SIZE ||
+ ipc_raw_cmd->outlen > IPC_RWBUF_SIZE_DWORD)
+ return -EINVAL;
+
+ ret = pre_simple_cmd_fn(ipc_raw_cmd->cmd_list, ipc_raw_cmd->cmdlen);
+ if (ret < 0)
+ return ret;
+
+ ipc_raw_cmd->cmd_list[0] |= (ipc_raw_cmd->inlen << IPC_CMD_SIZE);
+
+ return 0;
+}
+
+static int scu_ipc_err_code(int status)
+{
+ if (status & IPC_DEV_SCU_CMD_STATUS_ERR)
+ return (status & IPC_DEV_SCU_CMD_STATUS_ERR_MASK);
+ else
+ return 0;
+}
+
+static int scu_ipc_busy_check(int status)
+{
+ return status | IPC_DEV_SCU_CMD_STATUS_BUSY;
+}
+
+static u32 scu_ipc_enable_msi(u32 cmd)
+{
+ return cmd | IPC_DEV_SCU_CMD_MSI;
+}
+
+static struct intel_ipc_dev *intel_scu_ipc_dev_create(
+ struct device *dev,
+ void __iomem *base,
+ int irq)
+{
+ struct intel_ipc_dev_ops *ops;
+ struct intel_ipc_dev_cfg *cfg;
+ struct regmap *ipc_regs;
+ struct intel_scu_ipc_dev *scu = dev_get_drvdata(dev);
+
+ cfg = devm_kzalloc(dev, sizeof(*cfg), GFP_KERNEL);
+ if (!cfg)
+ return ERR_PTR(-ENOMEM);
+
+ ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
+ if (!ops)
+ return ERR_PTR(-ENOMEM);
+
+ ipc_regs = devm_regmap_init_mmio_clk(dev, NULL, base,
+ &ipc_regmap_config);
+ if (IS_ERR(ipc_regs)) {
+ dev_err(dev, "ipc_regs regmap init failed\n");
+ return ERR_CAST(ipc_regs);
+ }
+
+ scu->ipc_regs = ipc_regs;
+
+ /* set IPC dev ops */
+ ops->to_err_code = scu_ipc_err_code;
+ ops->busy_check = scu_ipc_busy_check;
+ ops->enable_msi = scu_ipc_enable_msi;
+ ops->pre_cmd_fn = pre_cmd_fn;
+ ops->pre_raw_cmd_fn = pre_raw_cmd_fn;
+ ops->pre_simple_cmd_fn = pre_simple_cmd_fn;
+
+ /* set cfg options */
+ if (scu->irq_mode)
+ cfg->mode = IPC_DEV_MODE_IRQ;
+ else
+ cfg->mode = IPC_DEV_MODE_POLLING;
+
+ cfg->chan_type = IPC_CHANNEL_IA_SCU;
+ cfg->irq = irq;
+ cfg->use_msi = true;
+ cfg->support_sptr = true;
+ cfg->support_dptr = true;
+ cfg->cmd_regs = ipc_regs;
+ cfg->data_regs = ipc_regs;
+ cfg->wrbuf_reg = IPC_DEV_SCU_WRBUF_OFFSET;
+ cfg->rbuf_reg = IPC_DEV_SCU_RBUF_OFFSET;
+ cfg->sptr_reg = IPC_DEV_SCU_SPTR_OFFSET;
+ cfg->dptr_reg = IPC_DEV_SCU_DPTR_OFFSET;
+ cfg->status_reg = IPC_DEV_SCU_STATUS_OFFSET;
+
+ return devm_intel_ipc_dev_create(dev, INTEL_SCU_IPC_DEV, cfg, ops);
}
/**
@@ -650,25 +562,35 @@ static int ipc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (err)
return err;
- init_completion(&scu->cmd_complete);
-
scu->ipc_base = pcim_iomap_table(pdev)[0];
- scu->i2c_base = ioremap_nocache(pdata->i2c_base, pdata->i2c_len);
+ scu->i2c_base = devm_ioremap_nocache(&pdev->dev, pdata->i2c_base,
+ pdata->i2c_len);
if (!scu->i2c_base)
return -ENOMEM;
- err = devm_request_irq(&pdev->dev, pdev->irq, ioc, 0, "intel_scu_ipc",
- scu);
- if (err)
- return err;
+ pci_set_drvdata(pdev, scu);
+
+ scu->i2c_regs = devm_regmap_init_mmio_clk(&pdev->dev, NULL,
+ scu->i2c_base,
+ &i2c_regmap_config);
+ if (IS_ERR(scu->i2c_regs)) {
+ dev_err(&pdev->dev, "i2c_regs regmap init failed\n");
+ return PTR_ERR(scu->i2c_regs);
+ }
+
+ scu->ipc_dev = intel_scu_ipc_dev_create(&pdev->dev, scu->ipc_base,
+ pdev->irq);
+ if (IS_ERR(scu->ipc_dev)) {
+ dev_err(&pdev->dev, "Failed to create SCU IPC device\n");
+ return PTR_ERR(scu->ipc_dev);
+ }
/* Assign device at last */
scu->dev = &pdev->dev;
intel_scu_devices_create();
- pci_set_drvdata(pdev, scu);
return 0;
}
diff --git a/drivers/rtc/rtc-mrst.c b/drivers/rtc/rtc-mrst.c
index 7334c44..37595c4 100644
--- a/drivers/rtc/rtc-mrst.c
+++ b/drivers/rtc/rtc-mrst.c
@@ -36,6 +36,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sfi.h>
+#include <linux/platform_data/x86/intel_ipc_dev.h>
#include <asm/intel_scu_ipc.h>
#include <asm/intel-mid.h>
@@ -46,6 +47,7 @@ struct mrst_rtc {
struct device *dev;
int irq;
struct resource *iomem;
+ struct intel_ipc_dev *ipc_dev;
u8 enabled_wake;
u8 suspend_ctrl;
@@ -110,10 +112,11 @@ static int mrst_read_time(struct device *dev, struct rtc_time *time)
static int mrst_set_time(struct device *dev, struct rtc_time *time)
{
- int ret;
unsigned long flags;
unsigned char mon, day, hrs, min, sec;
unsigned int yrs;
+ struct mrst_rtc *mrst = dev_get_drvdata(dev);
+ u32 cmds[SCU_PARAM_LEN] = {IPCMSG_VRTC, IPC_CMD_VRTC_SETTIME};
yrs = time->tm_year;
mon = time->tm_mon + 1; /* tm_mon starts at zero */
@@ -137,8 +140,7 @@ static int mrst_set_time(struct device *dev, struct rtc_time *time)
spin_unlock_irqrestore(&rtc_lock, flags);
- ret = intel_scu_ipc_simple_command(IPCMSG_VRTC, IPC_CMD_VRTC_SETTIME);
- return ret;
+ return ipc_dev_simple_cmd(mrst->ipc_dev, cmds, SCU_PARAM_LEN);
}
static int mrst_read_alarm(struct device *dev, struct rtc_wkalrm *t)
@@ -210,6 +212,7 @@ static int mrst_set_alarm(struct device *dev, struct rtc_wkalrm *t)
struct mrst_rtc *mrst = dev_get_drvdata(dev);
unsigned char hrs, min, sec;
int ret = 0;
+ u32 cmds[SCU_PARAM_LEN] = {IPCMSG_VRTC, IPC_CMD_VRTC_SETALARM};
if (!mrst->irq)
return -EIO;
@@ -229,7 +232,7 @@ static int mrst_set_alarm(struct device *dev, struct rtc_wkalrm *t)
spin_unlock_irq(&rtc_lock);
- ret = intel_scu_ipc_simple_command(IPCMSG_VRTC, IPC_CMD_VRTC_SETALARM);
+ ret = ipc_dev_simple_cmd(mrst->ipc_dev, cmds, SCU_PARAM_LEN);
if (ret)
return ret;
@@ -329,6 +332,10 @@ static int vrtc_mrst_do_probe(struct device *dev, struct resource *iomem,
if (!iomem)
return -ENODEV;
+ mrst_rtc.ipc_dev = intel_ipc_dev_get(INTEL_SCU_IPC_DEV);
+ if (IS_ERR_OR_NULL(mrst_rtc.ipc_dev))
+ return PTR_ERR(mrst_rtc.ipc_dev);
+
iomem = request_mem_region(iomem->start, resource_size(iomem),
driver_name);
if (!iomem) {
@@ -394,6 +401,7 @@ static void rtc_mrst_do_remove(struct device *dev)
rtc_mrst_do_shutdown();
+ intel_ipc_dev_put(mrst->ipc_dev);
if (mrst->irq)
free_irq(mrst->irq, mrst->rtc);
diff --git a/drivers/watchdog/intel-mid_wdt.c b/drivers/watchdog/intel-mid_wdt.c
index 72c108a..d6f2993 100644
--- a/drivers/watchdog/intel-mid_wdt.c
+++ b/drivers/watchdog/intel-mid_wdt.c
@@ -18,6 +18,7 @@
#include <linux/platform_device.h>
#include <linux/watchdog.h>
#include <linux/platform_data/intel-mid_wdt.h>
+#include <linux/platform_data/x86/intel_ipc_dev.h>
#include <asm/intel_scu_ipc.h>
#include <asm/intel-mid.h>
@@ -29,6 +30,8 @@
#define MID_WDT_TIMEOUT_MAX 170
#define MID_WDT_DEFAULT_TIMEOUT 90
+static struct intel_ipc_dev *scu_ipc_dev;
+
/* SCU watchdog messages */
enum {
SCU_WATCHDOG_START = 0,
@@ -38,7 +41,16 @@ enum {
static inline int wdt_command(int sub, u32 *in, int inlen)
{
- return intel_scu_ipc_command(IPC_WATCHDOG, sub, in, inlen, NULL, 0);
+ u32 cmds[SCU_PARAM_LEN] = {IPC_WATCHDOG, sub};
+ struct intel_ipc_cmd ipc_cmd = {0};
+
+ ipc_cmd.cmd_list = cmds;
+ ipc_cmd.cmdlen = SCU_PARAM_LEN;
+ ipc_cmd.in = in;
+ ipc_cmd.out = NULL;
+ ipc_cmd.inlen = inlen;
+
+ return ipc_dev_cmd(scu_ipc_dev, &ipc_cmd);
}
static int wdt_start(struct watchdog_device *wd)
@@ -129,6 +141,10 @@ static int mid_wdt_probe(struct platform_device *pdev)
if (!wdt_dev)
return -ENOMEM;
+ scu_ipc_dev = devm_intel_ipc_dev_get(&pdev->dev, INTEL_SCU_IPC_DEV);
+ if (IS_ERR_OR_NULL(scu_ipc_dev))
+ return PTR_ERR(scu_ipc_dev);
+
wdt_dev->info = &mid_wdt_info;
wdt_dev->ops = &mid_wdt_ops;
wdt_dev->min_timeout = MID_WDT_TIMEOUT_MIN;
diff --git a/drivers/watchdog/intel_scu_watchdog.c b/drivers/watchdog/intel_scu_watchdog.c
index 0caab62..1210961a7 100644
--- a/drivers/watchdog/intel_scu_watchdog.c
+++ b/drivers/watchdog/intel_scu_watchdog.c
@@ -49,6 +49,7 @@
#include <asm/intel_scu_ipc.h>
#include <asm/apb_timer.h>
#include <asm/intel-mid.h>
+#include <linux/platform_data/x86/intel_ipc_dev.h>
#include "intel_scu_watchdog.h"
@@ -94,6 +95,8 @@ MODULE_PARM_DESC(force_boot,
static struct intel_scu_watchdog_dev watchdog_device;
+static struct intel_ipc_dev *scu_ipc_dev;
+
/* Forces restart, if force_reboot is set */
static void watchdog_fire(void)
{
@@ -128,19 +131,20 @@ static int watchdog_set_ipc(int soft_threshold, int threshold)
u32 *ipc_wbuf;
u8 cbuf[16] = { '\0' };
int ipc_ret = 0;
+ u32 cmds[SCU_PARAM_LEN] = {IPC_SET_WATCHDOG_TIMER, 0};
+ struct intel_ipc_cmd ipc_cmd;
ipc_wbuf = (u32 *)&cbuf;
ipc_wbuf[0] = soft_threshold;
ipc_wbuf[1] = threshold;
- ipc_ret = intel_scu_ipc_command(
- IPC_SET_WATCHDOG_TIMER,
- 0,
- ipc_wbuf,
- 2,
- NULL,
- 0);
+ ipc_cmd.cmd_list = cmds;
+ ipc_cmd.cmdlen = SCU_PARAM_LEN;
+ ipc_cmd.in = ipc_wbuf;
+ ipc_cmd.inlen = 2;
+ ipc_cmd.out = NULL;
+ ipc_ret = ipc_dev_cmd(scu_ipc_dev, &ipc_cmd);
if (ipc_ret != 0)
pr_err("Error setting SCU watchdog timer: %x\n", ipc_ret);
@@ -460,6 +464,10 @@ static int __init intel_scu_watchdog_init(void)
if (check_timer_margin(timer_margin))
return -EINVAL;
+ scu_ipc_dev = intel_ipc_dev_get(INTEL_SCU_IPC_DEV);
+ if (IS_ERR_OR_NULL(scu_ipc_dev))
+ return PTR_ERR(scu_ipc_dev);
+
watchdog_device.timer_tbl_ptr = sfi_get_mtmr(sfi_mtimer_num-1);
if (watchdog_device.timer_tbl_ptr == NULL) {
@@ -550,6 +558,7 @@ static void __exit intel_scu_watchdog_exit(void)
{
misc_deregister(&watchdog_device.miscdev);
+ intel_ipc_dev_put(scu_ipc_dev);
unregister_reboot_notifier(&watchdog_device.intel_scu_notifier);
/* disable the timer */
iowrite32(0x00000002, watchdog_device.timer_control_addr);
--
2.7.4
^ permalink raw reply related
* [RFC v8 6/7] platform/x86: intel_pmc_ipc: Use generic Intel IPC device calls
From: sathyanarayanan.kuppuswamy @ 2017-10-29 9:49 UTC (permalink / raw)
To: a.zummo, x86, wim, mingo, alexandre.belloni, qipeng.zha, hpa,
dvhart, tglx, lee.jones, andy, souvik.k.chakravarty
Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86,
sathyaosid, Kuppuswamy Sathyanarayanan
In-Reply-To: <cover.1509268570.git.sathyanarayanan.kuppuswamy@linux.intel.com>
From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
Removed redundant IPC helper functions and refactored the driver to use
generic IPC device driver APIs. Also, cleaned up the driver to minimize
the usage of global variable ipcdev by propogating the struct
intel_pmc_ipc_dev pointer or by getting it from device private data.
This patch also cleans-up PMC IPC user drivers(intel_telemetry_pltdrv.c,
intel_soc_pmic_bxtwc.c) to use APIs provided by generic IPC driver.
Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
Acked-by: Lee Jones <lee.jones@linaro.org>
---
arch/x86/include/asm/intel_pmc_ipc.h | 31 +--
drivers/mfd/intel_soc_pmic_bxtwc.c | 37 ++-
drivers/platform/x86/intel_pmc_ipc.c | 382 ++++++++++----------------
drivers/platform/x86/intel_telemetry_pltdrv.c | 128 ++++-----
include/linux/mfd/intel_soc_pmic.h | 2 +
5 files changed, 247 insertions(+), 333 deletions(-)
Changes since v7:
* Fixed style issues.
Changes since v6:
* Fixed some style and rebase issues.
Changes since v5:
* Adapted to change in arguments of ipc_dev_cmd() and ipc_dev_raw_cmd() APIs.
Changes since v4:
* None
Changes since v3:
* Added unique name to PMC regmaps.
* Added support to clear interrupt bit.
* Added intel_ipc_dev_put() support.
Changes since v1:
* Removed custom APIs.
* Cleaned up PMC IPC user drivers to use APIs provided by generic
IPC driver.
diff --git a/arch/x86/include/asm/intel_pmc_ipc.h b/arch/x86/include/asm/intel_pmc_ipc.h
index fac89eb..ca2f5e3 100644
--- a/arch/x86/include/asm/intel_pmc_ipc.h
+++ b/arch/x86/include/asm/intel_pmc_ipc.h
@@ -1,10 +1,15 @@
#ifndef _ASM_X86_INTEL_PMC_IPC_H_
#define _ASM_X86_INTEL_PMC_IPC_H_
+#include <linux/platform_data/x86/intel_ipc_dev.h>
+
+#define INTEL_PMC_IPC_DEV "intel_pmc_ipc"
+#define PMC_PARAM_LEN 2
+
/* Commands */
#define PMC_IPC_PMIC_ACCESS 0xFF
-#define PMC_IPC_PMIC_ACCESS_READ 0x0
-#define PMC_IPC_PMIC_ACCESS_WRITE 0x1
+#define PMC_IPC_PMIC_ACCESS_READ 0x0
+#define PMC_IPC_PMIC_ACCESS_WRITE 0x1
#define PMC_IPC_USB_PWR_CTRL 0xF0
#define PMC_IPC_PMIC_BLACKLIST_SEL 0xEF
#define PMC_IPC_PHY_CONFIG 0xEE
@@ -30,11 +35,6 @@
#if IS_ENABLED(CONFIG_INTEL_PMC_IPC)
-int intel_pmc_ipc_simple_command(int cmd, int sub);
-int intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen,
- u32 *out, u32 outlen, u32 dptr, u32 sptr);
-int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
- u32 *out, u32 outlen);
int intel_pmc_s0ix_counter_read(u64 *data);
int intel_pmc_gcr_read(u32 offset, u32 *data);
int intel_pmc_gcr_write(u32 offset, u32 data);
@@ -42,23 +42,6 @@ int intel_pmc_gcr_update(u32 offset, u32 mask, u32 val);
#else
-static inline int intel_pmc_ipc_simple_command(int cmd, int sub)
-{
- return -EINVAL;
-}
-
-static inline int intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen,
- u32 *out, u32 outlen, u32 dptr, u32 sptr)
-{
- return -EINVAL;
-}
-
-static inline int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
- u32 *out, u32 outlen)
-{
- return -EINVAL;
-}
-
static inline int intel_pmc_s0ix_counter_read(u64 *data)
{
return -EINVAL;
diff --git a/drivers/mfd/intel_soc_pmic_bxtwc.c b/drivers/mfd/intel_soc_pmic_bxtwc.c
index 15bc052..55ffcae 100644
--- a/drivers/mfd/intel_soc_pmic_bxtwc.c
+++ b/drivers/mfd/intel_soc_pmic_bxtwc.c
@@ -271,6 +271,9 @@ static int regmap_ipc_byte_reg_read(void *context, unsigned int reg,
u8 ipc_in[2];
u8 ipc_out[4];
struct intel_soc_pmic *pmic = context;
+ struct intel_ipc_raw_cmd ipc_raw_cmd;
+ u32 cmd[PMC_PARAM_LEN] = {PMC_IPC_PMIC_ACCESS,
+ PMC_IPC_PMIC_ACCESS_READ};
if (!pmic)
return -EINVAL;
@@ -284,9 +287,15 @@ static int regmap_ipc_byte_reg_read(void *context, unsigned int reg,
ipc_in[0] = reg;
ipc_in[1] = i2c_addr;
- ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS,
- PMC_IPC_PMIC_ACCESS_READ,
- ipc_in, sizeof(ipc_in), (u32 *)ipc_out, 1);
+
+ ipc_raw_cmd.cmd_list = cmd;
+ ipc_raw_cmd.cmdlen = PMC_PARAM_LEN;
+ ipc_raw_cmd.in = ipc_in;
+ ipc_raw_cmd.inlen = sizeof(ipc_in);
+ ipc_raw_cmd.out = (u32 *)ipc_out;
+ ipc_raw_cmd.outlen = 1;
+
+ ret = ipc_dev_raw_cmd(pmic->ipc_dev, &ipc_raw_cmd);
if (ret) {
dev_err(pmic->dev, "Failed to read from PMIC\n");
return ret;
@@ -303,6 +312,9 @@ static int regmap_ipc_byte_reg_write(void *context, unsigned int reg,
int i2c_addr;
u8 ipc_in[3];
struct intel_soc_pmic *pmic = context;
+ struct intel_ipc_raw_cmd ipc_raw_cmd;
+ u32 cmd[PMC_PARAM_LEN] = {PMC_IPC_PMIC_ACCESS,
+ PMC_IPC_PMIC_ACCESS_WRITE};
if (!pmic)
return -EINVAL;
@@ -317,9 +329,15 @@ static int regmap_ipc_byte_reg_write(void *context, unsigned int reg,
ipc_in[0] = reg;
ipc_in[1] = i2c_addr;
ipc_in[2] = val;
- ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS,
- PMC_IPC_PMIC_ACCESS_WRITE,
- ipc_in, sizeof(ipc_in), NULL, 0);
+
+ ipc_raw_cmd.cmd_list = cmd;
+ ipc_raw_cmd.cmdlen = PMC_PARAM_LEN;
+ ipc_raw_cmd.in = ipc_in;
+ ipc_raw_cmd.inlen = sizeof(ipc_in);
+ ipc_raw_cmd.out = NULL;
+ ipc_raw_cmd.outlen = 0;
+
+ ret = ipc_dev_raw_cmd(pmic->ipc_dev, &ipc_raw_cmd);
if (ret) {
dev_err(pmic->dev, "Failed to write to PMIC\n");
return ret;
@@ -445,6 +463,10 @@ static int bxtwc_probe(struct platform_device *pdev)
if (!pmic)
return -ENOMEM;
+ pmic->ipc_dev = intel_ipc_dev_get(INTEL_PMC_IPC_DEV);
+ if (IS_ERR_OR_NULL(pmic->ipc_dev))
+ return PTR_ERR(pmic->ipc_dev);
+
ret = platform_get_irq(pdev, 0);
if (ret < 0) {
dev_err(&pdev->dev, "Invalid IRQ\n");
@@ -562,7 +584,10 @@ static int bxtwc_probe(struct platform_device *pdev)
static int bxtwc_remove(struct platform_device *pdev)
{
+ struct intel_soc_pmic *pmic = dev_get_drvdata(&pdev->dev);
+
sysfs_remove_group(&pdev->dev.kobj, &bxtwc_group);
+ intel_ipc_dev_put(pmic->ipc_dev);
return 0;
}
diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c
index df6af1f..7d6a82d 100644
--- a/drivers/platform/x86/intel_pmc_ipc.c
+++ b/drivers/platform/x86/intel_pmc_ipc.c
@@ -48,18 +48,8 @@
* The ARC handles the interrupt and services it, writing optional data to
* the IPC1 registers, updates the IPC_STS response register with the status.
*/
-#define IPC_CMD 0x0
-#define IPC_CMD_MSI 0x100
#define IPC_CMD_SIZE 16
#define IPC_CMD_SUBCMD 12
-#define IPC_STATUS 0x04
-#define IPC_STATUS_IRQ 0x4
-#define IPC_STATUS_ERR 0x2
-#define IPC_STATUS_BUSY 0x1
-#define IPC_SPTR 0x08
-#define IPC_DPTR 0x0C
-#define IPC_WRITE_BUFFER 0x80
-#define IPC_READ_BUFFER 0x90
/* Residency with clock rate at 19.2MHz to usecs */
#define S0IX_RESIDENCY_IN_USECS(d, s) \
@@ -74,11 +64,6 @@
*/
#define IPC_DATA_BUFFER_SIZE 16
-#define IPC_LOOP_CNT 3000000
-#define IPC_MAX_SEC 3
-
-#define IPC_TRIGGER_MODE_IRQ true
-
/* exported resources from IFWI */
#define PLAT_RESOURCE_IPC_INDEX 0
#define PLAT_RESOURCE_IPC_SIZE 0x1000
@@ -118,36 +103,41 @@
#define PMC_CFG_NO_REBOOT_EN (1 << 4)
#define PMC_CFG_NO_REBOOT_DIS (0 << 4)
+/* IPC PMC commands */
+#define IPC_DEV_PMC_CMD_MSI BIT(8)
+#define IPC_DEV_PMC_CMD_SIZE 16
+#define IPC_DEV_PMC_CMD_SUBCMD 12
+#define IPC_DEV_PMC_CMD_STATUS BIT(2)
+#define IPC_DEV_PMC_CMD_STATUS_IRQ BIT(2)
+#define IPC_DEV_PMC_CMD_STATUS_ERR BIT(1)
+#define IPC_DEV_PMC_CMD_STATUS_ERR_MASK GENMASK(7, 0)
+#define IPC_DEV_PMC_CMD_STATUS_BUSY BIT(0)
+
+/*IPC PMC reg offsets */
+#define IPC_DEV_PMC_STATUS_OFFSET 0x04
+#define IPC_DEV_PMC_SPTR_OFFSET 0x08
+#define IPC_DEV_PMC_DPTR_OFFSET 0x0C
+#define IPC_DEV_PMC_WRBUF_OFFSET 0x80
+#define IPC_DEV_PMC_RBUF_OFFSET 0x90
+
static struct intel_pmc_ipc_dev {
struct device *dev;
+ struct intel_ipc_dev *pmc_ipc_dev;
+ struct intel_ipc_dev_ops ops;
+ struct intel_ipc_dev_cfg cfg;
void __iomem *ipc_base;
- bool irq_mode;
- int irq;
- int cmd;
- struct completion cmd_complete;
/* gcr */
void __iomem *gcr_mem_base;
struct regmap *gcr_regs;
} ipcdev;
-static char *ipc_err_sources[] = {
- [IPC_ERR_NONE] =
- "no error",
- [IPC_ERR_CMD_NOT_SUPPORTED] =
- "command not supported",
- [IPC_ERR_CMD_NOT_SERVICED] =
- "command not serviced",
- [IPC_ERR_UNABLE_TO_SERVICE] =
- "unable to service",
- [IPC_ERR_CMD_INVALID] =
- "command invalid",
- [IPC_ERR_CMD_FAILED] =
- "command failed",
- [IPC_ERR_EMSECURITY] =
- "Invalid Battery",
- [IPC_ERR_UNSIGNEDKERNEL] =
- "Unsigned kernel",
+static struct regmap_config pmc_regmap_config = {
+ .name = "intel_pmc_regs",
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .fast_io = true,
};
static struct regmap_config gcr_regmap_config = {
@@ -159,40 +149,6 @@ static struct regmap_config gcr_regmap_config = {
.max_register = PLAT_RESOURCE_GCR_SIZE,
};
-/* Prevent concurrent calls to the PMC */
-static DEFINE_MUTEX(ipclock);
-
-static inline void ipc_send_command(u32 cmd)
-{
- ipcdev.cmd = cmd;
- if (ipcdev.irq_mode) {
- reinit_completion(&ipcdev.cmd_complete);
- cmd |= IPC_CMD_MSI;
- }
- writel(cmd, ipcdev.ipc_base + IPC_CMD);
-}
-
-static inline u32 ipc_read_status(void)
-{
- return readl(ipcdev.ipc_base + IPC_STATUS);
-}
-
-static inline void ipc_data_writel(u32 data, u32 offset)
-{
- writel(data, ipcdev.ipc_base + IPC_WRITE_BUFFER + offset);
-}
-
-static inline u8 __maybe_unused ipc_data_readb(u32 offset)
-{
- return readb(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
-}
-
-static inline u32 ipc_data_readl(u32 offset)
-{
- return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
-}
-
-
/**
* intel_pmc_gcr_read() - Read PMC GCR register
* @offset: offset of GCR register from GCR address base
@@ -264,160 +220,109 @@ static int update_no_reboot_bit(void *priv, bool set)
PMC_CFG_NO_REBOOT_MASK, value);
}
-static int intel_pmc_ipc_check_status(void)
+static int pre_simple_cmd_fn(u32 *cmd_list, u32 cmdlen)
{
- int status;
- int ret = 0;
-
- if (ipcdev.irq_mode) {
- if (0 == wait_for_completion_timeout(
- &ipcdev.cmd_complete, IPC_MAX_SEC * HZ))
- ret = -ETIMEDOUT;
- } else {
- int loop_count = IPC_LOOP_CNT;
-
- while ((ipc_read_status() & IPC_STATUS_BUSY) && --loop_count)
- udelay(1);
- if (loop_count == 0)
- ret = -ETIMEDOUT;
- }
-
- status = ipc_read_status();
- if (ret == -ETIMEDOUT) {
- dev_err(ipcdev.dev,
- "IPC timed out, TS=0x%x, CMD=0x%x\n",
- status, ipcdev.cmd);
- return ret;
- }
+ if (!cmd_list || cmdlen != PMC_PARAM_LEN)
+ return -EINVAL;
- if (status & IPC_STATUS_ERR) {
- int i;
-
- ret = -EIO;
- i = (status >> IPC_CMD_SIZE) & 0xFF;
- if (i < ARRAY_SIZE(ipc_err_sources))
- dev_err(ipcdev.dev,
- "IPC failed: %s, STS=0x%x, CMD=0x%x\n",
- ipc_err_sources[i], status, ipcdev.cmd);
- else
- dev_err(ipcdev.dev,
- "IPC failed: unknown, STS=0x%x, CMD=0x%x\n",
- status, ipcdev.cmd);
- if ((i == IPC_ERR_UNSIGNEDKERNEL) || (i == IPC_ERR_EMSECURITY))
- ret = -EACCES;
- }
+ cmd_list[0] |= (cmd_list[1] << IPC_CMD_SUBCMD);
- return ret;
+ return 0;
}
-/**
- * intel_pmc_ipc_simple_command() - Simple IPC command
- * @cmd: IPC command code.
- * @sub: IPC command sub type.
- *
- * Send a simple IPC command to PMC when don't need to specify
- * input/output data and source/dest pointers.
- *
- * Return: an IPC error code or 0 on success.
- */
-int intel_pmc_ipc_simple_command(int cmd, int sub)
+static int pre_raw_cmd_fn(struct intel_ipc_raw_cmd *ipc_raw_cmd)
{
int ret;
- mutex_lock(&ipclock);
- if (ipcdev.dev == NULL) {
- mutex_unlock(&ipclock);
- return -ENODEV;
- }
- ipc_send_command(sub << IPC_CMD_SUBCMD | cmd);
- ret = intel_pmc_ipc_check_status();
- mutex_unlock(&ipclock);
+ if (ipc_raw_cmd->inlen > IPC_DATA_BUFFER_SIZE ||
+ ipc_raw_cmd->outlen > (IPC_DATA_BUFFER_SIZE / 4))
+ return -EINVAL;
- return ret;
-}
-EXPORT_SYMBOL_GPL(intel_pmc_ipc_simple_command);
+ ret = pre_simple_cmd_fn(ipc_raw_cmd->cmd_list, ipc_raw_cmd->cmdlen);
+ if (ret < 0)
+ return ret;
-/**
- * intel_pmc_ipc_raw_cmd() - IPC command with data and pointers
- * @cmd: IPC command code.
- * @sub: IPC command sub type.
- * @in: input data of this IPC command.
- * @inlen: input data length in bytes.
- * @out: output data of this IPC command.
- * @outlen: output data length in dwords.
- * @sptr: data writing to SPTR register.
- * @dptr: data writing to DPTR register.
- *
- * Send an IPC command to PMC with input/output data and source/dest pointers.
- *
- * Return: an IPC error code or 0 on success.
- */
-int intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen, u32 *out,
- u32 outlen, u32 dptr, u32 sptr)
-{
- u32 wbuf[4] = { 0 };
- int ret;
- int i;
+ ipc_raw_cmd->cmd_list[0] |= (ipc_raw_cmd->inlen << IPC_CMD_SIZE);
- if (inlen > IPC_DATA_BUFFER_SIZE || outlen > IPC_DATA_BUFFER_SIZE / 4)
- return -EINVAL;
+ return 0;
+}
- mutex_lock(&ipclock);
- if (ipcdev.dev == NULL) {
- mutex_unlock(&ipclock);
- return -ENODEV;
- }
- memcpy(wbuf, in, inlen);
- writel(dptr, ipcdev.ipc_base + IPC_DPTR);
- writel(sptr, ipcdev.ipc_base + IPC_SPTR);
- /* The input data register is 32bit register and inlen is in Byte */
- for (i = 0; i < ((inlen + 3) / 4); i++)
- ipc_data_writel(wbuf[i], 4 * i);
- ipc_send_command((inlen << IPC_CMD_SIZE) |
- (sub << IPC_CMD_SUBCMD) | cmd);
- ret = intel_pmc_ipc_check_status();
- if (!ret) {
- /* out is read from 32bit register and outlen is in 32bit */
- for (i = 0; i < outlen; i++)
- *out++ = ipc_data_readl(4 * i);
- }
- mutex_unlock(&ipclock);
+static int pre_irq_handler_fn(struct intel_ipc_dev *ipc_dev, int irq)
+{
+ return regmap_write_bits(ipc_dev->cfg->cmd_regs,
+ ipc_dev->cfg->status_reg,
+ IPC_DEV_PMC_CMD_STATUS_IRQ,
+ IPC_DEV_PMC_CMD_STATUS_IRQ);
+}
- return ret;
+static int pmc_ipc_err_code(int status)
+{
+ return ((status >> IPC_DEV_PMC_CMD_SIZE) &
+ IPC_DEV_PMC_CMD_STATUS_ERR_MASK);
}
-EXPORT_SYMBOL_GPL(intel_pmc_ipc_raw_cmd);
-/**
- * intel_pmc_ipc_command() - IPC command with input/output data
- * @cmd: IPC command code.
- * @sub: IPC command sub type.
- * @in: input data of this IPC command.
- * @inlen: input data length in bytes.
- * @out: output data of this IPC command.
- * @outlen: output data length in dwords.
- *
- * Send an IPC command to PMC with input/output data.
- *
- * Return: an IPC error code or 0 on success.
- */
-int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
- u32 *out, u32 outlen)
+static int pmc_ipc_busy_check(int status)
{
- return intel_pmc_ipc_raw_cmd(cmd, sub, in, inlen, out, outlen, 0, 0);
+ return status | IPC_DEV_PMC_CMD_STATUS_BUSY;
}
-EXPORT_SYMBOL_GPL(intel_pmc_ipc_command);
-static irqreturn_t ioc(int irq, void *dev_id)
+static u32 pmc_ipc_enable_msi(u32 cmd)
{
- int status;
+ return cmd | IPC_DEV_PMC_CMD_MSI;
+}
- if (ipcdev.irq_mode) {
- status = ipc_read_status();
- writel(status | IPC_STATUS_IRQ, ipcdev.ipc_base + IPC_STATUS);
+static struct intel_ipc_dev *intel_pmc_ipc_dev_create(
+ struct device *pmc_dev,
+ void __iomem *base,
+ int irq)
+{
+ struct intel_ipc_dev_ops *ops;
+ struct intel_ipc_dev_cfg *cfg;
+ struct regmap *cmd_regs;
+
+ cfg = devm_kzalloc(pmc_dev, sizeof(*cfg), GFP_KERNEL);
+ if (!cfg)
+ return ERR_PTR(-ENOMEM);
+
+ ops = devm_kzalloc(pmc_dev, sizeof(*ops), GFP_KERNEL);
+ if (!ops)
+ return ERR_PTR(-ENOMEM);
+
+ cmd_regs = devm_regmap_init_mmio_clk(pmc_dev, NULL, base,
+ &pmc_regmap_config);
+ if (IS_ERR(cmd_regs)) {
+ dev_err(pmc_dev, "cmd_regs regmap init failed\n");
+ return ERR_CAST(cmd_regs);
}
- complete(&ipcdev.cmd_complete);
- return IRQ_HANDLED;
+ /* set IPC dev ops */
+ ops->to_err_code = pmc_ipc_err_code;
+ ops->busy_check = pmc_ipc_busy_check;
+ ops->enable_msi = pmc_ipc_enable_msi;
+ ops->pre_raw_cmd_fn = pre_raw_cmd_fn;
+ ops->pre_simple_cmd_fn = pre_simple_cmd_fn;
+ ops->pre_irq_handler_fn = pre_irq_handler_fn;
+
+ /* set cfg options */
+ if (irq > 0)
+ cfg->mode = IPC_DEV_MODE_IRQ;
+ else
+ cfg->mode = IPC_DEV_MODE_POLLING;
+
+ cfg->chan_type = IPC_CHANNEL_IA_PMC;
+ cfg->irq = irq;
+ cfg->use_msi = true;
+ cfg->support_sptr = true;
+ cfg->support_dptr = true;
+ cfg->cmd_regs = cmd_regs;
+ cfg->data_regs = cmd_regs;
+ cfg->wrbuf_reg = IPC_DEV_PMC_WRBUF_OFFSET;
+ cfg->rbuf_reg = IPC_DEV_PMC_RBUF_OFFSET;
+ cfg->sptr_reg = IPC_DEV_PMC_SPTR_OFFSET;
+ cfg->dptr_reg = IPC_DEV_PMC_DPTR_OFFSET;
+ cfg->status_reg = IPC_DEV_PMC_STATUS_OFFSET;
+
+ return devm_intel_ipc_dev_create(pmc_dev, INTEL_PMC_IPC_DEV, cfg, ops);
}
static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
@@ -429,8 +334,6 @@ static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (pmc->dev)
return -EBUSY;
- pmc->irq_mode = IPC_TRIGGER_MODE_IRQ;
-
ret = pcim_enable_device(pdev);
if (ret)
return ret;
@@ -439,15 +342,13 @@ static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (ret)
return ret;
- init_completion(&pmc->cmd_complete);
-
pmc->ipc_base = pcim_iomap_table(pdev)[0];
- ret = devm_request_irq(&pdev->dev, pdev->irq, ioc, 0, "intel_pmc_ipc",
- pmc);
- if (ret) {
- dev_err(&pdev->dev, "Failed to request IRQ\n");
- return ret;
+ pmc->pmc_ipc_dev = intel_pmc_ipc_dev_create(&pdev->dev, pmc->ipc_base,
+ pdev->irq);
+ if (IS_ERR(pmc->pmc_ipc_dev)) {
+ dev_err(&pdev->dev, "Failed to create PMC IPC device\n");
+ return PTR_ERR(pmc->pmc_ipc_dev);
}
pmc->dev = &pdev->dev;
@@ -475,19 +376,19 @@ static ssize_t intel_pmc_ipc_simple_cmd_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- int subcmd;
- int cmd;
+ struct intel_pmc_ipc_dev *pmc = dev_get_drvdata(dev);
+ int cmd[2];
int ret;
- ret = sscanf(buf, "%d %d", &cmd, &subcmd);
+ ret = sscanf(buf, "%d %d", &cmd[0], &cmd[2]);
if (ret != 2) {
dev_err(dev, "Error args\n");
return -EINVAL;
}
- ret = intel_pmc_ipc_simple_command(cmd, subcmd);
+ ret = ipc_dev_simple_cmd(pmc->pmc_ipc_dev, cmd, 2);
if (ret) {
- dev_err(dev, "command %d error with %d\n", cmd, ret);
+ dev_err(dev, "command %d error with %d\n", cmd[0], ret);
return ret;
}
return (ssize_t)count;
@@ -497,22 +398,23 @@ static ssize_t intel_pmc_ipc_northpeak_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
+ struct intel_pmc_ipc_dev *pmc = dev_get_drvdata(dev);
unsigned long val;
- int subcmd;
+ int cmd[2] = {PMC_IPC_NORTHPEAK_CTRL, 0};
int ret;
if (kstrtoul(buf, 0, &val))
return -EINVAL;
if (val)
- subcmd = 1;
- else
- subcmd = 0;
- ret = intel_pmc_ipc_simple_command(PMC_IPC_NORTHPEAK_CTRL, subcmd);
+ cmd[1] = 1;
+
+ ret = ipc_dev_simple_cmd(pmc->pmc_ipc_dev, cmd, 2);
if (ret) {
- dev_err(dev, "command north %d error with %d\n", subcmd, ret);
+ dev_err(dev, "command north %d error with %d\n", cmd[1], ret);
return ret;
}
+
return (ssize_t)count;
}
@@ -688,6 +590,7 @@ static int ipc_create_pmc_devices(struct platform_device *pdev)
static int ipc_plat_get_res(struct platform_device *pdev)
{
+ struct intel_pmc_ipc_dev *pmc = dev_get_drvdata(&pdev->dev);
struct resource *res;
void __iomem *addr;
@@ -706,9 +609,9 @@ static int ipc_plat_get_res(struct platform_device *pdev)
if (IS_ERR(addr))
return PTR_ERR(addr);
- ipcdev.ipc_base = addr;
- ipcdev.gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET;
- dev_dbg(&pdev->dev, "PMC IPC resource %pR\n", res);
+ pmc->ipc_base = addr;
+ pmc->gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET;
+ dev_info(&pdev->dev, "PMC IPC resource %pR\n", res);
return 0;
}
@@ -754,14 +657,15 @@ MODULE_DEVICE_TABLE(acpi, ipc_acpi_ids);
static int ipc_plat_probe(struct platform_device *pdev)
{
- int ret;
+ int ret, irq;
+ struct intel_pmc_ipc_dev *pmc = &ipcdev;
- ipcdev.dev = &pdev->dev;
- ipcdev.irq_mode = IPC_TRIGGER_MODE_IRQ;
- init_completion(&ipcdev.cmd_complete);
+ pmc->dev = &pdev->dev;
- ipcdev.irq = platform_get_irq(pdev, 0);
- if (ipcdev.irq < 0) {
+ dev_set_drvdata(&pdev->dev, pmc);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
dev_err(&pdev->dev, "Failed to get IRQ\n");
return -EINVAL;
}
@@ -786,28 +690,26 @@ static int ipc_plat_probe(struct platform_device *pdev)
return ret;
}
- ret = devm_request_irq(&pdev->dev, ipcdev.irq, ioc, IRQF_NO_SUSPEND,
- "intel_pmc_ipc", &ipcdev);
- if (ret) {
- dev_err(&pdev->dev, "Failed to request IRQ\n");
- return ret;
- }
-
ret = sysfs_create_group(&pdev->dev.kobj, &intel_ipc_group);
if (ret) {
dev_err(&pdev->dev, "Failed to create sysfs group %d\n",
ret);
- devm_free_irq(&pdev->dev, ipcdev.irq, &ipcdev);
return ret;
}
+ ipcdev.pmc_ipc_dev = intel_pmc_ipc_dev_create(&pdev->dev,
+ pmc->ipc_base, irq);
+ if (IS_ERR(pmc->pmc_ipc_dev)) {
+ dev_err(&pdev->dev, "Failed to create PMC IPC device\n");
+ return PTR_ERR(pmc->pmc_ipc_dev);
+ }
+
return 0;
}
static int ipc_plat_remove(struct platform_device *pdev)
{
sysfs_remove_group(&pdev->dev.kobj, &intel_ipc_group);
- devm_free_irq(&pdev->dev, ipcdev.irq, &ipcdev);
ipcdev.dev = NULL;
return 0;
diff --git a/drivers/platform/x86/intel_telemetry_pltdrv.c b/drivers/platform/x86/intel_telemetry_pltdrv.c
index 0dbd7be..74868bb 100644
--- a/drivers/platform/x86/intel_telemetry_pltdrv.c
+++ b/drivers/platform/x86/intel_telemetry_pltdrv.c
@@ -56,10 +56,6 @@
#define IOSS_TELEM_TRACE_CTL_WRITE 0x6
#define IOSS_TELEM_EVENT_CTL_READ 0x7
#define IOSS_TELEM_EVENT_CTL_WRITE 0x8
-#define IOSS_TELEM_EVT_CTRL_WRITE_SIZE 0x4
-#define IOSS_TELEM_READ_WORD 0x1
-#define IOSS_TELEM_WRITE_FOURBYTES 0x4
-#define IOSS_TELEM_EVT_WRITE_SIZE 0x3
#define TELEM_INFO_SRAMEVTS_MASK 0xFF00
#define TELEM_INFO_SRAMEVTS_SHIFT 0x8
@@ -98,7 +94,7 @@ struct telem_ssram_region {
};
static struct telemetry_plt_config *telm_conf;
-static struct intel_ipc_dev *punit_ipc_dev;
+static struct intel_ipc_dev *punit_ipc_dev, *pmc_ipc_dev;
/*
* The following counters are programmed by default during setup.
@@ -226,6 +222,29 @@ static int telem_punit_cmd(struct intel_ipc_dev *ipc_dev, u32 cmd, u32 sub,
return ipc_dev_cmd(ipc_dev, &ipc_cmd);
}
+static int telem_pmc_cmd(struct intel_ipc_dev *ipc_dev, u32 cmd, u32 sub,
+ u8 *in, u32 *out)
+{
+ struct intel_ipc_raw_cmd ipc_raw_cmd = {0};
+ u32 cmd_list[PMC_PARAM_LEN] = {0};
+
+ cmd_list[0] = cmd;
+ cmd_list[1] = sub;
+
+ ipc_raw_cmd.cmd_list = cmd_list;
+ ipc_raw_cmd.cmdlen = PMC_PARAM_LEN;
+ ipc_raw_cmd.in = in;
+ ipc_raw_cmd.out = out;
+
+ if (in)
+ ipc_raw_cmd.inlen = 4;
+ if (out)
+ ipc_raw_cmd.outlen = 1;
+
+ return ipc_dev_raw_cmd(ipc_dev, &ipc_raw_cmd);
+}
+
+
static inline int telem_get_unitconfig(enum telemetry_unit telem_unit,
struct telemetry_unit_config **unit_config)
{
@@ -289,17 +308,13 @@ static int telemetry_check_evtid(enum telemetry_unit telem_unit,
static inline int telemetry_plt_config_ioss_event(u32 evt_id, int index)
{
u32 write_buf;
- int ret;
write_buf = evt_id | TELEM_EVENT_ENABLE;
write_buf <<= BITS_PER_BYTE;
write_buf |= index;
- ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
- IOSS_TELEM_EVENT_WRITE, (u8 *)&write_buf,
- IOSS_TELEM_EVT_WRITE_SIZE, NULL, 0);
-
- return ret;
+ return telem_pmc_cmd(pmc_ipc_dev, PMC_IPC_PMC_TELEMTRY,
+ IOSS_TELEM_EVENT_WRITE, (u8 *)&write_buf, NULL);
}
static inline int telemetry_plt_config_pss_event(u32 evt_id, int index)
@@ -325,9 +340,8 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
ioss_evtmap = evtconfig.evtmap;
/* Get telemetry EVENT CTL */
- ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
- IOSS_TELEM_EVENT_CTL_READ, NULL, 0,
- &telem_ctrl, IOSS_TELEM_READ_WORD);
+ ret = telem_pmc_cmd(pmc_ipc_dev, PMC_IPC_PMC_TELEMTRY,
+ IOSS_TELEM_EVENT_CTL_READ, NULL, &telem_ctrl);
if (ret) {
pr_err("IOSS TELEM_CTRL Read Failed\n");
return ret;
@@ -335,12 +349,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
/* Disable Telemetry */
TELEM_DISABLE(telem_ctrl);
-
- ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
- IOSS_TELEM_EVENT_CTL_WRITE,
- (u8 *)&telem_ctrl,
- IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
- NULL, 0);
+ ret = telem_pmc_cmd(pmc_ipc_dev, PMC_IPC_PMC_TELEMTRY,
+ IOSS_TELEM_EVENT_CTL_WRITE, (u8 *)&telem_ctrl,
+ NULL);
if (ret) {
pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
return ret;
@@ -351,12 +362,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
if (action == TELEM_RESET) {
/* Clear All Events */
TELEM_CLEAR_EVENTS(telem_ctrl);
-
- ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
- IOSS_TELEM_EVENT_CTL_WRITE,
- (u8 *)&telem_ctrl,
- IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
- NULL, 0);
+ ret = telem_pmc_cmd(pmc_ipc_dev, PMC_IPC_PMC_TELEMTRY,
+ IOSS_TELEM_EVENT_CTL_WRITE,
+ (u8 *)&telem_ctrl, NULL);
if (ret) {
pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
return ret;
@@ -380,12 +388,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
if (action == TELEM_UPDATE) {
/* Clear All Events */
TELEM_CLEAR_EVENTS(telem_ctrl);
-
- ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
- IOSS_TELEM_EVENT_CTL_WRITE,
- (u8 *)&telem_ctrl,
- IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
- NULL, 0);
+ ret = telem_pmc_cmd(pmc_ipc_dev, PMC_IPC_PMC_TELEMTRY,
+ IOSS_TELEM_EVENT_CTL_WRITE,
+ (u8 *)&telem_ctrl, NULL);
if (ret) {
pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
return ret;
@@ -432,11 +437,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
TELEM_ENABLE_SRAM_EVT_TRACE(telem_ctrl);
TELEM_ENABLE_PERIODIC(telem_ctrl);
telem_ctrl |= ioss_period;
-
- ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
- IOSS_TELEM_EVENT_CTL_WRITE,
- (u8 *)&telem_ctrl,
- IOSS_TELEM_EVT_CTRL_WRITE_SIZE, NULL, 0);
+ ret = telem_pmc_cmd(pmc_ipc_dev, PMC_IPC_PMC_TELEMTRY,
+ IOSS_TELEM_EVENT_CTL_WRITE, (u8 *)&telem_ctrl,
+ NULL);
if (ret) {
pr_err("IOSS TELEM_CTRL Event Enable Write Failed\n");
return ret;
@@ -626,8 +629,8 @@ static int telemetry_setup(struct platform_device *pdev)
u32 read_buf, events, event_regs;
int ret;
- ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY, IOSS_TELEM_INFO_READ,
- NULL, 0, &read_buf, IOSS_TELEM_READ_WORD);
+ ret = telem_pmc_cmd(pmc_ipc_dev, PMC_IPC_PMC_TELEMTRY,
+ IOSS_TELEM_INFO_READ, NULL, &read_buf);
if (ret) {
dev_err(&pdev->dev, "IOSS TELEM_INFO Read Failed\n");
return ret;
@@ -728,9 +731,9 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
}
/* Get telemetry EVENT CTL */
- ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
- IOSS_TELEM_EVENT_CTL_READ, NULL, 0,
- &telem_ctrl, IOSS_TELEM_READ_WORD);
+ ret = telem_pmc_cmd(pmc_ipc_dev, PMC_IPC_PMC_TELEMTRY,
+ IOSS_TELEM_EVENT_CTL_READ, NULL,
+ &telem_ctrl);
if (ret) {
pr_err("IOSS TELEM_CTRL Read Failed\n");
goto out;
@@ -738,12 +741,9 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
/* Disable Telemetry */
TELEM_DISABLE(telem_ctrl);
-
- ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
- IOSS_TELEM_EVENT_CTL_WRITE,
- (u8 *)&telem_ctrl,
- IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
- NULL, 0);
+ ret = telem_pmc_cmd(pmc_ipc_dev, PMC_IPC_PMC_TELEMTRY,
+ IOSS_TELEM_EVENT_CTL_WRITE,
+ (u8 *)&telem_ctrl, NULL);
if (ret) {
pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
goto out;
@@ -755,11 +755,9 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
TELEM_ENABLE_PERIODIC(telem_ctrl);
telem_ctrl |= ioss_period;
- ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
- IOSS_TELEM_EVENT_CTL_WRITE,
- (u8 *)&telem_ctrl,
- IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
- NULL, 0);
+ ret = telem_pmc_cmd(pmc_ipc_dev, PMC_IPC_PMC_TELEMTRY,
+ IOSS_TELEM_EVENT_CTL_WRITE,
+ (u8 *)&telem_ctrl, NULL);
if (ret) {
pr_err("IOSS TELEM_CTRL Event Enable Write Failed\n");
goto out;
@@ -1054,9 +1052,9 @@ static int telemetry_plt_get_trace_verbosity(enum telemetry_unit telem_unit,
break;
case TELEM_IOSS:
- ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
- IOSS_TELEM_TRACE_CTL_READ, NULL, 0, &temp,
- IOSS_TELEM_READ_WORD);
+ ret = telem_pmc_cmd(pmc_ipc_dev, PMC_IPC_PMC_TELEMTRY,
+ IOSS_TELEM_TRACE_CTL_READ,
+ NULL, &temp);
if (ret) {
pr_err("IOSS TRACE_CTL Read Failed\n");
goto out;
@@ -1107,9 +1105,9 @@ static int telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
break;
case TELEM_IOSS:
- ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
- IOSS_TELEM_TRACE_CTL_READ, NULL, 0, &temp,
- IOSS_TELEM_READ_WORD);
+ ret = telem_pmc_cmd(pmc_ipc_dev, PMC_IPC_PMC_TELEMTRY,
+ IOSS_TELEM_TRACE_CTL_READ,
+ NULL, &temp);
if (ret) {
pr_err("IOSS TRACE_CTL Read Failed\n");
goto out;
@@ -1117,10 +1115,9 @@ static int telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
TELEM_CLEAR_VERBOSITY_BITS(temp);
TELEM_SET_VERBOSITY_BITS(temp, verbosity);
-
- ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
- IOSS_TELEM_TRACE_CTL_WRITE, (u8 *)&temp,
- IOSS_TELEM_WRITE_FOURBYTES, NULL, 0);
+ ret = telem_pmc_cmd(pmc_ipc_dev, PMC_IPC_PMC_TELEMTRY,
+ IOSS_TELEM_TRACE_CTL_WRITE,
+ (u8 *)&temp, NULL);
if (ret) {
pr_err("IOSS TRACE_CTL Verbosity Set Failed\n");
goto out;
@@ -1165,6 +1162,10 @@ static int telemetry_pltdrv_probe(struct platform_device *pdev)
if (IS_ERR_OR_NULL(punit_ipc_dev))
return PTR_ERR(punit_ipc_dev);
+ pmc_ipc_dev = intel_ipc_dev_get(INTEL_PMC_IPC_DEV);
+ if (IS_ERR_OR_NULL(pmc_ipc_dev))
+ return PTR_ERR(pmc_ipc_dev);
+
telm_conf = (struct telemetry_plt_config *)id->driver_data;
res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1244,6 +1245,7 @@ static int telemetry_pltdrv_probe(struct platform_device *pdev)
static int telemetry_pltdrv_remove(struct platform_device *pdev)
{
telemetry_clear_pltdata();
+ intel_ipc_dev_put(pmc_ipc_dev);
intel_ipc_dev_put(punit_ipc_dev);
iounmap(telm_conf->pss_config.regmap);
iounmap(telm_conf->ioss_config.regmap);
diff --git a/include/linux/mfd/intel_soc_pmic.h b/include/linux/mfd/intel_soc_pmic.h
index 5aacdb0..7cc39b6 100644
--- a/include/linux/mfd/intel_soc_pmic.h
+++ b/include/linux/mfd/intel_soc_pmic.h
@@ -20,6 +20,7 @@
#define __INTEL_SOC_PMIC_H__
#include <linux/regmap.h>
+#include <linux/platform_data/x86/intel_ipc_dev.h>
struct intel_soc_pmic {
int irq;
@@ -31,6 +32,7 @@ struct intel_soc_pmic {
struct regmap_irq_chip_data *irq_chip_data_chgr;
struct regmap_irq_chip_data *irq_chip_data_crit;
struct device *dev;
+ struct intel_ipc_dev *ipc_dev;
};
#endif /* __INTEL_SOC_PMIC_H__ */
--
2.7.4
^ permalink raw reply related
* [RFC v8 3/7] platform/x86: intel_pmc_ipc: Use regmap calls for GCR updates
From: sathyanarayanan.kuppuswamy @ 2017-10-29 9:49 UTC (permalink / raw)
To: a.zummo, x86, wim, mingo, alexandre.belloni, qipeng.zha, hpa,
dvhart, tglx, lee.jones, andy, souvik.k.chakravarty
Cc: linux-rtc, linux-watchdog, linux-kernel, platform-driver-x86,
sathyaosid, Kuppuswamy Sathyanarayanan
In-Reply-To: <cover.1509268570.git.sathyanarayanan.kuppuswamy@linux.intel.com>
From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
This patch adds support for regmap based implementation for GCR
read/write/update APIs.
Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
---
drivers/platform/x86/Kconfig | 1 +
drivers/platform/x86/intel_pmc_ipc.c | 122 +++++++++++++----------------------
2 files changed, 46 insertions(+), 77 deletions(-)
Changes since v7:
* Fixed style issues.
Changes since v6:
* None
Changes since v5:
* None
Changes since v4:
* None
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 80b8795..45f4e79 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1054,6 +1054,7 @@ config PVPANIC
config INTEL_PMC_IPC
tristate "Intel PMC IPC Driver"
depends on ACPI
+ select REGMAP_MMIO
---help---
This driver provides support for PMC control on some Intel platforms.
The PMC is an ARC processor which defines IPC commands for communication
diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c
index e36144c..df6af1f 100644
--- a/drivers/platform/x86/intel_pmc_ipc.c
+++ b/drivers/platform/x86/intel_pmc_ipc.c
@@ -35,6 +35,8 @@
#include <linux/acpi.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/spinlock.h>
+#include <linux/mfd/core.h>
+#include <linux/regmap.h>
#include <asm/intel_pmc_ipc.h>
@@ -126,8 +128,7 @@ static struct intel_pmc_ipc_dev {
/* gcr */
void __iomem *gcr_mem_base;
- bool has_gcr_regs;
- spinlock_t gcr_lock;
+ struct regmap *gcr_regs;
} ipcdev;
static char *ipc_err_sources[] = {
@@ -149,6 +150,15 @@ static char *ipc_err_sources[] = {
"Unsigned kernel",
};
+static struct regmap_config gcr_regmap_config = {
+ .name = "intel_pmc_gcr",
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .fast_io = true,
+ .max_register = PLAT_RESOURCE_GCR_SIZE,
+};
+
/* Prevent concurrent calls to the PMC */
static DEFINE_MUTEX(ipclock);
@@ -182,21 +192,6 @@ static inline u32 ipc_data_readl(u32 offset)
return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
}
-static inline u64 gcr_data_readq(u32 offset)
-{
- return readq(ipcdev.gcr_mem_base + offset);
-}
-
-static inline int is_gcr_valid(u32 offset)
-{
- if (!ipcdev.has_gcr_regs)
- return -EACCES;
-
- if (offset > PLAT_RESOURCE_GCR_SIZE)
- return -EINVAL;
-
- return 0;
-}
/**
* intel_pmc_gcr_read() - Read PMC GCR register
@@ -209,21 +204,12 @@ static inline int is_gcr_valid(u32 offset)
*/
int intel_pmc_gcr_read(u32 offset, u32 *data)
{
- int ret;
-
- spin_lock(&ipcdev.gcr_lock);
-
- ret = is_gcr_valid(offset);
- if (ret < 0) {
- spin_unlock(&ipcdev.gcr_lock);
- return ret;
- }
-
- *data = readl(ipcdev.gcr_mem_base + offset);
+ struct intel_pmc_ipc_dev *pmc = &ipcdev;
- spin_unlock(&ipcdev.gcr_lock);
+ if (!pmc->gcr_regs)
+ return -EACCES;
- return 0;
+ return regmap_read(pmc->gcr_regs, offset, data);
}
EXPORT_SYMBOL_GPL(intel_pmc_gcr_read);
@@ -239,21 +225,12 @@ EXPORT_SYMBOL_GPL(intel_pmc_gcr_read);
*/
int intel_pmc_gcr_write(u32 offset, u32 data)
{
- int ret;
-
- spin_lock(&ipcdev.gcr_lock);
-
- ret = is_gcr_valid(offset);
- if (ret < 0) {
- spin_unlock(&ipcdev.gcr_lock);
- return ret;
- }
-
- writel(data, ipcdev.gcr_mem_base + offset);
+ struct intel_pmc_ipc_dev *pmc = &ipcdev;
- spin_unlock(&ipcdev.gcr_lock);
+ if (!pmc->gcr_regs)
+ return -EACCES;
- return 0;
+ return regmap_write(pmc->gcr_regs, offset, data);
}
EXPORT_SYMBOL_GPL(intel_pmc_gcr_write);
@@ -270,33 +247,12 @@ EXPORT_SYMBOL_GPL(intel_pmc_gcr_write);
*/
int intel_pmc_gcr_update(u32 offset, u32 mask, u32 val)
{
- u32 new_val;
- int ret = 0;
-
- spin_lock(&ipcdev.gcr_lock);
-
- ret = is_gcr_valid(offset);
- if (ret < 0)
- goto gcr_ipc_unlock;
-
- new_val = readl(ipcdev.gcr_mem_base + offset);
-
- new_val &= ~mask;
- new_val |= val & mask;
-
- writel(new_val, ipcdev.gcr_mem_base + offset);
-
- new_val = readl(ipcdev.gcr_mem_base + offset);
+ struct intel_pmc_ipc_dev *pmc = &ipcdev;
- /* check whether the bit update is successful */
- if ((new_val & mask) != (val & mask)) {
- ret = -EIO;
- goto gcr_ipc_unlock;
- }
+ if (!pmc->gcr_regs)
+ return -EACCES;
-gcr_ipc_unlock:
- spin_unlock(&ipcdev.gcr_lock);
- return ret;
+ return regmap_update_bits(pmc->gcr_regs, offset, mask, val);
}
EXPORT_SYMBOL_GPL(intel_pmc_gcr_update);
@@ -475,8 +431,6 @@ static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
pmc->irq_mode = IPC_TRIGGER_MODE_IRQ;
- spin_lock_init(&ipcdev.gcr_lock);
-
ret = pcim_enable_device(pdev);
if (ret)
return ret;
@@ -767,17 +721,26 @@ static int ipc_plat_get_res(struct platform_device *pdev)
*/
int intel_pmc_s0ix_counter_read(u64 *data)
{
+ struct intel_pmc_ipc_dev *pmc = &ipcdev;
u64 deep, shlw;
+ int ret;
- if (!ipcdev.has_gcr_regs)
+ if (!pmc->gcr_regs)
return -EACCES;
- deep = gcr_data_readq(PMC_GCR_TELEM_DEEP_S0IX_REG);
- shlw = gcr_data_readq(PMC_GCR_TELEM_SHLW_S0IX_REG);
+ ret = regmap_bulk_read(pmc->gcr_regs, PMC_GCR_TELEM_DEEP_S0IX_REG,
+ &deep, 2);
+ if (ret)
+ return ret;
+
+ ret = regmap_bulk_read(pmc->gcr_regs, PMC_GCR_TELEM_SHLW_S0IX_REG,
+ &shlw, 2);
+ if (ret)
+ return ret;
*data = S0IX_RESIDENCY_IN_USECS(deep, shlw);
- return 0;
+ return ret;
}
EXPORT_SYMBOL_GPL(intel_pmc_s0ix_counter_read);
@@ -796,7 +759,6 @@ static int ipc_plat_probe(struct platform_device *pdev)
ipcdev.dev = &pdev->dev;
ipcdev.irq_mode = IPC_TRIGGER_MODE_IRQ;
init_completion(&ipcdev.cmd_complete);
- spin_lock_init(&ipcdev.gcr_lock);
ipcdev.irq = platform_get_irq(pdev, 0);
if (ipcdev.irq < 0) {
@@ -810,6 +772,14 @@ static int ipc_plat_probe(struct platform_device *pdev)
return ret;
}
+ ipcdev.gcr_regs = devm_regmap_init_mmio_clk(ipcdev.dev, NULL,
+ ipcdev.gcr_mem_base,
+ &gcr_regmap_config);
+ if (IS_ERR(ipcdev.gcr_regs)) {
+ dev_err(ipcdev.dev, "gcr_regs regmap init failed\n");
+ return PTR_ERR(ipcdev.gcr_regs);
+ }
+
ret = ipc_create_pmc_devices(pdev);
if (ret) {
dev_err(&pdev->dev, "Failed to create PMC devices\n");
@@ -831,8 +801,6 @@ static int ipc_plat_probe(struct platform_device *pdev)
return ret;
}
- ipcdev.has_gcr_regs = true;
-
return 0;
}
--
2.7.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox