Linux RTC
 help / color / mirror / Atom feed
* Re: [RFC v3 1/7] platform/x86: intel_pmc_ipc: Use devm_* calls in driver probe function
From: Andy Shevchenko @ 2017-10-01 14:34 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
In-Reply-To: <eada94e0dfe0302b22c16c22ba2ea806e096144a.1504588701.git.sathyanarayanan.kuppuswamy@linux.intel.com>

On Tue, Sep 5, 2017 at 8:37 AM,
<sathyanarayanan.kuppuswamy@linux.intel.com> wrote:
> From: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
>
> This patch cleans up unnecessary free/alloc calls in ipc_plat_probe(),
> ipc_pci_probe() and ipc_plat_get_res() functions by using devm_*
> calls.
>
> This patch also adds proper error handling for failure cases in
> ipc_pci_probe() function.
>

Pushed (this patch only) to my review queue with slight style changes, thanks.

> Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
> ---
>  drivers/platform/x86/intel_pmc_ipc.c | 104 ++++++++++++-----------------------
>  1 file changed, 34 insertions(+), 70 deletions(-)
>
> Changes since v2:
>  * Used pcim_* device managed functions.
>
> Changes since v1:
>  * Merged devm_* related changes into a single function.
>  * Instead of removing free_irq, use devm_free_irq function.
>
> diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c
> index bb792a5..5eef649 100644
> --- a/drivers/platform/x86/intel_pmc_ipc.c
> +++ b/drivers/platform/x86/intel_pmc_ipc.c
> @@ -480,52 +480,39 @@ static irqreturn_t ioc(int irq, void *dev_id)
>
>  static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>  {
> -       resource_size_t pci_resource;
>         int ret;
> -       int len;
> +       struct intel_pmc_ipc_dev *pmc = &ipcdev;
>
> -       ipcdev.dev = &pci_dev_get(pdev)->dev;
> -       ipcdev.irq_mode = IPC_TRIGGER_MODE_IRQ;
> +       /* Only one PMC is supported */
> +       if (pmc->dev)
> +               return -EBUSY;
>
> -       ret = pci_enable_device(pdev);
> +       pmc->irq_mode = IPC_TRIGGER_MODE_IRQ;
> +
> +       ret = pcim_enable_device(pdev);
>         if (ret)
>                 return ret;
>
> -       ret = pci_request_regions(pdev, "intel_pmc_ipc");
> +       ret = pcim_iomap_regions(pdev, 1 << 0, pci_name(pdev));
>         if (ret)
>                 return ret;
>
> -       pci_resource = pci_resource_start(pdev, 0);
> -       len = pci_resource_len(pdev, 0);
> -       if (!pci_resource || !len) {
> -               dev_err(&pdev->dev, "Failed to get resource\n");
> -               return -ENOMEM;
> -       }
> +       init_completion(&pmc->cmd_complete);
>
> -       init_completion(&ipcdev.cmd_complete);
> +       pmc->ipc_base =  pcim_iomap_table(pdev)[0];
>
> -       if (request_irq(pdev->irq, ioc, 0, "intel_pmc_ipc", &ipcdev)) {
> +       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 -EBUSY;
> +               return ret;
>         }
>
> -       ipcdev.ipc_base = ioremap_nocache(pci_resource, len);
> -       if (!ipcdev.ipc_base) {
> -               dev_err(&pdev->dev, "Failed to ioremap ipc base\n");
> -               free_irq(pdev->irq, &ipcdev);
> -               ret = -ENOMEM;
> -       }
> +       pmc->dev = &pdev->dev;
>
> -       return ret;
> -}
> +       pci_set_drvdata(pdev, pmc);
>
> -static void ipc_pci_remove(struct pci_dev *pdev)
> -{
> -       free_irq(pdev->irq, &ipcdev);
> -       pci_release_regions(pdev);
> -       pci_dev_put(pdev);
> -       iounmap(ipcdev.ipc_base);
> -       ipcdev.dev = NULL;
> +       return 0;
>  }
>
>  static const struct pci_device_id ipc_pci_ids[] = {
> @@ -540,7 +527,6 @@ static struct pci_driver ipc_pci_driver = {
>         .name = "intel_pmc_ipc",
>         .id_table = ipc_pci_ids,
>         .probe = ipc_pci_probe,
> -       .remove = ipc_pci_remove,
>  };
>
>  static ssize_t intel_pmc_ipc_simple_cmd_store(struct device *dev,
> @@ -849,18 +835,16 @@ static int ipc_plat_get_res(struct platform_device *pdev)
>                 dev_err(&pdev->dev, "Failed to get ipc resource\n");
>                 return -ENXIO;
>         }
> -       size = PLAT_RESOURCE_IPC_SIZE + PLAT_RESOURCE_GCR_SIZE;
> +       res->end = (res->start + PLAT_RESOURCE_IPC_SIZE +
> +                   PLAT_RESOURCE_GCR_SIZE - 1);
>
> -       if (!request_mem_region(res->start, size, pdev->name)) {
> -               dev_err(&pdev->dev, "Failed to request ipc resource\n");
> -               return -EBUSY;
> -       }
> -       addr = ioremap_nocache(res->start, size);
> -       if (!addr) {
> -               dev_err(&pdev->dev, "I/O memory remapping failed\n");
> -               release_mem_region(res->start, size);
> -               return -ENOMEM;
> +       addr = devm_ioremap_resource(&pdev->dev, res);
> +       if (IS_ERR(addr)) {
> +               dev_err(&pdev->dev,
> +                       "PMC I/O memory remapping failed\n");
> +               return PTR_ERR(addr);
>         }
> +
>         ipcdev.ipc_base = addr;
>
>         ipcdev.gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET;
> @@ -917,7 +901,6 @@ MODULE_DEVICE_TABLE(acpi, ipc_acpi_ids);
>
>  static int ipc_plat_probe(struct platform_device *pdev)
>  {
> -       struct resource *res;
>         int ret;
>
>         ipcdev.dev = &pdev->dev;
> @@ -939,61 +922,42 @@ static int ipc_plat_probe(struct platform_device *pdev)
>         ret = ipc_create_pmc_devices();
>         if (ret) {
>                 dev_err(&pdev->dev, "Failed to create pmc devices\n");
> -               goto err_device;
> +               return ret;
>         }
>
> -       if (request_irq(ipcdev.irq, ioc, IRQF_NO_SUSPEND,
> -                       "intel_pmc_ipc", &ipcdev)) {
> +       if (devm_request_irq(&pdev->dev, ipcdev.irq, ioc, IRQF_NO_SUSPEND,
> +                            "intel_pmc_ipc", &ipcdev)) {
>                 dev_err(&pdev->dev, "Failed to request irq\n");
>                 ret = -EBUSY;
> -               goto err_irq;
> +               goto unregister_devices;
>         }
>
>         ret = sysfs_create_group(&pdev->dev.kobj, &intel_ipc_group);
>         if (ret) {
>                 dev_err(&pdev->dev, "Failed to create sysfs group %d\n",
>                         ret);
> -               goto err_sys;
> +               goto unregister_devices;
>         }
>
>         ipcdev.has_gcr_regs = true;
>
>         return 0;
> -err_sys:
> -       free_irq(ipcdev.irq, &ipcdev);
> -err_irq:
> +
> +unregister_devices:
>         platform_device_unregister(ipcdev.tco_dev);
>         platform_device_unregister(ipcdev.punit_dev);
>         platform_device_unregister(ipcdev.telemetry_dev);
> -err_device:
> -       iounmap(ipcdev.ipc_base);
> -       res = platform_get_resource(pdev, IORESOURCE_MEM,
> -                                   PLAT_RESOURCE_IPC_INDEX);
> -       if (res) {
> -               release_mem_region(res->start,
> -                                  PLAT_RESOURCE_IPC_SIZE +
> -                                  PLAT_RESOURCE_GCR_SIZE);
> -       }
> +
>         return ret;
>  }
>
>  static int ipc_plat_remove(struct platform_device *pdev)
>  {
> -       struct resource *res;
> -
>         sysfs_remove_group(&pdev->dev.kobj, &intel_ipc_group);
> -       free_irq(ipcdev.irq, &ipcdev);
> +       devm_free_irq(&pdev->dev, ipcdev.irq, &ipcdev);
>         platform_device_unregister(ipcdev.tco_dev);
>         platform_device_unregister(ipcdev.punit_dev);
>         platform_device_unregister(ipcdev.telemetry_dev);
> -       iounmap(ipcdev.ipc_base);
> -       res = platform_get_resource(pdev, IORESOURCE_MEM,
> -                                   PLAT_RESOURCE_IPC_INDEX);
> -       if (res) {
> -               release_mem_region(res->start,
> -                                  PLAT_RESOURCE_IPC_SIZE +
> -                                  PLAT_RESOURCE_GCR_SIZE);
> -       }
>         ipcdev.dev = NULL;
>         return 0;
>  }
> --
> 2.7.4
>



-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply

* [PATCH] rtc: armada38x: add support for trimming the RTC
From: Russell King @ 2017-09-29 10:23 UTC (permalink / raw)
  To: Alessandro Zummo
  Cc: Jason Cooper, Andrew Lunn, Gregory Clement, Sebastian Hesselbarth,
	Alexandre Belloni, linux-arm-kernel, linux-rtc

Add support for trimming the RTC using the offset mechanism.  This RTC
supports two modes: low update mode and high update mode.  Low update
mode has finer precision than high update mode, so we use the low mode
where possible.

Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
 drivers/rtc/rtc-armada38x.c | 101 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 101 insertions(+)

diff --git a/drivers/rtc/rtc-armada38x.c b/drivers/rtc/rtc-armada38x.c
index 21f355c37eab..1e4978c96ffd 100644
--- a/drivers/rtc/rtc-armada38x.c
+++ b/drivers/rtc/rtc-armada38x.c
@@ -28,6 +28,8 @@
 #define RTC_IRQ_AL_EN		    BIT(0)
 #define RTC_IRQ_FREQ_EN		    BIT(1)
 #define RTC_IRQ_FREQ_1HZ	    BIT(2)
+#define RTC_CCR		    0x18
+#define RTC_CCR_MODE		    BIT(15)
 
 #define RTC_TIME	    0xC
 #define RTC_ALARM1	    0x10
@@ -343,18 +345,117 @@ static irqreturn_t armada38x_rtc_alarm_irq(int irq, void *data)
 	return IRQ_HANDLED;
 }
 
+/*
+ * The information given in the Armada 388 functional spec is complex.
+ * They give two different formulas for calculating the offset value,
+ * but when considering "Offset" as an 8-bit signed integer, they both
+ * reduce down to (we shall rename "Offset" as "val" here):
+ *
+ *   val = (f_ideal / f_measured - 1) / resolution   where f_ideal = 32768
+ *
+ * Converting to time, f = 1/t:
+ *   val = (t_measured / t_ideal - 1) / resolution   where t_ideal = 1/32768
+ *
+ *   =>  t_measured / t_ideal = val * resolution + 1
+ *
+ * "offset" in the RTC interface is defined as:
+ *   t = t0 * (1 + offset * 1e-9)
+ * where t is the desired period, t0 is the measured period with a zero
+ * offset, which is t_measured above. With t0 = t_measured and t = t_ideal,
+ *   offset = (t_ideal / t_measured - 1) / 1e-9
+ *
+ *   => t_ideal / t_measured = offset * 1e-9 + 1
+ *
+ * so:
+ *
+ *   offset * 1e-9 + 1 = 1 / (val * resolution + 1)
+ *
+ * We want "resolution" to be an integer, so resolution = R * 1e-9, giving
+ *   offset = 1e18 / (val * R + 1e9) - 1e9
+ *   val = (1e18 / (offset + 1e9) - 1e9) / R
+ * with a common transformation:
+ *   f(x) = 1e18 / (x + 1e9) - 1e9
+ *   offset = f(val * R)
+ *   val = f(offset) / R
+ *
+ * Armada 38x supports two modes, fine mode (954ppb) and coarse mode (3815ppb).
+ */
+static long armada38x_ppb_convert(long ppb)
+{
+	long div = ppb + 1000000000L;
+
+	return div_s64(1000000000000000000LL + div / 2, div) - 1000000000L;
+}
+
+static int armada38x_rtc_read_offset(struct device *dev, long *offset)
+{
+	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
+	unsigned long ccr, flags;
+	long ppb_cor;
+
+	spin_lock_irqsave(&rtc->lock, flags);
+	ccr = rtc->data->read_rtc_reg(rtc, RTC_CCR);
+	spin_unlock_irqrestore(&rtc->lock, flags);
+
+	ppb_cor = (ccr & RTC_CCR_MODE ? 3815 : 954) * (s8)ccr;
+	/* ppb_cor + 1000000000L can never be zero */
+	*offset = armada38x_ppb_convert(ppb_cor);
+
+	return 0;
+}
+
+static int armada38x_rtc_set_offset(struct device *dev, long offset)
+{
+	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
+	unsigned long ccr = 0;
+	long ppb_cor, off;
+
+	/*
+	 * The maximum ppb_cor is -128 * 3815 .. 127 * 3815, but we
+	 * need to clamp the input.  This equates to -484270 .. 488558.
+	 * Not only is this to stop out of range "off" but also to
+	 * avoid the division by zero in armada38x_ppb_convert().
+	 */
+	offset = clamp(offset, -484270L, 488558L);
+
+	ppb_cor = armada38x_ppb_convert(offset);
+
+	/*
+	 * Use low update mode where possible, which gives a better
+	 * resolution of correction.
+	 */
+	off = DIV_ROUND_CLOSEST(ppb_cor, 954);
+	if (off > 127 || off < -128) {
+		ccr = RTC_CCR_MODE;
+		off = DIV_ROUND_CLOSEST(ppb_cor, 3815);
+	}
+
+	/*
+	 * Armada 388 requires a bit pattern in bits 14..8 depending on
+	 * the sign bit: { 0, ~S, S, S, S, S, S }
+	 */
+	ccr |= (off & 0x3fff) ^ 0x2000;
+	rtc_delayed_write(ccr, rtc, RTC_CCR);
+
+	return 0;
+}
+
 static const struct rtc_class_ops armada38x_rtc_ops = {
 	.read_time = armada38x_rtc_read_time,
 	.set_time = armada38x_rtc_set_time,
 	.read_alarm = armada38x_rtc_read_alarm,
 	.set_alarm = armada38x_rtc_set_alarm,
 	.alarm_irq_enable = armada38x_rtc_alarm_irq_enable,
+	.read_offset = armada38x_rtc_read_offset,
+	.set_offset = armada38x_rtc_set_offset,
 };
 
 static const struct rtc_class_ops armada38x_rtc_ops_noirq = {
 	.read_time = armada38x_rtc_read_time,
 	.set_time = armada38x_rtc_set_time,
 	.read_alarm = armada38x_rtc_read_alarm,
+	.read_offset = armada38x_rtc_read_offset,
+	.set_offset = armada38x_rtc_set_offset,
 };
 
 static const struct armada38x_rtc_data armada38x_data = {
-- 
2.7.4

^ permalink raw reply related

* [PATCH] rtc: pcf8523: add support for trimming the RTC oscillator
From: Russell King @ 2017-09-29 10:23 UTC (permalink / raw)
  To: Alessandro Zummo; +Cc: Alexandre Belloni, linux-rtc

Add support for reading and writing the RTC offset register, converting
it to the corresponding parts-per-billion value.

When setting the drift, the PCF8523 has two modes: one applies the
adjustment every two hours, the other applies the adjustment every
minute.  We select between these two modes according to which ever
gives the closest PPB value to the one requested.

Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
 drivers/rtc/rtc-pcf8523.c | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/drivers/rtc/rtc-pcf8523.c b/drivers/rtc/rtc-pcf8523.c
index 28c48b3c1946..c312af0db729 100644
--- a/drivers/rtc/rtc-pcf8523.c
+++ b/drivers/rtc/rtc-pcf8523.c
@@ -35,6 +35,9 @@
 #define REG_MONTHS   0x08
 #define REG_YEARS    0x09
 
+#define REG_OFFSET   0x0e
+#define REG_OFFSET_MODE BIT(7)
+
 struct pcf8523 {
 	struct rtc_device *rtc;
 };
@@ -272,10 +275,47 @@ static int pcf8523_rtc_ioctl(struct device *dev, unsigned int cmd,
 #define pcf8523_rtc_ioctl NULL
 #endif
 
+static int pcf8523_rtc_read_offset(struct device *dev, long *offset)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	int err;
+	u8 value;
+	s8 val;
+
+	err = pcf8523_read(client, REG_OFFSET, &value);
+	if (err < 0)
+		return err;
+
+	/* sign extend the 7-bit offset value */
+	val = value << 1;
+	*offset = (value & REG_OFFSET_MODE ? 4069 : 4340) * (val >> 1);
+
+	return 0;
+}
+
+static int pcf8523_rtc_set_offset(struct device *dev, long offset)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	long reg_m0, reg_m1;
+	u8 value;
+
+	reg_m0 = clamp(DIV_ROUND_CLOSEST(offset, 4340), -64L, 63L);
+	reg_m1 = clamp(DIV_ROUND_CLOSEST(offset, 4069), -64L, 63L);
+
+	if (abs(reg_m0 * 4340 - offset) < abs(reg_m1 * 4069 - offset))
+		value = reg_m0 & 0x7f;
+	else
+		value = (reg_m1 & 0x7f) | REG_OFFSET_MODE;
+
+	return pcf8523_write(client, REG_OFFSET, value);
+}
+
 static const struct rtc_class_ops pcf8523_rtc_ops = {
 	.read_time = pcf8523_rtc_read_time,
 	.set_time = pcf8523_rtc_set_time,
 	.ioctl = pcf8523_rtc_ioctl,
+	.read_offset = pcf8523_rtc_read_offset,
+	.set_offset = pcf8523_rtc_set_offset,
 };
 
 static int pcf8523_probe(struct i2c_client *client,
-- 
2.7.4

^ permalink raw reply related

* [PATCH] rtc: clarify the RTC offset correction
From: Russell King @ 2017-09-29 10:23 UTC (permalink / raw)
  To: Alessandro Zummo; +Cc: Alexandre Belloni, linux-rtc

The RTC offset correction documentation is not very clear about the
exact relationship between "offset" and the effect it has on the RTC.
Supplement the documentation with an equation giving the relationship.

Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
 drivers/rtc/interface.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index 8cec9a02c0b8..045e0a72d14b 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -1004,6 +1004,10 @@ int rtc_read_offset(struct rtc_device *rtc, long *offset)
  * to compensate for differences in the actual clock rate due to temperature,
  * the crystal, capacitor, etc.
  *
+ * The adjustment applied is as follows:
+ *   t = t0 * (1 + offset * 1e-9)
+ * where t0 is the measured length of 1 RTC second with offset = 0
+ *
  * Kernel interface to adjust an rtc clock offset.
  * Return 0 on success, or a negative number on error.
  * If the rtc offset is not setable (or not implemented), return -EINVAL
-- 
2.7.4

^ permalink raw reply related

* [PATCH v2 4/4] rtc: pl031: make interrupt optional
From: Russell King @ 2017-09-29 10:22 UTC (permalink / raw)
  To: Alessandro Zummo
  Cc: Linus Walleij, Alexandre Belloni, linux-arm-kernel, linux-rtc
In-Reply-To: <20170929102116.GX20805@n2100.armlinux.org.uk>

On some platforms, the interrupt for the PL031 is optional.  Avoid
trying to claim the interrupt if it's not specified.

Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
 drivers/rtc/rtc-pl031.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c
index 64c77ec1b4ea..82eb7da2c478 100644
--- a/drivers/rtc/rtc-pl031.c
+++ b/drivers/rtc/rtc-pl031.c
@@ -308,7 +308,8 @@ static int pl031_remove(struct amba_device *adev)
 
 	dev_pm_clear_wake_irq(&adev->dev);
 	device_init_wakeup(&adev->dev, false);
-	free_irq(adev->irq[0], ldata);
+	if (adev->irq[0])
+		free_irq(adev->irq[0], ldata);
 	rtc_device_unregister(ldata->rtc);
 	amba_release_regions(adev);
 
@@ -389,12 +390,13 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
 		goto out;
 	}
 
-	if (request_irq(adev->irq[0], pl031_interrupt,
-			vendor->irqflags, "rtc-pl031", ldata)) {
-		ret = -EIO;
-		goto out_no_irq;
+	if (adev->irq[0]) {
+		ret = request_irq(adev->irq[0], pl031_interrupt,
+				  vendor->irqflags, "rtc-pl031", ldata);
+		if (ret)
+			goto out_no_irq;
+		dev_pm_set_wake_irq(&adev->dev, adev->irq[0]);
 	}
-	dev_pm_set_wake_irq(&adev->dev, adev->irq[0]);
 	return 0;
 
 out_no_irq:
-- 
2.7.4

^ permalink raw reply related

* [PATCH v2 3/4] rtc: pl031: avoid exposing alarm if no interrupt
From: Russell King @ 2017-09-29 10:22 UTC (permalink / raw)
  To: Alessandro Zummo
  Cc: Linus Walleij, Alexandre Belloni, linux-arm-kernel, linux-rtc
In-Reply-To: <20170929102116.GX20805@n2100.armlinux.org.uk>

If the RTC has no interrupt, there is little point in exposing the RTC
alarm capabilities, as it can't be used as a wakeup source nor can it
deliver an event to userspace.

Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
 drivers/rtc/rtc-pl031.c | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c
index 777da06d53ee..64c77ec1b4ea 100644
--- a/drivers/rtc/rtc-pl031.c
+++ b/drivers/rtc/rtc-pl031.c
@@ -320,7 +320,7 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
 	int ret;
 	struct pl031_local *ldata;
 	struct pl031_vendor_data *vendor = id->data;
-	struct rtc_class_ops *ops = &vendor->ops;
+	struct rtc_class_ops *ops;
 	unsigned long time, data;
 
 	ret = amba_request_regions(adev, NULL);
@@ -329,12 +329,14 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
 
 	ldata = devm_kzalloc(&adev->dev, sizeof(struct pl031_local),
 			     GFP_KERNEL);
-	if (!ldata) {
+	ops = devm_kmemdup(&adev->dev, &vendor->ops, sizeof(vendor->ops),
+			   GFP_KERNEL);
+	if (!ldata || !ops) {
 		ret = -ENOMEM;
 		goto out;
 	}
-	ldata->vendor = vendor;
 
+	ldata->vendor = vendor;
 	ldata->base = devm_ioremap(&adev->dev, adev->res.start,
 				   resource_size(&adev->res));
 	if (!ldata->base) {
@@ -372,6 +374,13 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
 		}
 	}
 
+	if (!adev->irq[0]) {
+		/* When there's no interrupt, no point in exposing the alarm */
+		ops->read_alarm = NULL;
+		ops->set_alarm = NULL;
+		ops->alarm_irq_enable = NULL;
+	}
+
 	device_init_wakeup(&adev->dev, true);
 	ldata->rtc = rtc_device_register("pl031", &adev->dev, ops,
 					THIS_MODULE);
-- 
2.7.4

^ permalink raw reply related

* [PATCH v2 2/4] rtc: pl031: use devm_* for allocating memory and mapping resource
From: Russell King @ 2017-09-29 10:22 UTC (permalink / raw)
  To: Alessandro Zummo
  Cc: Linus Walleij, Alexandre Belloni, linux-arm-kernel, linux-rtc
In-Reply-To: <20170929102116.GX20805@n2100.armlinux.org.uk>

Use the devm_* APIs for allocating memory and mapping the memory in
the probe function to relieve the driver from having to deal with
this in the cleanup paths.

Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
 drivers/rtc/rtc-pl031.c | 17 ++++++-----------
 1 file changed, 6 insertions(+), 11 deletions(-)

diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c
index 0d87b90b1903..777da06d53ee 100644
--- a/drivers/rtc/rtc-pl031.c
+++ b/drivers/rtc/rtc-pl031.c
@@ -310,8 +310,6 @@ static int pl031_remove(struct amba_device *adev)
 	device_init_wakeup(&adev->dev, false);
 	free_irq(adev->irq[0], ldata);
 	rtc_device_unregister(ldata->rtc);
-	iounmap(ldata->base);
-	kfree(ldata);
 	amba_release_regions(adev);
 
 	return 0;
@@ -329,18 +327,19 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
 	if (ret)
 		goto err_req;
 
-	ldata = kzalloc(sizeof(struct pl031_local), GFP_KERNEL);
+	ldata = devm_kzalloc(&adev->dev, sizeof(struct pl031_local),
+			     GFP_KERNEL);
 	if (!ldata) {
 		ret = -ENOMEM;
 		goto out;
 	}
 	ldata->vendor = vendor;
 
-	ldata->base = ioremap(adev->res.start, resource_size(&adev->res));
-
+	ldata->base = devm_ioremap(&adev->dev, adev->res.start,
+				   resource_size(&adev->res));
 	if (!ldata->base) {
 		ret = -ENOMEM;
-		goto out_no_remap;
+		goto out;
 	}
 
 	amba_set_drvdata(adev, ldata);
@@ -378,7 +377,7 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
 					THIS_MODULE);
 	if (IS_ERR(ldata->rtc)) {
 		ret = PTR_ERR(ldata->rtc);
-		goto out_no_rtc;
+		goto out;
 	}
 
 	if (request_irq(adev->irq[0], pl031_interrupt,
@@ -391,10 +390,6 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
 
 out_no_irq:
 	rtc_device_unregister(ldata->rtc);
-out_no_rtc:
-	iounmap(ldata->base);
-out_no_remap:
-	kfree(ldata);
 out:
 	amba_release_regions(adev);
 err_req:
-- 
2.7.4

^ permalink raw reply related

* [PATCH v2 1/4] rtc: pl031: constify amba_ids
From: Russell King @ 2017-09-29 10:22 UTC (permalink / raw)
  To: Alessandro Zummo
  Cc: Linus Walleij, Alexandre Belloni, linux-arm-kernel, linux-rtc
In-Reply-To: <20170929102116.GX20805@n2100.armlinux.org.uk>

The AMBA device IDs should be marked const.  Make that so.

Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
 drivers/rtc/rtc-pl031.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c
index e1687e19c59f..0d87b90b1903 100644
--- a/drivers/rtc/rtc-pl031.c
+++ b/drivers/rtc/rtc-pl031.c
@@ -446,7 +446,7 @@ static struct pl031_vendor_data stv2_pl031 = {
 	.irqflags = IRQF_SHARED | IRQF_COND_SUSPEND,
 };
 
-static struct amba_id pl031_ids[] = {
+static const struct amba_id pl031_ids[] = {
 	{
 		.id = 0x00041031,
 		.mask = 0x000fffff,
-- 
2.7.4

^ permalink raw reply related

* [PATCH v2 0/4] Make PL031 interrupt optional
From: Russell King - ARM Linux @ 2017-09-29 10:21 UTC (permalink / raw)
  To: Alessandro Zummo
  Cc: linux-rtc, Linus Walleij, Alexandre Belloni, linux-arm-kernel

There are some boards out there which have a PL031 RTC, but its
interrupt is not wired up.  To support these, we need the PL031
to support the primecell without interrupts.

When no interrupt is present, there's little point exposing the
RTC's alarm capabilities, so we omit the alarm-related function
calls - the RTC merely becomes a source of time-of-day.

This patch series cleans up the pl031 driver a little, and adds
support for this configuration.

 drivers/rtc/rtc-pl031.c | 48 +++++++++++++++++++++++++++---------------------
 1 file changed, 27 insertions(+), 21 deletions(-)

v2: update patch 2 & 3 for review comment on v1.

-- 
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line in suburbia: sync at 8.8Mbps down 630kbps up
According to speedtest.net: 8.21Mbps down 510kbps up

^ permalink raw reply

* Re: [RFC v3 7/7] platform/x86: intel_scu_ipc: Use generic Intel IPC device calls
From: Guenter Roeck @ 2017-09-28 13:35 UTC (permalink / raw)
  To: Alexandre Belloni, sathyanarayanan.kuppuswamy
  Cc: a.zummo, x86, wim, mingo, qipeng.zha, hpa, dvhart, tglx,
	lee.jones, andy, souvik.k.chakravarty, linux-rtc, linux-watchdog,
	linux-kernel, platform-driver-x86, sathyaosid
In-Reply-To: <20170928125519.ol5suebaoqsiixm5@piout.net>

On 09/28/2017 05:55 AM, Alexandre Belloni wrote:
> On 04/09/2017 at 22:37:27 -0700, sathyanarayanan.kuppuswamy@linux.intel.com wrote:
>> 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 to use APIs provided
>> by generic IPC driver.
>>
>> Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
> 
> For the RTC part:
> Acked-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
>  >
>> ---
>>   arch/x86/include/asm/intel_scu_ipc.h    |  23 +-
>>   arch/x86/platform/intel-mid/intel-mid.c |  12 +-
>>   drivers/platform/x86/Kconfig            |   1 +
>>   drivers/platform/x86/intel_scu_ipc.c    | 483 +++++++++++++-------------------
>>   drivers/rtc/rtc-mrst.c                  |  15 +-
>>   drivers/watchdog/intel-mid_wdt.c        |  12 +-
>>   drivers/watchdog/intel_scu_watchdog.c   |  17 +-
>>   7 files changed, 251 insertions(+), 312 deletions(-)

I think it would help in general to mention at least in the description which drivers are touched.

I see calls to intel_ipc_dev_get() but not associated put calls. Missing the context, it may
well be that those are not necessary, but then how does the ipc code know that the device
reference is no longer needed ?

Guenter

>>
>> diff --git a/arch/x86/include/asm/intel_scu_ipc.h b/arch/x86/include/asm/intel_scu_ipc.h
>> index 81d3d87..5842534 100644
>> --- a/arch/x86/include/asm/intel_scu_ipc.h
>> +++ b/arch/x86/include/asm/intel_scu_ipc.h
>> @@ -14,9 +14,19 @@
>>   #define IPCMSG_COLD_BOOT	0xF3
>>   
>>   #define IPCMSG_VRTC		0xFA	 /* Set vRTC device */
>> -	/* Command id associated with message IPCMSG_VRTC */
>> -	#define IPC_CMD_VRTC_SETTIME      1 /* Set time */
>> -	#define IPC_CMD_VRTC_SETALARM     2 /* Set alarm */
>> +
>> +/* Command id associated with message IPCMSG_VRTC */
>> +#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
>> +
>> +static inline void scu_cmd_init(u32 *cmd, u32 param1, u32 param2)
>> +{
>> +	cmd[0] = param1;
>> +	cmd[1] = param2;
>> +}
>>   
>>   /* Read single register */
>>   int intel_scu_ipc_ioread8(u16 addr, u8 *data);
>> @@ -45,13 +55,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 12a2725..27541e9 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>
>> @@ -68,18 +69,23 @@ static void *(*get_intel_mid_ops[])(void) = INTEL_MID_OPS_INIT;
>>   enum intel_mid_cpu_type __intel_mid_cpu_chip;
>>   EXPORT_SYMBOL_GPL(__intel_mid_cpu_chip);
>>   
>> +static struct intel_ipc_dev *ipc_dev;
>> +
>>   static void intel_mid_power_off(void)
>>   {
>> +	u32 cmds[SCU_PARAM_LEN] = {IPCMSG_COLD_OFF, 1};
>>   	/* Shut down South Complex via PWRMU */
>>   	intel_mid_pwr_power_off();
>>   
>>   	/* 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);
>>   };
>>   
>>   static void intel_mid_reboot(void)
>>   {
>> -	intel_scu_ipc_simple_command(IPCMSG_COLD_BOOT, 0);
>> +	u32 cmds[SCU_PARAM_LEN] = {IPCMSG_COLD_BOOT, 0};
>> +
>> +	ipc_dev_simple_cmd(ipc_dev, cmds, SCU_PARAM_LEN);
>>   }
>>   
>>   static unsigned long __init intel_mid_calibrate_tsc(void)
>> @@ -206,6 +212,8 @@ void __init x86_intel_mid_early_setup(void)
>>   	x86_init.mpparse.find_smp_config = x86_init_noop;
>>   	x86_init.mpparse.get_smp_config = x86_init_uint_noop;
>>   	set_bit(MP_BUS_ISA, mp_bus_not_pci);
>> +
>> +	ipc_dev = intel_ipc_dev_get(INTEL_SCU_IPC_DEV);
>>   }
>>   
>>   /*
>> 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 f7cf981..78013e4 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,140 +119,49 @@ static 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)
>> -{
>> -	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;
>> -
>> -	if (!wait_for_completion_timeout(&scu->cmd_complete, 3 * HZ)) {
>> -		dev_err(scu->dev, "IPC timed out\n");
>> -		return -ETIMEDOUT;
>> -	}
>> -
>> -	status = ipc_read_status(scu);
>> -	if (status & BIT(1))
>> -		return -EIO;
>> -
>> -	return 0;
>> -}
>> -
>> -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);
>> -}
>> -
>>   /* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */
>>   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;
>> +	u32 cmd[SCU_PARAM_LEN] = {0};
>> +	/* 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;
>> -	}
>> +	scu_cmd_init(cmd, op, id);
>>   
>>   	for (nc = 0; nc < count; nc++, offset += 2) {
>>   		cbuf[offset] = addr[nc];
>> @@ -234,30 +169,30 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
>>   	}
>>   
>>   	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 = ipc_dev_raw_cmd(scu->ipc_dev, cmd, SCU_PARAM_LEN,
>> +				(u8 *)wbuf, count * 2, (u32 *)rbuf,
>> +				IPC_RWBUF_SIZE_DWORD, 0, 0);
>>   	} 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 = ipc_dev_raw_cmd(scu->ipc_dev, cmd, SCU_PARAM_LEN,
>> +				(u8 *)wbuf, count * 3, NULL, 0, 0, 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 = ipc_dev_raw_cmd(scu->ipc_dev, cmd, SCU_PARAM_LEN,
>> +				(u8 *)wbuf, 4, NULL, 0, 0, 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 +357,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 +378,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 IRQ_HANDLED;
>> +	return 0;
>> +}
>> +
>> +static int pre_cmd_fn(u32 *cmd_list, u32 cmdlen, u32 *in, u32 inlen,
>> +		u32 *out, u32 outlen)
>> +{
>> +	int ret;
>> +
>> +	if (inlen > IPC_WWBUF_SIZE_DWORD || outlen > IPC_RWBUF_SIZE_DWORD)
>> +		return -EINVAL;
>> +
>> +	ret = pre_simple_cmd_fn(cmd_list, cmdlen);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	cmd_list[0] |= (inlen << IPC_CMD_SIZE);
>> +
>> +	return 0;
>> +}
>> +
>> +static int pre_raw_cmd_fn(u32 *cmd_list, u32 cmdlen, u8 *in, u32 inlen,
>> +		u32 *out, u32 outlen, u32 dptr, u32 sptr)
>> +{
>> +	int ret;
>> +
>> +	if (inlen > IPC_WWBUF_SIZE || outlen > IPC_RWBUF_SIZE_DWORD)
>> +		return -EINVAL;
>> +
>> +	ret = pre_simple_cmd_fn(cmd_list, cmdlen);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	cmd_list[0] |= (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 +548,34 @@ 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..a2d87e8 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) {
>> diff --git a/drivers/watchdog/intel-mid_wdt.c b/drivers/watchdog/intel-mid_wdt.c
>> index 72c108a..a73559b 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,10 @@ 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};
>> +
>> +	return ipc_dev_cmd(scu_ipc_dev, cmds, SCU_PARAM_LEN, in,
>> +			inlen, NULL, 0);
>>   }
>>   
>>   static int wdt_start(struct watchdog_device *wd)
>> @@ -129,6 +135,10 @@ static int mid_wdt_probe(struct platform_device *pdev)
>>   	if (!wdt_dev)
>>   		return -ENOMEM;
>>   
>> +	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);
>> +
>>   	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..9457c7a 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,18 +131,14 @@ 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};
>>   
>>   	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_ret = ipc_dev_cmd(scu_ipc_dev, cmds, SCU_PARAM_LEN, ipc_wbuf,
>> +			2, NULL, 0);
>>   
>>   	if (ipc_ret != 0)
>>   		pr_err("Error setting SCU watchdog timer: %x\n", ipc_ret);
>> @@ -460,6 +459,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) {
>> -- 
>> 2.7.4
>>
> 

^ permalink raw reply

* Re: [RFC v3 7/7] platform/x86: intel_scu_ipc: Use generic Intel IPC device calls
From: Alexandre Belloni @ 2017-09-28 12:55 UTC (permalink / raw)
  To: sathyanarayanan.kuppuswamy
  Cc: a.zummo, x86, wim, mingo, qipeng.zha, hpa, dvhart, tglx,
	lee.jones, andy, souvik.k.chakravarty, linux-rtc, linux-watchdog,
	linux-kernel, platform-driver-x86, sathyaosid
In-Reply-To: <90f4d15c9ca0430f6ac4cfafe0eaf6ece6c8761c.1504588701.git.sathyanarayanan.kuppuswamy@linux.intel.com>

On 04/09/2017 at 22:37:27 -0700, sathyanarayanan.kuppuswamy@linux.intel.com wrote:
> 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 to use APIs provided
> by generic IPC driver.
> 
> Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>

For the RTC part:
Acked-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>


> ---
>  arch/x86/include/asm/intel_scu_ipc.h    |  23 +-
>  arch/x86/platform/intel-mid/intel-mid.c |  12 +-
>  drivers/platform/x86/Kconfig            |   1 +
>  drivers/platform/x86/intel_scu_ipc.c    | 483 +++++++++++++-------------------
>  drivers/rtc/rtc-mrst.c                  |  15 +-
>  drivers/watchdog/intel-mid_wdt.c        |  12 +-
>  drivers/watchdog/intel_scu_watchdog.c   |  17 +-
>  7 files changed, 251 insertions(+), 312 deletions(-)
> 
> diff --git a/arch/x86/include/asm/intel_scu_ipc.h b/arch/x86/include/asm/intel_scu_ipc.h
> index 81d3d87..5842534 100644
> --- a/arch/x86/include/asm/intel_scu_ipc.h
> +++ b/arch/x86/include/asm/intel_scu_ipc.h
> @@ -14,9 +14,19 @@
>  #define IPCMSG_COLD_BOOT	0xF3
>  
>  #define IPCMSG_VRTC		0xFA	 /* Set vRTC device */
> -	/* Command id associated with message IPCMSG_VRTC */
> -	#define IPC_CMD_VRTC_SETTIME      1 /* Set time */
> -	#define IPC_CMD_VRTC_SETALARM     2 /* Set alarm */
> +
> +/* Command id associated with message IPCMSG_VRTC */
> +#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
> +
> +static inline void scu_cmd_init(u32 *cmd, u32 param1, u32 param2)
> +{
> +	cmd[0] = param1;
> +	cmd[1] = param2;
> +}
>  
>  /* Read single register */
>  int intel_scu_ipc_ioread8(u16 addr, u8 *data);
> @@ -45,13 +55,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 12a2725..27541e9 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>
> @@ -68,18 +69,23 @@ static void *(*get_intel_mid_ops[])(void) = INTEL_MID_OPS_INIT;
>  enum intel_mid_cpu_type __intel_mid_cpu_chip;
>  EXPORT_SYMBOL_GPL(__intel_mid_cpu_chip);
>  
> +static struct intel_ipc_dev *ipc_dev;
> +
>  static void intel_mid_power_off(void)
>  {
> +	u32 cmds[SCU_PARAM_LEN] = {IPCMSG_COLD_OFF, 1};
>  	/* Shut down South Complex via PWRMU */
>  	intel_mid_pwr_power_off();
>  
>  	/* 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);
>  };
>  
>  static void intel_mid_reboot(void)
>  {
> -	intel_scu_ipc_simple_command(IPCMSG_COLD_BOOT, 0);
> +	u32 cmds[SCU_PARAM_LEN] = {IPCMSG_COLD_BOOT, 0};
> +
> +	ipc_dev_simple_cmd(ipc_dev, cmds, SCU_PARAM_LEN);
>  }
>  
>  static unsigned long __init intel_mid_calibrate_tsc(void)
> @@ -206,6 +212,8 @@ void __init x86_intel_mid_early_setup(void)
>  	x86_init.mpparse.find_smp_config = x86_init_noop;
>  	x86_init.mpparse.get_smp_config = x86_init_uint_noop;
>  	set_bit(MP_BUS_ISA, mp_bus_not_pci);
> +
> +	ipc_dev = intel_ipc_dev_get(INTEL_SCU_IPC_DEV);
>  }
>  
>  /*
> 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 f7cf981..78013e4 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,140 +119,49 @@ static 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)
> -{
> -	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;
> -
> -	if (!wait_for_completion_timeout(&scu->cmd_complete, 3 * HZ)) {
> -		dev_err(scu->dev, "IPC timed out\n");
> -		return -ETIMEDOUT;
> -	}
> -
> -	status = ipc_read_status(scu);
> -	if (status & BIT(1))
> -		return -EIO;
> -
> -	return 0;
> -}
> -
> -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);
> -}
> -
>  /* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */
>  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;
> +	u32 cmd[SCU_PARAM_LEN] = {0};
> +	/* 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;
> -	}
> +	scu_cmd_init(cmd, op, id);
>  
>  	for (nc = 0; nc < count; nc++, offset += 2) {
>  		cbuf[offset] = addr[nc];
> @@ -234,30 +169,30 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
>  	}
>  
>  	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 = ipc_dev_raw_cmd(scu->ipc_dev, cmd, SCU_PARAM_LEN,
> +				(u8 *)wbuf, count * 2, (u32 *)rbuf,
> +				IPC_RWBUF_SIZE_DWORD, 0, 0);
>  	} 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 = ipc_dev_raw_cmd(scu->ipc_dev, cmd, SCU_PARAM_LEN,
> +				(u8 *)wbuf, count * 3, NULL, 0, 0, 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 = ipc_dev_raw_cmd(scu->ipc_dev, cmd, SCU_PARAM_LEN,
> +				(u8 *)wbuf, 4, NULL, 0, 0, 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 +357,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 +378,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 IRQ_HANDLED;
> +	return 0;
> +}
> +
> +static int pre_cmd_fn(u32 *cmd_list, u32 cmdlen, u32 *in, u32 inlen,
> +		u32 *out, u32 outlen)
> +{
> +	int ret;
> +
> +	if (inlen > IPC_WWBUF_SIZE_DWORD || outlen > IPC_RWBUF_SIZE_DWORD)
> +		return -EINVAL;
> +
> +	ret = pre_simple_cmd_fn(cmd_list, cmdlen);
> +	if (ret < 0)
> +		return ret;
> +
> +	cmd_list[0] |= (inlen << IPC_CMD_SIZE);
> +
> +	return 0;
> +}
> +
> +static int pre_raw_cmd_fn(u32 *cmd_list, u32 cmdlen, u8 *in, u32 inlen,
> +		u32 *out, u32 outlen, u32 dptr, u32 sptr)
> +{
> +	int ret;
> +
> +	if (inlen > IPC_WWBUF_SIZE || outlen > IPC_RWBUF_SIZE_DWORD)
> +		return -EINVAL;
> +
> +	ret = pre_simple_cmd_fn(cmd_list, cmdlen);
> +	if (ret < 0)
> +		return ret;
> +
> +	cmd_list[0] |= (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 +548,34 @@ 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..a2d87e8 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) {
> diff --git a/drivers/watchdog/intel-mid_wdt.c b/drivers/watchdog/intel-mid_wdt.c
> index 72c108a..a73559b 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,10 @@ 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};
> +
> +	return ipc_dev_cmd(scu_ipc_dev, cmds, SCU_PARAM_LEN, in,
> +			inlen, NULL, 0);
>  }
>  
>  static int wdt_start(struct watchdog_device *wd)
> @@ -129,6 +135,10 @@ static int mid_wdt_probe(struct platform_device *pdev)
>  	if (!wdt_dev)
>  		return -ENOMEM;
>  
> +	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);
> +
>  	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..9457c7a 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,18 +131,14 @@ 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};
>  
>  	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_ret = ipc_dev_cmd(scu_ipc_dev, cmds, SCU_PARAM_LEN, ipc_wbuf,
> +			2, NULL, 0);
>  
>  	if (ipc_ret != 0)
>  		pr_err("Error setting SCU watchdog timer: %x\n", ipc_ret);
> @@ -460,6 +459,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) {
> -- 
> 2.7.4
> 

-- 
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply

* [PATCH] rtc: set the alarm to the next expiring timer
From: Alexandre Belloni @ 2017-09-28 12:11 UTC (permalink / raw)
  To: linux-rtc; +Cc: linux-kernel, Alexandre Belloni

If there is any non expired timer in the queue, the RTC alarm is never set.
This is an issue when adding a timer that expires before the next non
expired timer.

Ensure the RTC alarm is set in that case.

Fixes: 2b2f5ff00f63 ("rtc: interface: ignore expired timers when enqueuing new timers")
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
---
 drivers/rtc/interface.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index 8cec9a02c0b8..9eb32ead63db 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -779,7 +779,7 @@ static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer)
 	}
 
 	timerqueue_add(&rtc->timerqueue, &timer->node);
-	if (!next) {
+	if (!next || ktime_before(timer->node.expires, next->expires)) {
 		struct rtc_wkalrm alarm;
 		int err;
 		alarm.time = rtc_ktime_to_tm(timer->node.expires);
-- 
2.14.2

^ permalink raw reply related

* [PATCH] rtc: ds1307: simplify hwmon config
From: Heiner Kallweit @ 2017-09-27 20:41 UTC (permalink / raw)
  To: Alexandre Belloni, linux-rtc

We don't have to define an extra config symbol, IS_REACHABLE does
what we need. And having this config symbol just to save the few
bytes of hwmon support on non-DS3231 chips isn't worth it IMO
(especially as the symbol is set per default).

Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
 drivers/rtc/Kconfig      | 9 ---------
 drivers/rtc/rtc-ds1307.c | 2 +-
 2 files changed, 1 insertion(+), 10 deletions(-)

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index e0e58f3b..5572819d 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -244,15 +244,6 @@ config RTC_DRV_DS1307
 	  This driver can also be built as a module. If so, the module
 	  will be called rtc-ds1307.
 
-config RTC_DRV_DS1307_HWMON
-	bool "HWMON support for rtc-ds1307"
-	depends on RTC_DRV_DS1307 && HWMON
-	depends on !(RTC_DRV_DS1307=y && HWMON=m)
-	default y
-	help
-	  Say Y here if you want to expose temperature sensor data on
-	  rtc-ds1307 (only DS3231)
-
 config RTC_DRV_DS1307_CENTURY
 	bool "Century bit support for rtc-ds1307"
 	depends on RTC_DRV_DS1307
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
index e7d9215c..699ac68a 100644
--- a/drivers/rtc/rtc-ds1307.c
+++ b/drivers/rtc/rtc-ds1307.c
@@ -1005,7 +1005,7 @@ static u8 ds1307_trickle_init(struct ds1307 *ds1307,
 
 /*----------------------------------------------------------------------*/
 
-#ifdef CONFIG_RTC_DRV_DS1307_HWMON
+#if IS_REACHABLE(CONFIG_HWMON)
 
 /*
  * Temperature sensor support for ds3231 devices.
-- 
2.14.1

^ permalink raw reply related

* [PATCH v2 4/6] dt-bindings: rtc: DS1307 and compatibles are not trivial
From: Alexandre Belloni @ 2017-09-27 14:03 UTC (permalink / raw)
  To: linux-rtc; +Cc: linux-kernel, Rob Herring, devicetree, Alexandre Belloni
In-Reply-To: <20170927140345.5537-1-alexandre.belloni@free-electrons.com>

Document optional properties for ds1307 and compatible RTCs

Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
---
 .../devicetree/bindings/rtc/rtc-ds1307.txt         | 36 ++++++++++++++++++++++
 .../devicetree/bindings/trivial-devices.txt        |  6 ----
 2 files changed, 36 insertions(+), 6 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/rtc/rtc-ds1307.txt

diff --git a/Documentation/devicetree/bindings/rtc/rtc-ds1307.txt b/Documentation/devicetree/bindings/rtc/rtc-ds1307.txt
new file mode 100644
index 000000000000..c010f18a85a0
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/rtc-ds1307.txt
@@ -0,0 +1,36 @@
+Dallas DS1307 and compatible RTC
+
+Required properties:
+- compatible: should be one of:
+	"dallas,ds1307",
+	"dallas,ds1308",
+	"dallas,ds1337",
+	"dallas,ds1338",
+	"dallas,ds1339",
+	"dallas,ds1388",
+	"dallas,ds1340",
+	"dallas,ds1341",
+	"maxim,ds3231",
+	"st,m41t0",
+	"st,m41t00",
+	"microchip,mcp7940x",
+	"microchip,mcp7941x",
+	"pericom,pt7c4338",
+	"epson,rx8025",
+	"isil,isl12057"
+- reg: I2C bus address of the device
+
+Optional properties:
+- interrupt-parent: phandle for the interrupt controller.
+- interrupts: rtc alarm interrupt.
+- clock-output-names: From common clock binding to override the default output
+                      clock name
+- wakeup-source: Enables wake up of host system on alarm
+
+Example:
+	rtc1: ds1339@68 {
+		compatible = "dallas,ds1339";
+		reg = <0x68>;
+		interrupt-parent = <&gpio4>;
+		interrupts = <20 0>;
+	};
diff --git a/Documentation/devicetree/bindings/trivial-devices.txt b/Documentation/devicetree/bindings/trivial-devices.txt
index d0666acdec66..db7dc1edd4a2 100644
--- a/Documentation/devicetree/bindings/trivial-devices.txt
+++ b/Documentation/devicetree/bindings/trivial-devices.txt
@@ -36,9 +36,6 @@ atmel,at97sc3204t	i2c trusted platform module (TPM)
 capella,cm32181		CM32181: Ambient Light Sensor
 capella,cm3232		CM3232: Ambient Light Sensor
 cirrus,cs42l51		Cirrus Logic CS42L51 audio codec
-dallas,ds1307		64 x 8, Serial, I2C Real-Time Clock
-dallas,ds1338		I2C RTC with 56-Byte NV RAM
-dallas,ds1340		I2C RTC with Trickle Charger
 dallas,ds1374		I2C, 32-Bit Binary Counter Watchdog RTC with Trickle Charger and Reset Input/Output
 dallas,ds1631		High-Precision Digital Thermometer
 dallas,ds1672		Dallas DS1672 Real-time Clock
@@ -55,7 +52,6 @@ dlg,da9063		DA9063: system PMIC for quad-core application processors
 domintech,dmard09	DMARD09: 3-axis Accelerometer
 domintech,dmard10	DMARD10: 3-axis Accelerometer
 epson,rx8010		I2C-BUS INTERFACE REAL TIME CLOCK MODULE
-epson,rx8025		High-Stability. I2C-Bus INTERFACE REAL TIME CLOCK MODULE
 epson,rx8581		I2C-BUS INTERFACE REAL TIME CLOCK MODULE
 emmicro,em3027		EM Microelectronic EM3027 Real-time Clock
 fsl,mag3110		MAG3110: Xtrinsic High Accuracy, 3D Magnetometer
@@ -179,8 +175,6 @@ sii,s35390a		2-wire CMOS real-time clock
 silabs,si7020		Relative Humidity and Temperature Sensors
 skyworks,sky81452	Skyworks SKY81452: Six-Channel White LED Driver with Touch Panel Bias Supply
 st,24c256		i2c serial eeprom  (24cxx)
-st,m41t0		Serial real-time clock (RTC)
-st,m41t00		Serial real-time clock (RTC)
 st,m41t62		Serial real-time clock (RTC) with alarm
 st,m41t80		M41T80 - SERIAL ACCESS RTC WITH ALARMS
 taos,tsl2550		Ambient Light Sensor with SMBUS/Two Wire Serial Interface
-- 
2.14.2

^ permalink raw reply related

* [PATCH v2 6/6] dt-bindings: rtc: merge ds1339 in ds1307 documentation
From: Alexandre Belloni @ 2017-09-27 14:03 UTC (permalink / raw)
  To: linux-rtc; +Cc: linux-kernel, Rob Herring, devicetree, Alexandre Belloni
In-Reply-To: <20170927140345.5537-1-alexandre.belloni@free-electrons.com>

Now that there is documentation for the ds1307 and compatible RTCs, merge
the ds1339 documentation in it.

Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
---
 .../devicetree/bindings/rtc/dallas,ds1339.txt          | 18 ------------------
 Documentation/devicetree/bindings/rtc/rtc-ds1307.txt   |  8 ++++++++
 2 files changed, 8 insertions(+), 18 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/rtc/dallas,ds1339.txt

diff --git a/Documentation/devicetree/bindings/rtc/dallas,ds1339.txt b/Documentation/devicetree/bindings/rtc/dallas,ds1339.txt
deleted file mode 100644
index 916f57601a8f..000000000000
--- a/Documentation/devicetree/bindings/rtc/dallas,ds1339.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-* Dallas DS1339		I2C Serial Real-Time Clock
-
-Required properties:
-- compatible: Should contain "dallas,ds1339".
-- reg: I2C address for chip
-
-Optional properties:
-- trickle-resistor-ohms : Selected resistor for trickle charger
-	Values usable for ds1339 are 250, 2000, 4000
-	Should be given if trickle charger should be enabled
-- trickle-diode-disable : Do not use internal trickle charger diode
-	Should be given if internal trickle charger diode should be disabled
-Example:
-	ds1339: rtc@68 {
-		compatible = "dallas,ds1339";
-		trickle-resistor-ohms = <250>;
-		reg = <0x68>;
-	};
diff --git a/Documentation/devicetree/bindings/rtc/rtc-ds1307.txt b/Documentation/devicetree/bindings/rtc/rtc-ds1307.txt
index c010f18a85a0..d28d6e7f6ae8 100644
--- a/Documentation/devicetree/bindings/rtc/rtc-ds1307.txt
+++ b/Documentation/devicetree/bindings/rtc/rtc-ds1307.txt
@@ -26,6 +26,13 @@ Optional properties:
 - clock-output-names: From common clock binding to override the default output
                       clock name
 - wakeup-source: Enables wake up of host system on alarm
+- trickle-resistor-ohms : ds1339, ds1340 and ds 1388 only
+	Selected resistor for trickle charger
+	Possible values are 250, 2000, 4000
+	Should be given if trickle charger should be enabled
+- trickle-diode-disable : ds1339, ds1340 and ds 1388 only
+	Do not use internal trickle charger diode
+	Should be given if internal trickle charger diode should be disabled
 
 Example:
 	rtc1: ds1339@68 {
@@ -33,4 +40,5 @@ Example:
 		reg = <0x68>;
 		interrupt-parent = <&gpio4>;
 		interrupts = <20 0>;
+		trickle-resistor-ohms = <250>;
 	};
-- 
2.14.2

^ permalink raw reply related

* [PATCH v2 5/6] dt-bindings: rtc: Add bindings for m41t80 and compatibles
From: Alexandre Belloni @ 2017-09-27 14:03 UTC (permalink / raw)
  To: linux-rtc; +Cc: linux-kernel, Rob Herring, devicetree, Alexandre Belloni
In-Reply-To: <20170927140345.5537-1-alexandre.belloni@free-electrons.com>

The ST M41T80 family of RTC are not trivial devices, document them.

Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
---
 .../devicetree/bindings/rtc/rtc-m41t80.txt         | 31 ++++++++++++++++++++++
 .../devicetree/bindings/trivial-devices.txt        |  2 --
 2 files changed, 31 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/rtc/rtc-m41t80.txt

diff --git a/Documentation/devicetree/bindings/rtc/rtc-m41t80.txt b/Documentation/devicetree/bindings/rtc/rtc-m41t80.txt
new file mode 100644
index 000000000000..717d93860af1
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/rtc-m41t80.txt
@@ -0,0 +1,31 @@
+ST M41T80 family of RTC and compatible
+
+Required properties:
+- compatible: should be one of:
+	"st,m41t62",
+	"st,m41t65",
+	"st,m41t80",
+	"st,m41t81",
+	"st,m41t81s",
+	"st,m41t82",
+	"st,m41t83",
+	"st,m41t84",
+	"st,m41t85",
+	"st,m41t87",
+	"microcrystal,rv4162",
+- reg: I2C bus address of the device
+
+Optional properties:
+- interrupt-parent: phandle for the interrupt controller.
+- interrupts: rtc alarm interrupt.
+- clock-output-names: From common clock binding to override the default output
+                      clock name
+- wakeup-source: Enables wake up of host system on alarm
+
+Example:
+	rtc@68 {
+		compatible = "st,m41t80";
+		reg = <0x68>;
+		interrupt-parent = <&UIC0>;
+		interrupts = <0x9 0x8>;
+	};
diff --git a/Documentation/devicetree/bindings/trivial-devices.txt b/Documentation/devicetree/bindings/trivial-devices.txt
index db7dc1edd4a2..c6ae418f2e22 100644
--- a/Documentation/devicetree/bindings/trivial-devices.txt
+++ b/Documentation/devicetree/bindings/trivial-devices.txt
@@ -175,8 +175,6 @@ sii,s35390a		2-wire CMOS real-time clock
 silabs,si7020		Relative Humidity and Temperature Sensors
 skyworks,sky81452	Skyworks SKY81452: Six-Channel White LED Driver with Touch Panel Bias Supply
 st,24c256		i2c serial eeprom  (24cxx)
-st,m41t62		Serial real-time clock (RTC) with alarm
-st,m41t80		M41T80 - SERIAL ACCESS RTC WITH ALARMS
 taos,tsl2550		Ambient Light Sensor with SMBUS/Two Wire Serial Interface
 ti,ads7828		8-Channels, 12-bit ADC
 ti,ads7830		8-Channels, 8-bit ADC
-- 
2.14.2

^ permalink raw reply related

* [PATCH v2 2/6] dt-bindings: rtc: add stericsson,coh901331 bindings
From: Alexandre Belloni @ 2017-09-27 14:03 UTC (permalink / raw)
  To: linux-rtc; +Cc: linux-kernel, Rob Herring, devicetree, Alexandre Belloni
In-Reply-To: <20170927140345.5537-1-alexandre.belloni@free-electrons.com>

Add device tree bindings for the ST-Ericsson COH 901 331 Real Time Clock

Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
---
 .../devicetree/bindings/rtc/stericsson,coh901331.txt    | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/rtc/stericsson,coh901331.txt

diff --git a/Documentation/devicetree/bindings/rtc/stericsson,coh901331.txt b/Documentation/devicetree/bindings/rtc/stericsson,coh901331.txt
new file mode 100644
index 000000000000..3ebeb311335f
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/stericsson,coh901331.txt
@@ -0,0 +1,17 @@
+ST-Ericsson COH 901 331 Real Time Clock
+
+Required properties:
+- compatible: must be "stericsson,coh901331"
+- reg: address range of rtc register set.
+- interrupt-parent: phandle for the interrupt controller.
+- interrupts: rtc alarm interrupt.
+- clocks: phandle to the rtc clock source
+
+Example:
+	rtc: rtc@c0017000 {
+		compatible = "stericsson,coh901331";
+		reg = <0xc0017000 0x1000>;
+		interrupt-parent = <&vicb>;
+		interrupts = <10>;
+		clocks = <&rtc_clk>;
+	};
-- 
2.14.2

^ permalink raw reply related

* [PATCH v2 1/6] dt-bindings: trivial: Add RTCs
From: Alexandre Belloni @ 2017-09-27 14:03 UTC (permalink / raw)
  To: linux-rtc; +Cc: linux-kernel, Rob Herring, devicetree, Alexandre Belloni
In-Reply-To: <20170927140345.5537-1-alexandre.belloni@free-electrons.com>

Add remaining trivial RTC bindings.

Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
---
 Documentation/devicetree/bindings/trivial-devices.txt | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/Documentation/devicetree/bindings/trivial-devices.txt b/Documentation/devicetree/bindings/trivial-devices.txt
index aae37352c574..d0666acdec66 100644
--- a/Documentation/devicetree/bindings/trivial-devices.txt
+++ b/Documentation/devicetree/bindings/trivial-devices.txt
@@ -41,6 +41,7 @@ dallas,ds1338		I2C RTC with 56-Byte NV RAM
 dallas,ds1340		I2C RTC with Trickle Charger
 dallas,ds1374		I2C, 32-Bit Binary Counter Watchdog RTC with Trickle Charger and Reset Input/Output
 dallas,ds1631		High-Precision Digital Thermometer
+dallas,ds1672		Dallas DS1672 Real-time Clock
 dallas,ds1682		Total-Elapsed-Time Recorder with Alarm
 dallas,ds1775		Tiny Digital Thermometer and Thermostat
 dallas,ds3232		Extremely Accurate I²C RTC with Integrated Crystal and SRAM
@@ -56,6 +57,7 @@ domintech,dmard10	DMARD10: 3-axis Accelerometer
 epson,rx8010		I2C-BUS INTERFACE REAL TIME CLOCK MODULE
 epson,rx8025		High-Stability. I2C-Bus INTERFACE REAL TIME CLOCK MODULE
 epson,rx8581		I2C-BUS INTERFACE REAL TIME CLOCK MODULE
+emmicro,em3027		EM Microelectronic EM3027 Real-time Clock
 fsl,mag3110		MAG3110: Xtrinsic High Accuracy, 3D Magnetometer
 fsl,mc13892		MC13892: Power Management Integrated Circuit (PMIC) for i.MX35/51
 fsl,mma7660		MMA7660FC: 3-Axis Orientation/Motion Detection Sensor
@@ -67,6 +69,8 @@ gmt,g751		G751: Digital Temperature Sensor and Thermal Watchdog with Two-Wire In
 infineon,slb9635tt	Infineon SLB9635 (Soft-) I2C TPM (old protocol, max 100khz)
 infineon,slb9645tt	Infineon SLB9645 I2C TPM (new protocol, max 400khz)
 isil,isl1208		Intersil ISL1208 Low Power RTC with Battery Backed SRAM
+isil,isl1218		Intersil ISL1218 Low Power RTC with Battery Backed SRAM
+isil,isl12022		Intersil ISL12022 Real-time Clock
 isil,isl29028		Intersil ISL29028 Ambient Light and Proximity Sensor
 isil,isl29030		Intersil ISL29030 Ambient Light and Proximity Sensor
 maxim,ds1050		5 Bit Programmable, Pulse-Width Modulator
@@ -155,6 +159,7 @@ nxp,pca9556		Octal SMBus and I2C registered interface
 nxp,pca9557		8-bit I2C-bus and SMBus I/O port with reset
 nxp,pcf2127		Real-time clock
 nxp,pcf2129		Real-time clock
+nxp,pcf8523		Real-time Clock
 nxp,pcf8563		Real-time clock/calendar
 nxp,pcf85063		Tiny Real-Time Clock
 oki,ml86v7667		OKI ML86V7667 video decoder
-- 
2.14.2

^ permalink raw reply related

* [PATCH v2 3/6] dt-bindings: rtc: Add sirf,prima2-sysrtc bindings
From: Alexandre Belloni @ 2017-09-27 14:03 UTC (permalink / raw)
  To: linux-rtc; +Cc: linux-kernel, Rob Herring, devicetree, Alexandre Belloni
In-Reply-To: <20170927140345.5537-1-alexandre.belloni@free-electrons.com>

Add device tree bindings for the SiRFSoC Real Time Clock.

Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
---
 .../devicetree/bindings/rtc/sirf,prima2-sysrtc.txt          | 13 +++++++++++++
 1 file changed, 13 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/rtc/sirf,prima2-sysrtc.txt

diff --git a/Documentation/devicetree/bindings/rtc/sirf,prima2-sysrtc.txt b/Documentation/devicetree/bindings/rtc/sirf,prima2-sysrtc.txt
new file mode 100644
index 000000000000..58885b55da21
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/sirf,prima2-sysrtc.txt
@@ -0,0 +1,13 @@
+SiRFSoC Real Time Clock
+
+Required properties:
+- compatible: must be "sirf,prima2-sysrtc"
+- reg: address range of rtc register set.
+- interrupts: rtc alarm interrupts.
+
+Example:
+	rtc@2000 {
+		compatible = "sirf,prima2-sysrtc";
+		reg = <0x2000 0x1000>;
+		interrupts = <52 53 54>;
+	};
-- 
2.14.2

^ permalink raw reply related

* [PATCH v2 0/6] dt-bindings: rtc: document existing bindings
From: Alexandre Belloni @ 2017-09-27 14:03 UTC (permalink / raw)
  To: linux-rtc; +Cc: linux-kernel, Rob Herring, devicetree, Alexandre Belloni

Document currently undocumented bindings.

Rob, as discussed, all of this can probably go through your tree.

Changes in v2:
 - made a proper series
 - fixed sirf2 example
 - listed the ds1307 compatible that support the trickle charging properties

Alexandre Belloni (6):
  dt-bindings: trivial: Add RTCs
  dt-bindings: rtc: add stericsson,coh901331 bindings
  dt-bindings: rtc: Add sirf,prima2-sysrtc bindings
  dt-bindings: rtc: DS1307 and compatibles are not trivial
  dt-bindings: rtc: Add bindings for m41t80 and compatibles
  dt-bindings: rtc: merge ds1339 in ds1307 documentation

 .../devicetree/bindings/rtc/dallas,ds1339.txt      | 18 ---------
 .../devicetree/bindings/rtc/rtc-ds1307.txt         | 44 ++++++++++++++++++++++
 .../devicetree/bindings/rtc/rtc-m41t80.txt         | 31 +++++++++++++++
 .../devicetree/bindings/rtc/sirf,prima2-sysrtc.txt | 13 +++++++
 .../bindings/rtc/stericsson,coh901331.txt          | 17 +++++++++
 .../devicetree/bindings/trivial-devices.txt        | 13 +++----
 6 files changed, 110 insertions(+), 26 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/rtc/dallas,ds1339.txt
 create mode 100644 Documentation/devicetree/bindings/rtc/rtc-ds1307.txt
 create mode 100644 Documentation/devicetree/bindings/rtc/rtc-m41t80.txt
 create mode 100644 Documentation/devicetree/bindings/rtc/sirf,prima2-sysrtc.txt
 create mode 100644 Documentation/devicetree/bindings/rtc/stericsson,coh901331.txt

-- 
2.14.2

^ permalink raw reply

* Re: [PATCH] dt-bindings: RTC: merge ds1339 in ds1307 documentation
From: Alexandre Belloni @ 2017-09-27 13:58 UTC (permalink / raw)
  To: Rob Herring; +Cc: linux-rtc, linux-kernel, devicetree
In-Reply-To: <20170920205220.ykkxsqrxu5t47px5@rob-hp-laptop>

On 20/09/2017 at 15:52:20 -0500, Rob Herring wrote:
> On Fri, Sep 15, 2017 at 03:45:51AM +0200, Alexandre Belloni wrote:
> > Now that there is documentation for the ds1307 and compatible RTCs, merge
> > the ds1339 documentation in it.
> > 
> > Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
> > ---
> >  .../devicetree/bindings/rtc/dallas,ds1339.txt          | 18 ------------------
> >  Documentation/devicetree/bindings/rtc/rtc-ds1307.txt   |  6 ++++++
> >  2 files changed, 6 insertions(+), 18 deletions(-)
> >  delete mode 100644 Documentation/devicetree/bindings/rtc/dallas,ds1339.txt
> > 
> > diff --git a/Documentation/devicetree/bindings/rtc/dallas,ds1339.txt b/Documentation/devicetree/bindings/rtc/dallas,ds1339.txt
> > deleted file mode 100644
> > index 916f57601a8f..000000000000
> > --- a/Documentation/devicetree/bindings/rtc/dallas,ds1339.txt
> > +++ /dev/null
> > @@ -1,18 +0,0 @@
> > -* Dallas DS1339		I2C Serial Real-Time Clock
> > -
> > -Required properties:
> > -- compatible: Should contain "dallas,ds1339".
> > -- reg: I2C address for chip
> > -
> > -Optional properties:
> > -- trickle-resistor-ohms : Selected resistor for trickle charger
> > -	Values usable for ds1339 are 250, 2000, 4000
> > -	Should be given if trickle charger should be enabled
> > -- trickle-diode-disable : Do not use internal trickle charger diode
> > -	Should be given if internal trickle charger diode should be disabled
> > -Example:
> > -	ds1339: rtc@68 {
> > -		compatible = "dallas,ds1339";
> > -		trickle-resistor-ohms = <250>;
> > -		reg = <0x68>;
> > -	};
> > diff --git a/Documentation/devicetree/bindings/rtc/rtc-ds1307.txt b/Documentation/devicetree/bindings/rtc/rtc-ds1307.txt
> > index d1a820e25cb9..2eacc17a9b8d 100644
> > --- a/Documentation/devicetree/bindings/rtc/rtc-ds1307.txt
> > +++ b/Documentation/devicetree/bindings/rtc/rtc-ds1307.txt
> > @@ -26,6 +26,11 @@ Optional properties:
> >  - clock-output-names: From common clock binding to override the default output
> >                        clock name
> >  - wakeup-source: Enables wake up of host system on alarm
> > +- trickle-resistor-ohms : Selected resistor for trickle charger
> > +	Values usable for ds1339 are 250, 2000, 4000
> > +	Should be given if trickle charger should be enabled
> > +- trickle-diode-disable : Do not use internal trickle charger diode
> > +	Should be given if internal trickle charger diode should be disabled
> 
> Do these apply to all compatibles or just 1339?

I'll send v2 shortly that is clearer about that.

> 
> Rob

-- 
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply

* Re: [PATCH] rtc: sun6i: fix memory leak
From: Alexandre Belloni @ 2017-09-27 13:25 UTC (permalink / raw)
  To: Sudip Mukherjee
  Cc: Alessandro Zummo, Maxime Ripard, Chen-Yu Tsai, linux-kernel,
	linux-rtc, linux-arm-kernel
In-Reply-To: <1505652190-4942-1-git-send-email-sudipm.mukherjee@gmail.com>

Hi,

On 17/09/2017 at 13:43:10 +0100, Sudip Mukherjee wrote:
> If 'clk_data' is not allocated we returned but we failed to free 'rtc'.
> 
> Signed-off-by: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
> ---
>  drivers/rtc/rtc-sun6i.c | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c
> index 3d2216c..5bc28ee 100644
> --- a/drivers/rtc/rtc-sun6i.c
> +++ b/drivers/rtc/rtc-sun6i.c
> @@ -201,8 +201,10 @@ static void __init sun6i_rtc_clk_init(struct device_node *node)
>  
>  	clk_data = kzalloc(sizeof(*clk_data) + (sizeof(*clk_data->hws) * 2),
>  			   GFP_KERNEL);
> -	if (!clk_data)
> +	if (!clk_data) {
> +		kfree(rtc);
>  		return;
> +	}

Please also fix the other occurrence of the issue in the same function.

>  
>  	spin_lock_init(&rtc->lock);
>  
> -- 
> 2.7.4
> 

-- 
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply

* [PATCH v5 5/6] input: Add MediaTek PMIC keys support
From: Chen Zhong @ 2017-09-27 10:44 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring, Lee Jones, Alexandre Belloni
  Cc: Mark Rutland, Matthias Brugger, Eddie Huang, Alessandro Zummo,
	Linus Walleij, Chanwoo Choi, Chen Zhong, Jaechul Lee, Andi Shyti,
	linux-input, devicetree, linux-kernel, linux-arm-kernel,
	linux-mediatek, linux-rtc
In-Reply-To: <1506509048-19032-1-git-send-email-chen.zhong@mediatek.com>

This patch add support to handle MediaTek PMIC MT6397/MT6323 key
interrupts including pwrkey and homekey, also add setting for
long press key shutdown behavior.

Signed-off-by: Chen Zhong <chen.zhong@mediatek.com>
---
 drivers/input/keyboard/Kconfig         |    9 +
 drivers/input/keyboard/Makefile        |    1 +
 drivers/input/keyboard/mtk-pmic-keys.c |  341 ++++++++++++++++++++++++++++++++
 3 files changed, 351 insertions(+)
 create mode 100644 drivers/input/keyboard/mtk-pmic-keys.c

diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 4c4ab1c..bd4e20a 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -756,4 +756,13 @@ config KEYBOARD_BCM
 	  To compile this driver as a module, choose M here: the
 	  module will be called bcm-keypad.
 
+config KEYBOARD_MTK_PMIC
+	tristate "MediaTek PMIC keys support"
+	depends on MFD_MT6397
+	help
+	  Say Y here if you want to use the pmic keys (powerkey/homekey).
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called pmic-keys.
+
 endif
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index d2338ba..20c0b98 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_KEYBOARD_MATRIX)		+= matrix_keypad.o
 obj-$(CONFIG_KEYBOARD_MAX7359)		+= max7359_keypad.o
 obj-$(CONFIG_KEYBOARD_MCS)		+= mcs_touchkey.o
 obj-$(CONFIG_KEYBOARD_MPR121)		+= mpr121_touchkey.o
+obj-$(CONFIG_KEYBOARD_MTK_PMIC) 	+= mtk-pmic-keys.o
 obj-$(CONFIG_KEYBOARD_NEWTON)		+= newtonkbd.o
 obj-$(CONFIG_KEYBOARD_NOMADIK)		+= nomadik-ske-keypad.o
 obj-$(CONFIG_KEYBOARD_NSPIRE)		+= nspire-keypad.o
diff --git a/drivers/input/keyboard/mtk-pmic-keys.c b/drivers/input/keyboard/mtk-pmic-keys.c
new file mode 100644
index 0000000..529fd95
--- /dev/null
+++ b/drivers/input/keyboard/mtk-pmic-keys.c
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2017 MediaTek, Inc.
+ *
+ * Author: Chen Zhong <chen.zhong@mediatek.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/mt6323/registers.h>
+#include <linux/mfd/mt6397/registers.h>
+#include <linux/mfd/mt6397/core.h>
+
+#define MTK_PMIC_PWRKEY_RST_EN_MASK	0x1
+#define MTK_PMIC_PWRKEY_RST_EN_SHIFT	6
+#define MTK_PMIC_HOMEKEY_RST_EN_MASK	0x1
+#define MTK_PMIC_HOMEKEY_RST_EN_SHIFT	5
+#define MTK_PMIC_RST_DU_MASK		0x3
+#define MTK_PMIC_RST_DU_SHIFT		8
+
+#define MTK_PMIC_PWRKEY_RST		\
+	(MTK_PMIC_PWRKEY_RST_EN_MASK << MTK_PMIC_PWRKEY_RST_EN_SHIFT)
+#define MTK_PMIC_HOMEKEY_RST		\
+	(MTK_PMIC_HOMEKEY_RST_EN_MASK << MTK_PMIC_HOMEKEY_RST_EN_SHIFT)
+
+#define MTK_PMIC_PWRKEY_INDEX	0
+#define MTK_PMIC_HOMEKEY_INDEX	1
+#define MTK_PMIC_MAX_KEY_COUNT	2
+
+struct mtk_pmic_keys_regs {
+	u32 deb_reg;
+	u32 deb_mask;
+	u32 intsel_reg;
+	u32 intsel_mask;
+};
+
+#define MTK_PMIC_KEYS_REGS(_deb_reg, _deb_mask,		\
+	_intsel_reg, _intsel_mask)			\
+{							\
+	.deb_reg		= _deb_reg,		\
+	.deb_mask		= _deb_mask,		\
+	.intsel_reg		= _intsel_reg,		\
+	.intsel_mask		= _intsel_mask,		\
+}
+
+struct mtk_pmic_regs {
+	const struct mtk_pmic_keys_regs keys_regs[MTK_PMIC_MAX_KEY_COUNT];
+	u32 pmic_rst_reg;
+};
+
+static const struct mtk_pmic_regs mt6397_regs = {
+	.keys_regs[MTK_PMIC_PWRKEY_INDEX] =
+		MTK_PMIC_KEYS_REGS(MT6397_CHRSTATUS,
+		0x8, MT6397_INT_RSV, 0x10),
+	.keys_regs[MTK_PMIC_HOMEKEY_INDEX] =
+		MTK_PMIC_KEYS_REGS(MT6397_OCSTATUS2,
+		0x10, MT6397_INT_RSV, 0x8),
+	.pmic_rst_reg = MT6397_TOP_RST_MISC,
+};
+
+static const struct mtk_pmic_regs mt6323_regs = {
+	.keys_regs[MTK_PMIC_PWRKEY_INDEX] =
+		MTK_PMIC_KEYS_REGS(MT6323_CHRSTATUS,
+		0x2, MT6323_INT_MISC_CON, 0x10),
+	.keys_regs[MTK_PMIC_HOMEKEY_INDEX] =
+		MTK_PMIC_KEYS_REGS(MT6323_CHRSTATUS,
+		0x4, MT6323_INT_MISC_CON, 0x8),
+	.pmic_rst_reg = MT6323_TOP_RST_MISC,
+};
+
+struct mtk_pmic_keys_info {
+	struct mtk_pmic_keys *keys;
+	const struct mtk_pmic_keys_regs *regs;
+	unsigned int keycode;
+	int irq;
+	bool wakeup:1;
+};
+
+struct mtk_pmic_keys {
+	struct input_dev *input_dev;
+	struct device *dev;
+	struct regmap *regmap;
+	struct mtk_pmic_keys_info keys[MTK_PMIC_MAX_KEY_COUNT];
+};
+
+enum mtk_pmic_keys_lp_mode {
+	LP_DISABLE,
+	LP_ONEKEY,
+	LP_TWOKEY,
+};
+
+static void mtk_pmic_keys_lp_reset_setup(struct mtk_pmic_keys *keys,
+		u32 pmic_rst_reg)
+{
+	int ret;
+	u32 long_press_mode, long_press_debounce;
+
+	ret = of_property_read_u32(keys->dev->of_node,
+		"power-off-time-sec", &long_press_debounce);
+	if (ret)
+		long_press_debounce = 0;
+
+	regmap_update_bits(keys->regmap, pmic_rst_reg,
+			   MTK_PMIC_RST_DU_MASK << MTK_PMIC_RST_DU_SHIFT,
+			   long_press_debounce << MTK_PMIC_RST_DU_SHIFT);
+
+	ret = of_property_read_u32(keys->dev->of_node,
+		"mediatek,long-press-mode", &long_press_mode);
+	if (ret)
+		long_press_mode = LP_DISABLE;
+
+	switch (long_press_mode) {
+	case LP_ONEKEY:
+		regmap_update_bits(keys->regmap, pmic_rst_reg,
+				   MTK_PMIC_PWRKEY_RST,
+				   MTK_PMIC_PWRKEY_RST);
+		regmap_update_bits(keys->regmap, pmic_rst_reg,
+				   MTK_PMIC_HOMEKEY_RST,
+				   0);
+		break;
+	case LP_TWOKEY:
+		regmap_update_bits(keys->regmap, pmic_rst_reg,
+				   MTK_PMIC_PWRKEY_RST,
+				   MTK_PMIC_PWRKEY_RST);
+		regmap_update_bits(keys->regmap, pmic_rst_reg,
+				   MTK_PMIC_HOMEKEY_RST,
+				   MTK_PMIC_HOMEKEY_RST);
+		break;
+	case LP_DISABLE:
+		regmap_update_bits(keys->regmap, pmic_rst_reg,
+				   MTK_PMIC_PWRKEY_RST,
+				   0);
+		regmap_update_bits(keys->regmap, pmic_rst_reg,
+				   MTK_PMIC_HOMEKEY_RST,
+				   0);
+		break;
+	default:
+		break;
+	}
+}
+
+static irqreturn_t mtk_pmic_keys_irq_handler_thread(int irq, void *data)
+{
+	struct mtk_pmic_keys_info *info = data;
+	u32 key_deb, pressed;
+
+	regmap_read(info->keys->regmap, info->regs->deb_reg, &key_deb);
+
+	key_deb &= info->regs->deb_mask;
+
+	pressed = !key_deb;
+
+	input_report_key(info->keys->input_dev, info->keycode, pressed);
+	input_sync(info->keys->input_dev);
+
+	dev_dbg(info->keys->dev, "(%s) key =%d using PMIC\n",
+		 pressed ? "pressed" : "released", info->keycode);
+
+	return IRQ_HANDLED;
+}
+
+static int mtk_pmic_key_setup(struct mtk_pmic_keys *keys,
+		struct mtk_pmic_keys_info *info)
+{
+	int ret;
+
+	info->keys = keys;
+
+	ret = regmap_update_bits(keys->regmap, info->regs->intsel_reg,
+				 info->regs->intsel_mask,
+				 info->regs->intsel_mask);
+	if (ret < 0)
+		return ret;
+
+	ret = devm_request_threaded_irq(keys->dev, info->irq, NULL,
+					mtk_pmic_keys_irq_handler_thread,
+					IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
+					"mtk-pmic-keys", info);
+	if (ret) {
+		dev_err(keys->dev, "Failed to request IRQ: %d: %d\n",
+			info->irq, ret);
+		return ret;
+	}
+
+	input_set_capability(keys->input_dev, EV_KEY, info->keycode);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mtk_pmic_keys_suspend(struct device *dev)
+{
+	struct mtk_pmic_keys *keys = dev_get_drvdata(dev);
+	int index;
+
+	for (index = 0; index < MTK_PMIC_MAX_KEY_COUNT; index++) {
+		if (keys->keys[index].wakeup)
+			enable_irq_wake(keys->keys[index].irq);
+	}
+
+	return 0;
+}
+
+static int mtk_pmic_keys_resume(struct device *dev)
+{
+	struct mtk_pmic_keys *keys = dev_get_drvdata(dev);
+	int index;
+
+	for (index = 0; index < MTK_PMIC_MAX_KEY_COUNT; index++) {
+		if (keys->keys[index].wakeup)
+			disable_irq_wake(keys->keys[index].irq);
+	}
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(mtk_pmic_keys_pm_ops, mtk_pmic_keys_suspend,
+			mtk_pmic_keys_resume);
+
+static const struct of_device_id of_mtk_pmic_keys_match_tbl[] = {
+	{
+		.compatible = "mediatek,mt6397-keys",
+		.data = &mt6397_regs,
+	}, {
+		.compatible = "mediatek,mt6323-keys",
+		.data = &mt6323_regs,
+	}, {
+		/* sentinel */
+	}
+};
+MODULE_DEVICE_TABLE(of, of_mtk_pmic_keys_match_tbl);
+
+static int mtk_pmic_keys_probe(struct platform_device *pdev)
+{
+	int error, index = 0;
+	unsigned int keycount;
+	struct mt6397_chip *pmic_chip = dev_get_drvdata(pdev->dev.parent);
+	struct device_node *node = pdev->dev.of_node, *child;
+	struct mtk_pmic_keys *keys;
+	const struct mtk_pmic_regs *mtk_pmic_regs;
+	struct input_dev *input_dev;
+	const struct of_device_id *of_id =
+		of_match_device(of_mtk_pmic_keys_match_tbl, &pdev->dev);
+
+	keys = devm_kzalloc(&pdev->dev, sizeof(*keys), GFP_KERNEL);
+	if (!keys)
+		return -ENOMEM;
+
+	keys->dev = &pdev->dev;
+	keys->regmap = pmic_chip->regmap;
+	mtk_pmic_regs = of_id->data;
+
+	keys->input_dev = input_dev = devm_input_allocate_device(keys->dev);
+	if (!input_dev) {
+		dev_err(keys->dev, "input allocate device fail.\n");
+		return -ENOMEM;
+	}
+
+	input_dev->name = "mtk-pmic-keys";
+	input_dev->id.bustype = BUS_HOST;
+	input_dev->id.vendor = 0x0001;
+	input_dev->id.product = 0x0001;
+	input_dev->id.version = 0x0001;
+
+	keycount = device_get_child_node_count(keys->dev);
+	if (keycount > MTK_PMIC_MAX_KEY_COUNT) {
+		dev_err(keys->dev, "too many keys defined (%d)\n", keycount);
+		return -EINVAL;
+	}
+
+	for_each_child_of_node(node, child) {
+		keys->keys[index].regs = &mtk_pmic_regs->keys_regs[index];
+
+		keys->keys[index].irq = platform_get_irq(pdev, index);
+		if (keys->keys[index].irq < 0)
+			return keys->keys[index].irq;
+
+		error = of_property_read_u32(child,
+			"linux,keycodes", &keys->keys[index].keycode);
+		if (error) {
+			dev_err(keys->dev,
+				"failed to read key:%d linux,keycode property: %d\n",
+				index, error);
+			return error;
+		}
+
+		if (of_property_read_bool(child, "wakeup-source"))
+			keys->keys[index].wakeup = true;
+
+		error = mtk_pmic_key_setup(keys, &keys->keys[index]);
+		if (error)
+			return error;
+
+		index++;
+	}
+
+	error = input_register_device(input_dev);
+	if (error) {
+		dev_err(&pdev->dev,
+			"register input device failed (%d)\n", error);
+		return error;
+	}
+
+	mtk_pmic_keys_lp_reset_setup(keys, mtk_pmic_regs->pmic_rst_reg);
+
+	platform_set_drvdata(pdev, keys);
+
+	return 0;
+}
+
+static struct platform_driver pmic_keys_pdrv = {
+	.probe = mtk_pmic_keys_probe,
+	.driver = {
+		   .name = "mtk-pmic-keys",
+		   .of_match_table = of_mtk_pmic_keys_match_tbl,
+		   .pm = &mtk_pmic_keys_pm_ops,
+	},
+};
+
+module_platform_driver(pmic_keys_pdrv);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Chen Zhong <chen.zhong@mediatek.com>");
+MODULE_DESCRIPTION("MTK pmic-keys driver v0.1");
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH v5 6/6] mfd: mt6397: Add PMIC keys support to MT6397 driver
From: Chen Zhong @ 2017-09-27 10:44 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring, Lee Jones, Alexandre Belloni
  Cc: Mark Rutland, Matthias Brugger, Eddie Huang, Alessandro Zummo,
	Linus Walleij, Chanwoo Choi, Chen Zhong, Jaechul Lee, Andi Shyti,
	linux-input, devicetree, linux-kernel, linux-arm-kernel,
	linux-mediatek, linux-rtc
In-Reply-To: <1506509048-19032-1-git-send-email-chen.zhong@mediatek.com>

This patch adds compatible strings and interrupts for pmic keys
which serves as child device of MFD.

Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Chen Zhong <chen.zhong@mediatek.com>
---
 drivers/mfd/mt6397-core.c |   22 +++++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)

diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c
index 6546d7f..77b64bd 100644
--- a/drivers/mfd/mt6397-core.c
+++ b/drivers/mfd/mt6397-core.c
@@ -43,6 +43,16 @@
 	},
 };
 
+static const struct resource mt6323_keys_resources[] = {
+	DEFINE_RES_IRQ(MT6323_IRQ_STATUS_PWRKEY),
+	DEFINE_RES_IRQ(MT6323_IRQ_STATUS_FCHRKEY),
+};
+
+static const struct resource mt6397_keys_resources[] = {
+	DEFINE_RES_IRQ(MT6397_IRQ_PWRKEY),
+	DEFINE_RES_IRQ(MT6397_IRQ_HOMEKEY),
+};
+
 static const struct mfd_cell mt6323_devs[] = {
 	{
 		.name = "mt6323-regulator",
@@ -50,6 +60,11 @@
 	}, {
 		.name = "mt6323-led",
 		.of_compatible = "mediatek,mt6323-led"
+	}, {
+		.name = "mtk-pmic-keys",
+		.num_resources = ARRAY_SIZE(mt6323_keys_resources),
+		.resources = mt6323_keys_resources,
+		.of_compatible = "mediatek,mt6323-keys"
 	},
 };
 
@@ -71,7 +86,12 @@
 	}, {
 		.name = "mt6397-pinctrl",
 		.of_compatible = "mediatek,mt6397-pinctrl",
-	},
+	}, {
+		.name = "mtk-pmic-keys",
+		.num_resources = ARRAY_SIZE(mt6397_keys_resources),
+		.resources = mt6397_keys_resources,
+		.of_compatible = "mediatek,mt6397-keys"
+	}
 };
 
 static void mt6397_irq_lock(struct irq_data *data)
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH v5 3/6] dt-bindings: input: Add document bindings for mtk-pmic-keys
From: Chen Zhong @ 2017-09-27 10:44 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring, Lee Jones, Alexandre Belloni
  Cc: Mark Rutland, Matthias Brugger, Eddie Huang, Alessandro Zummo,
	Linus Walleij, Chanwoo Choi, Chen Zhong, Jaechul Lee, Andi Shyti,
	linux-input, devicetree, linux-kernel, linux-arm-kernel,
	linux-mediatek, linux-rtc
In-Reply-To: <1506509048-19032-1-git-send-email-chen.zhong@mediatek.com>

This patch adds the device tree binding documentation for the MediaTek
pmic keys found on PMIC MT6397/MT6323.

Signed-off-by: Chen Zhong <chen.zhong@mediatek.com>
---
 .../devicetree/bindings/input/mtk-pmic-keys.txt    |   43 ++++++++++++++++++++
 1 file changed, 43 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/mtk-pmic-keys.txt

diff --git a/Documentation/devicetree/bindings/input/mtk-pmic-keys.txt b/Documentation/devicetree/bindings/input/mtk-pmic-keys.txt
new file mode 100644
index 0000000..2888d07
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/mtk-pmic-keys.txt
@@ -0,0 +1,43 @@
+MediaTek MT6397/MT6323 PMIC Keys Device Driver
+
+There are two key functions provided by MT6397/MT6323 PMIC, pwrkey
+and homekey. The key functions are defined as the subnode of the function
+node provided by MT6397/MT6323 PMIC that is being defined as one kind
+of Muti-Function Device (MFD)
+
+For MT6397/MT6323 MFD bindings see:
+Documentation/devicetree/bindings/mfd/mt6397.txt
+
+Required properties:
+- compatible: "mediatek,mt6397-keys" or "mediatek,mt6323-keys"
+- linux,keycodes: See Documentation/devicetree/bindings/input/keys.txt
+
+Optional Properties:
+- wakeup-source: See Documentation/devicetree/bindings/power/wakeup-source.txt
+- mediatek,long-press-mode: Long press key shutdown setting, 1 for
+	pwrkey only, 2 for pwrkey/homekey together, others for disabled.
+- power-off-time-sec: See Documentation/devicetree/bindings/input/keys.txt
+
+Example:
+
+	pmic: mt6397 {
+		compatible = "mediatek,mt6397";
+
+		...
+
+		mt6397keys: mt6397keys {
+			compatible = "mediatek,mt6397-keys";
+			mediatek,long-press-mode = <1>;
+			power-off-time-sec = <0>;
+
+			power {
+				linux,keycodes = <116>;
+				wakeup-source;
+			};
+
+			home {
+				linux,keycodes = <114>;
+			};
+		};
+
+	};
-- 
1.7.9.5

^ permalink raw reply related


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