Skip to contentSkip to conversation

Change snvs rtc and poweroff to use syscon add pwrkey driver

PATCH New Frank.Li at freescale.com 6 participants 18 messages May 26, 2015 – Jun 29, 2015

Subsystems (6)
lore
Share & tools
lore
#0 lore raw

v5 cover Change snvs rtc and poweroff to use syscon add pwrkey driver

From: Frank Li <[email protected]>

Tested old and new dts in imx6sx platform.

Change from v5 to v6
- use dev_warn and dev_err
- use phandle regmap
- update document and dts for phandle regmap
- use data variable in rtc-snvs.c

Change from v3 to v4
- fix align
- change to pr_warn

Change from v2 to v3
- Keep snvs rtc compatiblity with old dts file
- use general syscon-poweroff instead of modifying snvs-poweroff.c

Change from v1 to v2
- Change simple-bus to simple-mfd
- RTC remove lock.
- RTC remove comments "These register offsets are relative to LP (Low Power) range"
- power off, remove extra space

Frank Li (5):
rtc: arm: imx: snvs: change use syscon to access register
Document: dt: fsl: snvs: change support syscon
ARM: dts: imx: update snvs to use syscon access register
Document: devicetree: input: imx: i.mx snvs power device tree bindings
ARM: dts: imx6sx: enable snvs power key

Robin Gong (1):
input: keyboard: imx: add snvs power key driver

 .../devicetree/bindings/crypto/fsl-sec4.txt        |  91 +++++++--
 .../devicetree/bindings/input/snvs-pwrkey.txt      |   1 +
 arch/arm/boot/dts/imx6qdl.dtsi                     |  21 +-
 arch/arm/boot/dts/imx6sl.dtsi                      |  21 +-
 arch/arm/boot/dts/imx6sx.dtsi                      |  28 ++-
 arch/arm/boot/dts/vfxxx.dtsi                       |  11 +-
 drivers/input/keyboard/Kconfig                     |  11 +
 drivers/input/keyboard/Makefile                    |   1 +
 drivers/input/keyboard/snvs_pwrkey.c               | 227 +++++++++++++++++++++
 drivers/rtc/rtc-snvs.c                             | 132 ++++++------
 10 files changed, 431 insertions(+), 113 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/input/snvs-pwrkey.txt
 create mode 100644 drivers/input/keyboard/snvs_pwrkey.c
Signature · 1 line
1.9.1
#1 lore raw

PATCH v5 1/6 rtc: arm: imx: snvs: change use syscon to access register

From: Frank Li <[email protected]>

snvs included rtc, on/off key, power-off module
change to syscon to access register

---
 drivers/rtc/rtc-snvs.c | 132 +++++++++++++++++++++++++------------------------
 1 file changed, 67 insertions(+), 65 deletions(-)
drivers/rtc/rtc-snvs.c +67 -65
index 0479e80..fa67d7c 100644
@@ -18,6 +18,10 @@
 #include <linux/platform_device.h>
 #include <linux/rtc.h>
 #include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#define SNVS_LPREGISTER_OFFSET	0x34
 
 /* These register offsets are relative to LP (Low Power) range */
 #define SNVS_LPCR		0x04
@@ -37,31 +41,36 @@
 
 struct snvs_rtc_data {
 	struct rtc_device *rtc;
-	void __iomem *ioaddr;
+	struct regmap *regmap;
+	int offset;
 	int irq;
-	spinlock_t lock;
 	struct clk *clk;
 };
 
-static u32 rtc_read_lp_counter(void __iomem *ioaddr)
+static u32 rtc_read_lp_counter(struct snvs_rtc_data *data)
 {
 	u64 read1, read2;
+	u32 val;
 
 	do {
-		read1 = readl(ioaddr + SNVS_LPSRTCMR);
+		regmap_read(data->regmap, data->offset + SNVS_LPSRTCMR, &val);
+		read1 = val;
 		read1 <<= 32;
-		read1 |= readl(ioaddr + SNVS_LPSRTCLR);
+		regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &val);
+		read1 |= val;
 
-		read2 = readl(ioaddr + SNVS_LPSRTCMR);
+		regmap_read(data->regmap, data->offset + SNVS_LPSRTCMR, &val);
+		read2 = val;
 		read2 <<= 32;
-		read2 |= readl(ioaddr + SNVS_LPSRTCLR);
+		regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &val);
+		read2 |= val;
 	} while (read1 != read2);
 
 	/* Convert 47-bit counter to 32-bit raw second count */
 	return (u32) (read1 >> CNTR_TO_SECS_SH);
 }
 
-static void rtc_write_sync_lp(void __iomem *ioaddr)
+static void rtc_write_sync_lp(struct snvs_rtc_data *data)
 {
 	u32 count1, count2, count3;
 	int i;
@@ -69,15 +78,15 @@ static void rtc_write_sync_lp(void __iomem *ioaddr)
 	/* Wait for 3 CKIL cycles */
 	for (i = 0; i < 3; i++) {
 		do {
-			count1 = readl(ioaddr + SNVS_LPSRTCLR);
-			count2 = readl(ioaddr + SNVS_LPSRTCLR);
+			regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count1);
+			regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count2);
 		} while (count1 != count2);
 
 		/* Now wait until counter value changes */
 		do {
 			do {
-				count2 = readl(ioaddr + SNVS_LPSRTCLR);
-				count3 = readl(ioaddr + SNVS_LPSRTCLR);
+				regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count2);
+				regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count3);
 			} while (count2 != count3);
 		} while (count3 == count1);
 	}
@@ -85,23 +94,14 @@ static void rtc_write_sync_lp(void __iomem *ioaddr)
 
 static int snvs_rtc_enable(struct snvs_rtc_data *data, bool enable)
 {
-	unsigned long flags;
 	int timeout = 1000;
 	u32 lpcr;
 
-	spin_lock_irqsave(&data->lock, flags);
-
-	lpcr = readl(data->ioaddr + SNVS_LPCR);
-	if (enable)
-		lpcr |= SNVS_LPCR_SRTC_ENV;
-	else
-		lpcr &= ~SNVS_LPCR_SRTC_ENV;
-	writel(lpcr, data->ioaddr + SNVS_LPCR);
-
-	spin_unlock_irqrestore(&data->lock, flags);
+	regmap_update_bits(data->regmap, data->offset + SNVS_LPCR, SNVS_LPCR_SRTC_ENV,
+			   enable ? SNVS_LPCR_SRTC_ENV : 0);
 
 	while (--timeout) {
-		lpcr = readl(data->ioaddr + SNVS_LPCR);
+		regmap_read(data->regmap, data->offset + SNVS_LPCR, &lpcr);
 
 		if (enable) {
 			if (lpcr & SNVS_LPCR_SRTC_ENV)
@@ -121,7 +121,7 @@ static int snvs_rtc_enable(struct snvs_rtc_data *data, bool enable)
 static int snvs_rtc_read_time(struct device *dev, struct rtc_time *tm)
 {
 	struct snvs_rtc_data *data = dev_get_drvdata(dev);
-	unsigned long time = rtc_read_lp_counter(data->ioaddr);
+	unsigned long time = rtc_read_lp_counter(data);
 
 	rtc_time_to_tm(time, tm);
 
@@ -139,8 +139,8 @@ static int snvs_rtc_set_time(struct device *dev, struct rtc_time *tm)
 	snvs_rtc_enable(data, false);
 
 	/* Write 32-bit time to 47-bit timer, leaving 15 LSBs blank */
-	writel(time << CNTR_TO_SECS_SH, data->ioaddr + SNVS_LPSRTCLR);
-	writel(time >> (32 - CNTR_TO_SECS_SH), data->ioaddr + SNVS_LPSRTCMR);
+	regmap_write(data->regmap, data->offset + SNVS_LPSRTCLR, time << CNTR_TO_SECS_SH);
+	regmap_write(data->regmap, data->offset + SNVS_LPSRTCMR, time >> (32 - CNTR_TO_SECS_SH));
 
 	/* Enable RTC again */
 	snvs_rtc_enable(data, true);
@@ -153,10 +153,10 @@ static int snvs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 	struct snvs_rtc_data *data = dev_get_drvdata(dev);
 	u32 lptar, lpsr;
 
-	lptar = readl(data->ioaddr + SNVS_LPTAR);
+	regmap_read(data->regmap, data->offset + SNVS_LPTAR, &lptar);
 	rtc_time_to_tm(lptar, &alrm->time);
 
-	lpsr = readl(data->ioaddr + SNVS_LPSR);
+	regmap_read(data->regmap, data->offset + SNVS_LPSR, &lpsr);
 	alrm->pending = (lpsr & SNVS_LPSR_LPTA) ? 1 : 0;
 
 	return 0;
@@ -165,21 +165,12 @@ static int snvs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 static int snvs_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
 {
 	struct snvs_rtc_data *data = dev_get_drvdata(dev);
-	u32 lpcr;
-	unsigned long flags;
-
-	spin_lock_irqsave(&data->lock, flags);
 
-	lpcr = readl(data->ioaddr + SNVS_LPCR);
-	if (enable)
-		lpcr |= (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN);
-	else
-		lpcr &= ~(SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN);
-	writel(lpcr, data->ioaddr + SNVS_LPCR);
+	regmap_update_bits(data->regmap, data->offset + SNVS_LPCR,
+			   (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN),
+			   enable ? (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN) : 0);
 
-	spin_unlock_irqrestore(&data->lock, flags);
-
-	rtc_write_sync_lp(data->ioaddr);
+	rtc_write_sync_lp(data);
 
 	return 0;
 }
@@ -189,24 +180,14 @@ static int snvs_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 	struct snvs_rtc_data *data = dev_get_drvdata(dev);
 	struct rtc_time *alrm_tm = &alrm->time;
 	unsigned long time;
-	unsigned long flags;
-	u32 lpcr;
 
 	rtc_tm_to_time(alrm_tm, &time);
 
-	spin_lock_irqsave(&data->lock, flags);
-
-	/* Have to clear LPTA_EN before programming new alarm time in LPTAR */
-	lpcr = readl(data->ioaddr + SNVS_LPCR);
-	lpcr &= ~SNVS_LPCR_LPTA_EN;
-	writel(lpcr, data->ioaddr + SNVS_LPCR);
-
-	spin_unlock_irqrestore(&data->lock, flags);
-
-	writel(time, data->ioaddr + SNVS_LPTAR);
+	regmap_update_bits(data->regmap, data->offset + SNVS_LPCR, SNVS_LPCR_LPTA_EN, 0);
+	regmap_write(data->regmap, data->offset + SNVS_LPTAR, time);
 
 	/* Clear alarm interrupt status bit */
-	writel(SNVS_LPSR_LPTA, data->ioaddr + SNVS_LPSR);
+	regmap_write(data->regmap, data->offset + SNVS_LPSR, SNVS_LPSR_LPTA);
 
 	return snvs_rtc_alarm_irq_enable(dev, alrm->enabled);
 }
@@ -226,7 +207,7 @@ static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id)
 	u32 lpsr;
 	u32 events = 0;
 
-	lpsr = readl(data->ioaddr + SNVS_LPSR);
+	regmap_read(data->regmap, data->offset + SNVS_LPSR, &lpsr);
 
 	if (lpsr & SNVS_LPSR_LPTA) {
 		events |= (RTC_AF | RTC_IRQF);
@@ -238,25 +219,48 @@ static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id)
 	}
 
 	/* clear interrupt status */
-	writel(lpsr, data->ioaddr + SNVS_LPSR);
+	regmap_write(data->regmap, data->offset + SNVS_LPSR, lpsr);
 
 	return events ? IRQ_HANDLED : IRQ_NONE;
 }
 
+static const struct regmap_config snvs_rtc_config = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+};
+
 static int snvs_rtc_probe(struct platform_device *pdev)
 {
 	struct snvs_rtc_data *data;
 	struct resource *res;
 	int ret;
+	void __iomem *mmio;
 
 	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
 	if (!data)
 		return -ENOMEM;
 
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	data->ioaddr = devm_ioremap_resource(&pdev->dev, res);
-	if (IS_ERR(data->ioaddr))
-		return PTR_ERR(data->ioaddr);
+	data->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "regmap");
+
+	if (IS_ERR(data->regmap)) {
+		dev_warn(&pdev->dev, "snvs rtc: you use old dts file, please update it\n");
+		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+		mmio = devm_ioremap_resource(&pdev->dev, res);
+		if (IS_ERR(mmio))
+			return PTR_ERR(mmio);
+
+		data->regmap = devm_regmap_init_mmio(&pdev->dev, mmio, &snvs_rtc_config);
+	} else {
+		data->offset = SNVS_LPREGISTER_OFFSET;
+		of_property_read_u32(pdev->dev.of_node, "offset", &data->offset);
+	}
+
+	if (!data->regmap) {
+		dev_err(&pdev->dev, "Can't find snvs syscon\n");
+		return -ENODEV;
+	}
 
 	data->irq = platform_get_irq(pdev, 0);
 	if (data->irq < 0)
@@ -276,13 +280,11 @@ static int snvs_rtc_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, data);
 
-	spin_lock_init(&data->lock);
-
 	/* Initialize glitch detect */
-	writel(SNVS_LPPGDR_INIT, data->ioaddr + SNVS_LPPGDR);
+	regmap_write(data->regmap, data->offset + SNVS_LPPGDR, SNVS_LPPGDR_INIT);
 
 	/* Clear interrupt status */
-	writel(0xffffffff, data->ioaddr + SNVS_LPSR);
+	regmap_write(data->regmap, data->offset + SNVS_LPSR, 0xffffffff);
 
 	/* Enable RTC */
 	snvs_rtc_enable(data, true);
Signature · 1 line
1.9.1
#2 lore raw

PATCH v5 1/6 rtc: arm: imx: snvs: change use syscon to access register

Quoted 9 lines · On Tue, May 26, 2015 at 11:25 AM, <[email protected]> wrote:

From: Frank Li <[email protected]>

snvs included rtc, on/off key, power-off module
change to syscon to access register

---
 drivers/rtc/rtc-snvs.c | 132 +++++++++++++++++++++++++------------------------
 1 file changed, 67 insertions(+), 65 deletions(-)

Alexandre Belloni

I think you merge this patch because shawn guo already Acked.

best regards
Frank Li

Quoted 277 lines
drivers/rtc/rtc-snvs.c +67 -66
index 0479e80..fa67d7c 100644
@@ -18,6 +18,10 @@
 #include <linux/platform_device.h>
 #include <linux/rtc.h>
 #include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#define SNVS_LPREGISTER_OFFSET 0x34
 
 /* These register offsets are relative to LP (Low Power) range */
 #define SNVS_LPCR              0x04
@@ -37,31 +41,36 @@
 
 struct snvs_rtc_data {
        struct rtc_device *rtc;
-       void __iomem *ioaddr;
+       struct regmap *regmap;
+       int offset;
        int irq;
-       spinlock_t lock;
        struct clk *clk;
 };
 
-static u32 rtc_read_lp_counter(void __iomem *ioaddr)
+static u32 rtc_read_lp_counter(struct snvs_rtc_data *data)
 {
        u64 read1, read2;
+       u32 val;
 
        do {
-               read1 = readl(ioaddr + SNVS_LPSRTCMR);
+               regmap_read(data->regmap, data->offset + SNVS_LPSRTCMR, &val);
+               read1 = val;
                read1 <<= 32;
-               read1 |= readl(ioaddr + SNVS_LPSRTCLR);
+               regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &val);
+               read1 |= val;
 
-               read2 = readl(ioaddr + SNVS_LPSRTCMR);
+               regmap_read(data->regmap, data->offset + SNVS_LPSRTCMR, &val);
+               read2 = val;
                read2 <<= 32;
-               read2 |= readl(ioaddr + SNVS_LPSRTCLR);
+               regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &val);
+               read2 |= val;
        } while (read1 != read2);
 
        /* Convert 47-bit counter to 32-bit raw second count */
        return (u32) (read1 >> CNTR_TO_SECS_SH);
 }
 
-static void rtc_write_sync_lp(void __iomem *ioaddr)
+static void rtc_write_sync_lp(struct snvs_rtc_data *data)
 {
        u32 count1, count2, count3;
        int i;
@@ -69,15 +78,15 @@ static void rtc_write_sync_lp(void __iomem *ioaddr)
        /* Wait for 3 CKIL cycles */
        for (i = 0; i < 3; i++) {
                do {
-                       count1 = readl(ioaddr + SNVS_LPSRTCLR);
-                       count2 = readl(ioaddr + SNVS_LPSRTCLR);
+                       regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count1);
+                       regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count2);
                } while (count1 != count2);
 
                /* Now wait until counter value changes */
                do {
                        do {
-                               count2 = readl(ioaddr + SNVS_LPSRTCLR);
-                               count3 = readl(ioaddr + SNVS_LPSRTCLR);
+                               regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count2);
+                               regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count3);
                        } while (count2 != count3);
                } while (count3 == count1);
        }
@@ -85,23 +94,14 @@ static void rtc_write_sync_lp(void __iomem *ioaddr)
 
 static int snvs_rtc_enable(struct snvs_rtc_data *data, bool enable)
 {
-       unsigned long flags;
        int timeout = 1000;
        u32 lpcr;
 
-       spin_lock_irqsave(&data->lock, flags);
-
-       lpcr = readl(data->ioaddr + SNVS_LPCR);
-       if (enable)
-               lpcr |= SNVS_LPCR_SRTC_ENV;
-       else
-               lpcr &= ~SNVS_LPCR_SRTC_ENV;
-       writel(lpcr, data->ioaddr + SNVS_LPCR);
-
-       spin_unlock_irqrestore(&data->lock, flags);
+       regmap_update_bits(data->regmap, data->offset + SNVS_LPCR, SNVS_LPCR_SRTC_ENV,
+                          enable ? SNVS_LPCR_SRTC_ENV : 0);
 
        while (--timeout) {
-               lpcr = readl(data->ioaddr + SNVS_LPCR);
+               regmap_read(data->regmap, data->offset + SNVS_LPCR, &lpcr);
 
                if (enable) {
                        if (lpcr & SNVS_LPCR_SRTC_ENV)
@@ -121,7 +121,7 @@ static int snvs_rtc_enable(struct snvs_rtc_data *data, bool enable)
 static int snvs_rtc_read_time(struct device *dev, struct rtc_time *tm)
 {
        struct snvs_rtc_data *data = dev_get_drvdata(dev);
-       unsigned long time = rtc_read_lp_counter(data->ioaddr);
+       unsigned long time = rtc_read_lp_counter(data);
 
        rtc_time_to_tm(time, tm);
 
@@ -139,8 +139,8 @@ static int snvs_rtc_set_time(struct device *dev, struct rtc_time *tm)
        snvs_rtc_enable(data, false);
 
        /* Write 32-bit time to 47-bit timer, leaving 15 LSBs blank */
-       writel(time << CNTR_TO_SECS_SH, data->ioaddr + SNVS_LPSRTCLR);
-       writel(time >> (32 - CNTR_TO_SECS_SH), data->ioaddr + SNVS_LPSRTCMR);
+       regmap_write(data->regmap, data->offset + SNVS_LPSRTCLR, time << CNTR_TO_SECS_SH);
+       regmap_write(data->regmap, data->offset + SNVS_LPSRTCMR, time >> (32 - CNTR_TO_SECS_SH));
 
        /* Enable RTC again */
        snvs_rtc_enable(data, true);
@@ -153,10 +153,10 @@ static int snvs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
        struct snvs_rtc_data *data = dev_get_drvdata(dev);
        u32 lptar, lpsr;
 
-       lptar = readl(data->ioaddr + SNVS_LPTAR);
+       regmap_read(data->regmap, data->offset + SNVS_LPTAR, &lptar);
        rtc_time_to_tm(lptar, &alrm->time);
 
-       lpsr = readl(data->ioaddr + SNVS_LPSR);
+       regmap_read(data->regmap, data->offset + SNVS_LPSR, &lpsr);
        alrm->pending = (lpsr & SNVS_LPSR_LPTA) ? 1 : 0;
 
        return 0;
@@ -165,21 +165,12 @@ static int snvs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 static int snvs_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
 {
        struct snvs_rtc_data *data = dev_get_drvdata(dev);
-       u32 lpcr;
-       unsigned long flags;
-
-       spin_lock_irqsave(&data->lock, flags);
 
-       lpcr = readl(data->ioaddr + SNVS_LPCR);
-       if (enable)
-               lpcr |= (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN);
-       else
-               lpcr &= ~(SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN);
-       writel(lpcr, data->ioaddr + SNVS_LPCR);
+       regmap_update_bits(data->regmap, data->offset + SNVS_LPCR,
+                          (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN),
+                          enable ? (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN) : 0);
 
-       spin_unlock_irqrestore(&data->lock, flags);
-
-       rtc_write_sync_lp(data->ioaddr);
+       rtc_write_sync_lp(data);
 
        return 0;
 }
@@ -189,24 +180,14 @@ static int snvs_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
        struct snvs_rtc_data *data = dev_get_drvdata(dev);
        struct rtc_time *alrm_tm = &alrm->time;
        unsigned long time;
-       unsigned long flags;
-       u32 lpcr;
 
        rtc_tm_to_time(alrm_tm, &time);
 
-       spin_lock_irqsave(&data->lock, flags);
-
-       /* Have to clear LPTA_EN before programming new alarm time in LPTAR */
-       lpcr = readl(data->ioaddr + SNVS_LPCR);
-       lpcr &= ~SNVS_LPCR_LPTA_EN;
-       writel(lpcr, data->ioaddr + SNVS_LPCR);
-
-       spin_unlock_irqrestore(&data->lock, flags);
-
-       writel(time, data->ioaddr + SNVS_LPTAR);
+       regmap_update_bits(data->regmap, data->offset + SNVS_LPCR, SNVS_LPCR_LPTA_EN, 0);
+       regmap_write(data->regmap, data->offset + SNVS_LPTAR, time);
 
        /* Clear alarm interrupt status bit */
-       writel(SNVS_LPSR_LPTA, data->ioaddr + SNVS_LPSR);
+       regmap_write(data->regmap, data->offset + SNVS_LPSR, SNVS_LPSR_LPTA);
 
        return snvs_rtc_alarm_irq_enable(dev, alrm->enabled);
 }
@@ -226,7 +207,7 @@ static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id)
        u32 lpsr;
        u32 events = 0;
 
-       lpsr = readl(data->ioaddr + SNVS_LPSR);
+       regmap_read(data->regmap, data->offset + SNVS_LPSR, &lpsr);
 
        if (lpsr & SNVS_LPSR_LPTA) {
                events |= (RTC_AF | RTC_IRQF);
@@ -238,25 +219,48 @@ static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id)
        }
 
        /* clear interrupt status */
-       writel(lpsr, data->ioaddr + SNVS_LPSR);
+       regmap_write(data->regmap, data->offset + SNVS_LPSR, lpsr);
 
        return events ? IRQ_HANDLED : IRQ_NONE;
 }
 
+static const struct regmap_config snvs_rtc_config = {
+       .reg_bits = 32,
+       .val_bits = 32,
+       .reg_stride = 4,
+};
+
 static int snvs_rtc_probe(struct platform_device *pdev)
 {
        struct snvs_rtc_data *data;
        struct resource *res;
        int ret;
+       void __iomem *mmio;
 
        data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
        if (!data)
                return -ENOMEM;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       data->ioaddr = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(data->ioaddr))
-               return PTR_ERR(data->ioaddr);
+       data->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "regmap");
+
+       if (IS_ERR(data->regmap)) {
+               dev_warn(&pdev->dev, "snvs rtc: you use old dts file, please update it\n");
+               res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+               mmio = devm_ioremap_resource(&pdev->dev, res);
+               if (IS_ERR(mmio))
+                       return PTR_ERR(mmio);
+
+               data->regmap = devm_regmap_init_mmio(&pdev->dev, mmio, &snvs_rtc_config);
+       } else {
+               data->offset = SNVS_LPREGISTER_OFFSET;
+               of_property_read_u32(pdev->dev.of_node, "offset", &data->offset);
+       }
+
+       if (!data->regmap) {
+               dev_err(&pdev->dev, "Can't find snvs syscon\n");
+               return -ENODEV;
+       }
 
        data->irq = platform_get_irq(pdev, 0);
        if (data->irq < 0)
@@ -276,13 +280,11 @@ static int snvs_rtc_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, data);
 
-       spin_lock_init(&data->lock);
-
        /* Initialize glitch detect */
-       writel(SNVS_LPPGDR_INIT, data->ioaddr + SNVS_LPPGDR);
+       regmap_write(data->regmap, data->offset + SNVS_LPPGDR, SNVS_LPPGDR_INIT);
 
        /* Clear interrupt status */
-       writel(0xffffffff, data->ioaddr + SNVS_LPSR);
+       regmap_write(data->regmap, data->offset + SNVS_LPSR, 0xffffffff);
 
        /* Enable RTC */
        snvs_rtc_enable(data, true);
--

1.9.1

#3 lore raw

PATCH v5 1/6 rtc: arm: imx: snvs: change use syscon to access register

In reply to Frank.Li at freescale.com
Quoted 6 lines · Frank.Li at freescale.com

From: Frank Li <[email protected]>

snvs included rtc, on/off key, power-off module
change to syscon to access register

Shawn, when applying could you please change the subject to:
rtc: snvs: use syscon to access registers

Thanks,

Signature · 3 lines
Alexandre Belloni, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
#4 lore raw

PATCH v5 1/6 rtc: arm: imx: snvs: change use syscon to access register

Quoted 11 lines · Alexandre Belloni
Quoted 6 lines · Frank.Li at freescale.com

From: Frank Li <[email protected]>

snvs included rtc, on/off key, power-off module
change to syscon to access register

Shawn, when applying could you please change the subject to:
rtc: snvs: use syscon to access registers

Okay, will do.

Shawn

#5 lore raw

PATCH v5 2/6 Document: dt: fsl: snvs: change support syscon

In reply to Frank.Li at freescale.com

From: Frank Li <[email protected]>

snvs actually is multi fucntion driver.
Change to use syscon to access register.
Change snvs parent interrupt to option because single function may have seperated irq number.

---
 .../devicetree/bindings/crypto/fsl-sec4.txt        | 42 +++++++++++++++-------
 1 file changed, 29 insertions(+), 13 deletions(-)
Documentation/devicetree/bindings/crypto/fsl-sec4.txt +29 -13
index e402277..b21e7c2 100644
@@ -288,12 +288,13 @@ Secure Non-Volatile Storage (SNVS) Node
     Node defines address range and the associated
     interrupt for the SNVS function.  This function
     monitors security state information & reports
-    security violations.
+    security violations. This also included rtc,
+    system power off and ON/OFF key.
 
   - compatible
       Usage: required
       Value type: <string>
-      Definition: Must include "fsl,sec-v4.0-mon".
+      Definition: Must include "fsl,sec-v4.0-mon" and "syscon".
 
   - reg
       Usage: required
@@ -324,7 +325,7 @@ Secure Non-Volatile Storage (SNVS) Node
            the child address, parent address, & length.
 
    - interrupts
-      Usage: required
+      Usage: optional
       Value type: <prop_encoded-array>
       Definition:  Specifies the interrupts generated by this
            device.  The value of the interrupts property
@@ -341,7 +342,7 @@ Secure Non-Volatile Storage (SNVS) Node
 
 EXAMPLE
 	sec_mon at 314000 {
-		compatible = "fsl,sec-v4.0-mon";
+		compatible = "fsl,sec-v4.0-mon", "syscon";
 		reg = <0x314000 0x1000>;
 		ranges = <0 0x314000 0x1000>;
 		interrupt-parent = <&mpic>;
@@ -358,16 +359,31 @@ Secure Non-Volatile Storage (SNVS) Low Power (LP) RTC Node
       Value type: <string>
       Definition: Must include "fsl,sec-v4.0-mon-rtc-lp".
 
-  - reg
+  - interrupts
       Usage: required
-      Value type: <prop-encoded-array>
-      Definition: A standard property.  Specifies the physical
-          address and length of the SNVS LP configuration registers.
+      Value type: <prop_encoded-array>
+      Definition: Specifies the interrupts generated by this
+	   device.  The value of the interrupts property
+	   consists of one interrupt specifier. The format
+	   of the specifier is defined by the binding document
+	   describing the node's interrupt parent.
+
+ - regmap
+	Usage: required
+	Value type: <phandle>
+	Definition: this is phandle to the register map node.
+
+ - offset
+	Usage: option
+	value type: <u32>
+	Definition: LP register offset. default it is 0x34.
 
 EXAMPLE
-	sec_mon_rtc_lp at 314000 {
+	sec_mon_rtc_lp at 1 {
 		compatible = "fsl,sec-v4.0-mon-rtc-lp";
-		reg = <0x34 0x58>;
+		interrupts = <93 2>;
+		regmap = <&snvs>;
+		offset = <0x34>;
 	};
 
 =====================================================================
@@ -443,12 +459,12 @@ FULL EXAMPLE
 		compatible = "fsl,sec-v4.0-mon";
 		reg = <0x314000 0x1000>;
 		ranges = <0 0x314000 0x1000>;
-		interrupt-parent = <&mpic>;
-		interrupts = <93 2>;
 
 		sec_mon_rtc_lp at 34 {
 			compatible = "fsl,sec-v4.0-mon-rtc-lp";
-			reg = <0x34 0x58>;
+			regmap = <&sec_mon>;
+			offset = <0x34>;
+			interrupts = <93 2>;
 		};
 	};
 
Signature · 1 line
1.9.1
#6 lore raw

PATCH v5 3/6 ARM: dts: imx: update snvs to use syscon access register

In reply to Frank.Li at freescale.com

From: Frank Li <[email protected]>

snvs is MFP device. Change dts to use syscon to allocate register resource. snvs power off also switch to common syscon-poweroff

---
 arch/arm/boot/dts/imx6qdl.dtsi | 21 +++++++++++----------
 arch/arm/boot/dts/imx6sl.dtsi  | 21 +++++++++++----------
 arch/arm/boot/dts/imx6sx.dtsi  | 19 ++++++++++---------
 arch/arm/boot/dts/vfxxx.dtsi   | 11 +++++------
 4 files changed, 37 insertions(+), 35 deletions(-)
arch/arm/boot/dts/imx6qdl.dtsi +11 -10
index 0482a66..6e53709 100644
@@ -686,22 +686,23 @@
 				fsl,anatop = <&anatop>;
 			};
 
-			snvs at 020cc000 {
-				compatible = "fsl,sec-v4.0-mon", "simple-bus";
-				#address-cells = <1>;
-				#size-cells = <1>;
-				ranges = <0 0x020cc000 0x4000>;
+			snvs: snvs at 020cc000 {
+				compatible = "fsl,sec-v4.0-mon", "syscon", "simple-mfd";
+				reg = <0x020cc000 0x4000>;
 
-				snvs_rtc: snvs-rtc-lp at 34 {
+				snvs_rtc: snvs-rtc-lp {
 					compatible = "fsl,sec-v4.0-mon-rtc-lp";
-					reg = <0x34 0x58>;
+					regmap = <&snvs>;
+					offset = <0x34>;
 					interrupts = <0 19 IRQ_TYPE_LEVEL_HIGH>,
 						     <0 20 IRQ_TYPE_LEVEL_HIGH>;
 				};
 
-				snvs_poweroff: snvs-poweroff at 38 {
-					compatible = "fsl,sec-v4.0-poweroff";
-					reg = <0x38 0x4>;
+				snvs_poweroff: snvs-poweroff {
+					compatible = "syscon-poweroff";
+					regmap = <&snvs>;
+					offset = <0x38>;
+					mask = <0x60>;
 					status = "disabled";
 				};
 			};
arch/arm/boot/dts/imx6sl.dtsi +11 -10
index a78e715..320a27f 100644
@@ -563,22 +563,23 @@
 				fsl,anatop = <&anatop>;
 			};
 
-			snvs at 020cc000 {
-				compatible = "fsl,sec-v4.0-mon", "simple-bus";
-				#address-cells = <1>;
-				#size-cells = <1>;
-				ranges = <0 0x020cc000 0x4000>;
+			snvs: snvs at 020cc000 {
+				compatible = "fsl,sec-v4.0-mon", "syscon", "simple-mfd";
+				reg = <0x020cc000 0x4000>;
 
-				snvs_rtc: snvs-rtc-lp at 34 {
+				snvs_rtc: snvs-rtc-lp {
 					compatible = "fsl,sec-v4.0-mon-rtc-lp";
-					reg = <0x34 0x58>;
+					regmap = <&snvs>;
+					offset = <0x34>;
 					interrupts = <0 19 IRQ_TYPE_LEVEL_HIGH>,
 						     <0 20 IRQ_TYPE_LEVEL_HIGH>;
 				};
 
-				snvs_poweroff: snvs-poweroff at 38 {
-					compatible = "fsl,sec-v4.0-poweroff";
-					reg = <0x38 0x4>;
+				snvs_poweroff: snvs-poweroff {
+					compatible = "syscon-poweroff";
+					regmap = <&snvs>;
+					offset = <0x38>;
+					mask = <0x60>;
 					status = "disabled";
 				};
 			};
arch/arm/boot/dts/imx6sx.dtsi +10 -9
index 708175d..f8f0819 100644
@@ -662,20 +662,21 @@
 			};
 
 			snvs: snvs at 020cc000 {
-				compatible = "fsl,sec-v4.0-mon", "simple-bus";
-				#address-cells = <1>;
-				#size-cells = <1>;
-				ranges = <0 0x020cc000 0x4000>;
+				compatible = "fsl,sec-v4.0-mon", "syscon", "simple-mfd";
+				reg = <0x020cc000 0x4000>;
 
-				snvs_rtc: snvs-rtc-lp at 34 {
+				snvs_rtc: snvs-rtc-lp {
 					compatible = "fsl,sec-v4.0-mon-rtc-lp";
-					reg = <0x34 0x58>;
+					regmap = <&snvs>;
+					offset = <0x34>;
 					interrupts = <GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 20 IRQ_TYPE_LEVEL_HIGH>;
 				};
 
-				snvs_poweroff: snvs-poweroff at 38 {
-					compatible = "fsl,sec-v4.0-poweroff";
-					reg = <0x38 0x4>;
+				snvs_poweroff: snvs-poweroff {
+					compatible = "syscon-poweroff";
+					regmap = <&snvs>;
+					offset = <0x38>;
+					mask = <0x60>;
 					status = "disabled";
 				};
 			};
arch/arm/boot/dts/vfxxx.dtsi +5 -6
index 4aa3351..23b7243 100644
@@ -404,14 +404,13 @@
 			};
 
 			snvs0: snvs at 400a7000 {
-			    compatible = "fsl,sec-v4.0-mon", "simple-bus";
-				#address-cells = <1>;
-				#size-cells = <1>;
-				ranges = <0 0x400a7000 0x2000>;
+			    compatible = "fsl,sec-v4.0-mon", "syscon", "simple-mfd";
+				reg = <0x400a7000 0x2000>;
 
-				snvsrtc: snvs-rtc-lp at 34 {
+				snvsrtc: snvs-rtc-lp {
 					compatible = "fsl,sec-v4.0-mon-rtc-lp";
-					reg = <0x34 0x58>;
+					regmap = <&snvs0>;
+					offset = <0x34>;
 					interrupts = <100 IRQ_TYPE_LEVEL_HIGH>;
 					clocks = <&clks VF610_CLK_SNVS>;
 					clock-names = "snvs-rtc";
Signature · 1 line
1.9.1
#7 lore raw

PATCH v5 4/6 input: keyboard: imx: add snvs power key driver

In reply to Frank.Li at freescale.com

From: Robin Gong <[email protected]>

add snvs power key driver.
It work in imx chips after i.mx6sx

ON/OFF key used power on/off whole system.
This driver make it wakeup from suspend state when short press ON/OFF key.

Long time press will trig SNVS power off chip without software intervention.

---
 drivers/input/keyboard/Kconfig       |  11 ++
 drivers/input/keyboard/Makefile      |   1 +
 drivers/input/keyboard/snvs_pwrkey.c | 227 +++++++++++++++++++++++++++++++++++
 3 files changed, 239 insertions(+)
 create mode 100644 drivers/input/keyboard/snvs_pwrkey.c
drivers/input/keyboard/Kconfig +11 -0
index 106fbac..61b3fab 100644
@@ -400,6 +400,17 @@ config KEYBOARD_MPR121
 	  To compile this driver as a module, choose M here: the
 	  module will be called mpr121_touchkey.
 
+config KEYBOARD_SNVS_PWRKEY
+	tristate "IMX SNVS Power Key Driver"
+	depends on SOC_IMX6SX
+	depends on OF
+	help
+	  This is the snvs powerkey driver for the Freescale i.MX application
+	  processors that are newer than i.MX6 SX.
+
+	  To compile this driver as a module, choose M here; the
+	  module will be called snvs_pwrkey.
+
 config KEYBOARD_IMX
 	tristate "IMX keypad support"
 	depends on ARCH_MXC
drivers/input/keyboard/Makefile +1 -0
index df28d55..1d416dd 100644
@@ -51,6 +51,7 @@ obj-$(CONFIG_KEYBOARD_QT1070)           += qt1070.o
 obj-$(CONFIG_KEYBOARD_QT2160)		+= qt2160.o
 obj-$(CONFIG_KEYBOARD_SAMSUNG)		+= samsung-keypad.o
 obj-$(CONFIG_KEYBOARD_SH_KEYSC)		+= sh_keysc.o
+obj-$(CONFIG_KEYBOARD_SNVS_PWRKEY)	+= snvs_pwrkey.o
 obj-$(CONFIG_KEYBOARD_SPEAR)		+= spear-keyboard.o
 obj-$(CONFIG_KEYBOARD_STMPE)		+= stmpe-keypad.o
 obj-$(CONFIG_KEYBOARD_STOWAWAY)		+= stowaway.o
drivers/input/keyboard/snvs_pwrkey.c new file +227 -0

227 lines not on this page · raw message

Signature · 1 line
1.9.1
#8 lore raw

PATCH v5 4/6 input: keyboard: imx: add snvs power key driver

Quoted 9 lines · On Tue, May 26, 2015 at 11:26 AM, <[email protected]> wrote:

From: Robin Gong <[email protected]>

add snvs power key driver.
It work in imx chips after i.mx6sx

ON/OFF key used power on/off whole system.
This driver make it wakeup from suspend state when short press ON/OFF key.

Quoted 5 lines

Long time press will trig SNVS power off chip without software intervention.

Dmitry Torokhov:

I think you can merge this patch now because Guo shawn already Acked.

best regards
Frank Li

Quoted 277 lines
---
 drivers/input/keyboard/Kconfig       |  11 ++
 drivers/input/keyboard/Makefile      |   1 +
 drivers/input/keyboard/snvs_pwrkey.c | 227 +++++++++++++++++++++++++++++++++++
 3 files changed, 239 insertions(+)
 create mode 100644 drivers/input/keyboard/snvs_pwrkey.c
drivers/input/keyboard/Kconfig +11 -0
index 106fbac..61b3fab 100644
@@ -400,6 +400,17 @@ config KEYBOARD_MPR121
          To compile this driver as a module, choose M here: the
          module will be called mpr121_touchkey.
 
+config KEYBOARD_SNVS_PWRKEY
+       tristate "IMX SNVS Power Key Driver"
+       depends on SOC_IMX6SX
+       depends on OF
+       help
+         This is the snvs powerkey driver for the Freescale i.MX application
+         processors that are newer than i.MX6 SX.
+
+         To compile this driver as a module, choose M here; the
+         module will be called snvs_pwrkey.
+
 config KEYBOARD_IMX
        tristate "IMX keypad support"
        depends on ARCH_MXC
drivers/input/keyboard/Makefile +1 -0
index df28d55..1d416dd 100644
@@ -51,6 +51,7 @@ obj-$(CONFIG_KEYBOARD_QT1070)           += qt1070.o
 obj-$(CONFIG_KEYBOARD_QT2160)          += qt2160.o
 obj-$(CONFIG_KEYBOARD_SAMSUNG)         += samsung-keypad.o
 obj-$(CONFIG_KEYBOARD_SH_KEYSC)                += sh_keysc.o
+obj-$(CONFIG_KEYBOARD_SNVS_PWRKEY)     += snvs_pwrkey.o
 obj-$(CONFIG_KEYBOARD_SPEAR)           += spear-keyboard.o
 obj-$(CONFIG_KEYBOARD_STMPE)           += stmpe-keypad.o
 obj-$(CONFIG_KEYBOARD_STOWAWAY)                += stowaway.o
drivers/input/keyboard/snvs_pwrkey.c new file +227 -1
new file mode 100644
index 0000000..512a1fc
@@ -0,0 +1,227 @@
+/*
+ * Driver for the IMX SNVS ON/OFF Power Key
+ * Copyright (C) 2015 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#define SNVS_LPSR_REG  0x4C    /* LP Status Register */
+#define SNVS_LPCR_REG  0x38    /* LP Control Register */
+#define SNVS_HPSR_REG  0x14
+#define SNVS_HPSR_BTN  BIT(6)
+#define SNVS_LPSR_SPO  BIT(18)
+#define SNVS_LPCR_DEP_EN BIT(5)
+
+#define DEBOUNCE_TIME 30
+#define REPEAT_INTERVAL 60
+
+struct pwrkey_drv_data {
+       struct regmap *snvs;
+       int irq;
+       int keycode;
+       int keystate;  /* 1:pressed */
+       int wakeup;
+       struct timer_list check_timer;
+       struct input_dev *input;
+};
+
+static void imx_imx_snvs_check_for_events(unsigned long data)
+{
+       struct pwrkey_drv_data *pdata = (struct pwrkey_drv_data *) data;
+       struct input_dev *input = pdata->input;
+       u32 state;
+
+       regmap_read(pdata->snvs, SNVS_HPSR_REG, &state);
+       state = state & SNVS_HPSR_BTN ? 1 : 0;
+
+       /* only report new event if status changed */
+       if (state ^ pdata->keystate) {
+               pdata->keystate = state;
+               input_event(input, EV_KEY, pdata->keycode, state);
+               input_sync(input);
+               pm_relax(pdata->input->dev.parent);
+       }
+
+       /* repeat check if pressed long */
+       if (state) {
+               mod_timer(&pdata->check_timer,
+                         jiffies + msecs_to_jiffies(REPEAT_INTERVAL));
+       }
+}
+
+static irqreturn_t imx_snvs_pwrkey_interrupt(int irq, void *dev_id)
+{
+       struct platform_device *pdev = dev_id;
+       struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev);
+       u32 lp_status;
+
+       pm_wakeup_event(pdata->input->dev.parent, 0);
+
+       regmap_read(pdata->snvs, SNVS_LPSR_REG, &lp_status);
+       if (lp_status & SNVS_LPSR_SPO)
+               mod_timer(&pdata->check_timer, jiffies + msecs_to_jiffies(DEBOUNCE_TIME));
+
+       /* clear SPO status */
+       regmap_write(pdata->snvs, SNVS_LPSR_REG, SNVS_LPSR_SPO);
+
+       return IRQ_HANDLED;
+}
+
+static void imx_snvs_pwrkey_act(void *pdata)
+{
+       struct pwrkey_drv_data *pd = pdata;
+
+       del_timer_sync(&pd->check_timer);
+}
+
+static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
+{
+       struct pwrkey_drv_data *pdata = NULL;
+       struct input_dev *input = NULL;
+       struct device_node *np;
+       int error;
+
+       /* Get SNVS register Page */
+       np = pdev->dev.of_node;
+       if (!np)
+               return -ENODEV;
+
+       pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return -ENOMEM;
+
+       pdata->snvs = syscon_regmap_lookup_by_phandle(np, "regmap");;
+
+       if (!pdata->snvs) {
+               dev_err(&pdev->dev, "Can't get snvs syscon\n");
+               return -ENODEV;
+       }
+
+       if (of_property_read_u32(np, "linux,keycode", &pdata->keycode)) {
+               pdata->keycode = KEY_POWER;
+               dev_warn(&pdev->dev, "KEY_POWER without setting in dts\n");
+       }
+
+       pdata->wakeup = of_property_read_bool(np, "wakeup");
+
+       pdata->irq = platform_get_irq(pdev, 0);
+       if (pdata->irq < 0) {
+               dev_err(&pdev->dev, "no irq defined in platform data\n");
+               return -EINVAL;
+       }
+
+       regmap_update_bits(pdata->snvs, SNVS_LPCR_REG, SNVS_LPCR_DEP_EN, SNVS_LPCR_DEP_EN);
+
+       /* clear the unexpected interrupt before driver ready */
+       regmap_write(pdata->snvs, SNVS_LPSR_REG, SNVS_LPSR_SPO);
+
+       setup_timer(&pdata->check_timer,
+                   imx_imx_snvs_check_for_events, (unsigned long) pdata);
+
+       input = devm_input_allocate_device(&pdev->dev);
+       if (!input) {
+               dev_err(&pdev->dev, "failed to allocate the input device\n");
+               return -ENOMEM;
+       }
+
+       input->name = pdev->name;
+       input->phys = "snvs-pwrkey/input0";
+       input->id.bustype = BUS_HOST;
+
+       input_set_capability(input, EV_KEY, pdata->keycode);
+
+       /* input customer action to cancel release timer */
+       error = devm_add_action(&pdev->dev, imx_snvs_pwrkey_act, pdata);
+       if (error) {
+               dev_err(&pdev->dev, "failed to register remove action\n");
+               return error;
+       }
+
+       error = devm_request_irq(&pdev->dev, pdata->irq,
+                              imx_snvs_pwrkey_interrupt,
+                              0, pdev->name, pdev);
+
+       if (error) {
+               dev_err(&pdev->dev, "interrupt not available.\n");
+               return error;
+       }
+
+       error = input_register_device(input);
+       if (error < 0) {
+               dev_err(&pdev->dev, "failed to register input device\n");
+               input_free_device(input);
+               return error;
+       }
+
+       pdata->input = input;
+       platform_set_drvdata(pdev, pdata);
+
+       device_init_wakeup(&pdev->dev, pdata->wakeup);
+
+       return 0;
+}
+
+static int imx_snvs_pwrkey_suspend(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev);
+
+       if (device_may_wakeup(&pdev->dev))
+               enable_irq_wake(pdata->irq);
+
+       return 0;
+}
+
+static int imx_snvs_pwrkey_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev);
+
+       if (device_may_wakeup(&pdev->dev))
+               disable_irq_wake(pdata->irq);
+
+       return 0;
+}
+
+static const struct of_device_id imx_snvs_pwrkey_ids[] = {
+       { .compatible = "fsl,sec-v4.0-pwrkey" },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_snvs_pwrkey_ids);
+
+static SIMPLE_DEV_PM_OPS(imx_snvs_pwrkey_pm_ops, imx_snvs_pwrkey_suspend,
+                               imx_snvs_pwrkey_resume);
+
+static struct platform_driver imx_snvs_pwrkey_driver = {
+       .driver = {
+               .name = "snvs_pwrkey",
+               .pm     = &imx_snvs_pwrkey_pm_ops,
+               .of_match_table = imx_snvs_pwrkey_ids,
+       },
+       .probe = imx_snvs_pwrkey_probe,
+};
+module_platform_driver(imx_snvs_pwrkey_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor");
+MODULE_DESCRIPTION("i.MX snvs power key Driver");
+MODULE_LICENSE("GPL");
--

1.9.1

maintainer
#9 lore raw

PATCH v5 4/6 input: keyboard: imx: add snvs power key driver

In reply to Frank.Li at freescale.com
Quoted 9 lines · Frank.Li at freescale.com

From: Robin Gong <[email protected]>

add snvs power key driver.
It work in imx chips after i.mx6sx

ON/OFF key used power on/off whole system.
This driver make it wakeup from suspend state when short press ON/OFF key.

Quoted 5 lines

Long time press will trig SNVS power off chip without software intervention.

Quoted 277 lines
---
 drivers/input/keyboard/Kconfig       |  11 ++
 drivers/input/keyboard/Makefile      |   1 +
 drivers/input/keyboard/snvs_pwrkey.c | 227 +++++++++++++++++++++++++++++++++++
 3 files changed, 239 insertions(+)
 create mode 100644 drivers/input/keyboard/snvs_pwrkey.c
drivers/input/keyboard/Kconfig +11 -0
index 106fbac..61b3fab 100644
@@ -400,6 +400,17 @@ config KEYBOARD_MPR121
 	  To compile this driver as a module, choose M here: the
 	  module will be called mpr121_touchkey.
 
+config KEYBOARD_SNVS_PWRKEY
+	tristate "IMX SNVS Power Key Driver"
+	depends on SOC_IMX6SX
+	depends on OF
+	help
+	  This is the snvs powerkey driver for the Freescale i.MX application
+	  processors that are newer than i.MX6 SX.
+
+	  To compile this driver as a module, choose M here; the
+	  module will be called snvs_pwrkey.
+
 config KEYBOARD_IMX
 	tristate "IMX keypad support"
 	depends on ARCH_MXC
drivers/input/keyboard/Makefile +1 -0
index df28d55..1d416dd 100644
@@ -51,6 +51,7 @@ obj-$(CONFIG_KEYBOARD_QT1070)           += qt1070.o
 obj-$(CONFIG_KEYBOARD_QT2160)		+= qt2160.o
 obj-$(CONFIG_KEYBOARD_SAMSUNG)		+= samsung-keypad.o
 obj-$(CONFIG_KEYBOARD_SH_KEYSC)		+= sh_keysc.o
+obj-$(CONFIG_KEYBOARD_SNVS_PWRKEY)	+= snvs_pwrkey.o
 obj-$(CONFIG_KEYBOARD_SPEAR)		+= spear-keyboard.o
 obj-$(CONFIG_KEYBOARD_STMPE)		+= stmpe-keypad.o
 obj-$(CONFIG_KEYBOARD_STOWAWAY)		+= stowaway.o
drivers/input/keyboard/snvs_pwrkey.c new file +227 -0
new file mode 100644
index 0000000..512a1fc
@@ -0,0 +1,227 @@
+/*
+ * Driver for the IMX SNVS ON/OFF Power Key
+ * Copyright (C) 2015 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#define SNVS_LPSR_REG	0x4C	/* LP Status Register */
+#define SNVS_LPCR_REG	0x38	/* LP Control Register */
+#define SNVS_HPSR_REG	0x14
+#define SNVS_HPSR_BTN	BIT(6)
+#define SNVS_LPSR_SPO	BIT(18)
+#define SNVS_LPCR_DEP_EN BIT(5)
+
+#define DEBOUNCE_TIME 30
+#define REPEAT_INTERVAL 60
+
+struct pwrkey_drv_data {
+	struct regmap *snvs;
+	int irq;
+	int keycode;
+	int keystate;  /* 1:pressed */
+	int wakeup;
+	struct timer_list check_timer;
+	struct input_dev *input;
+};
+
+static void imx_imx_snvs_check_for_events(unsigned long data)
+{
+	struct pwrkey_drv_data *pdata = (struct pwrkey_drv_data *) data;
+	struct input_dev *input = pdata->input;
+	u32 state;
+
+	regmap_read(pdata->snvs, SNVS_HPSR_REG, &state);
+	state = state & SNVS_HPSR_BTN ? 1 : 0;
+
+	/* only report new event if status changed */
+	if (state ^ pdata->keystate) {
+		pdata->keystate = state;
+		input_event(input, EV_KEY, pdata->keycode, state);
+		input_sync(input);
+		pm_relax(pdata->input->dev.parent);
+	}
+
+	/* repeat check if pressed long */
+	if (state) {
+		mod_timer(&pdata->check_timer,
+			  jiffies + msecs_to_jiffies(REPEAT_INTERVAL));
+	}
+}
+
+static irqreturn_t imx_snvs_pwrkey_interrupt(int irq, void *dev_id)
+{
+	struct platform_device *pdev = dev_id;
+	struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev);
+	u32 lp_status;
+
+	pm_wakeup_event(pdata->input->dev.parent, 0);
+
+	regmap_read(pdata->snvs, SNVS_LPSR_REG, &lp_status);
+	if (lp_status & SNVS_LPSR_SPO)
+		mod_timer(&pdata->check_timer, jiffies + msecs_to_jiffies(DEBOUNCE_TIME));
+
+	/* clear SPO status */
+	regmap_write(pdata->snvs, SNVS_LPSR_REG, SNVS_LPSR_SPO);
+
+	return IRQ_HANDLED;
+}
+
+static void imx_snvs_pwrkey_act(void *pdata)
+{
+	struct pwrkey_drv_data *pd = pdata;
+
+	del_timer_sync(&pd->check_timer);
+}
+
+static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
+{
+	struct pwrkey_drv_data *pdata = NULL;
+	struct input_dev *input = NULL;
+	struct device_node *np;
+	int error;
+
+	/* Get SNVS register Page */
+	np = pdev->dev.of_node;
+	if (!np)
+		return -ENODEV;
+
+	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return -ENOMEM;
+
+	pdata->snvs = syscon_regmap_lookup_by_phandle(np, "regmap");;
+
+	if (!pdata->snvs) {
+		dev_err(&pdev->dev, "Can't get snvs syscon\n");
+		return -ENODEV;
+	}
+
+	if (of_property_read_u32(np, "linux,keycode", &pdata->keycode)) {
+		pdata->keycode = KEY_POWER;
+		dev_warn(&pdev->dev, "KEY_POWER without setting in dts\n");
+	}
+
+	pdata->wakeup = of_property_read_bool(np, "wakeup");
+
+	pdata->irq = platform_get_irq(pdev, 0);
+	if (pdata->irq < 0) {
+		dev_err(&pdev->dev, "no irq defined in platform data\n");
+		return -EINVAL;
+	}
+
+	regmap_update_bits(pdata->snvs, SNVS_LPCR_REG, SNVS_LPCR_DEP_EN, SNVS_LPCR_DEP_EN);
+
+	/* clear the unexpected interrupt before driver ready */
+	regmap_write(pdata->snvs, SNVS_LPSR_REG, SNVS_LPSR_SPO);
+
+	setup_timer(&pdata->check_timer,
+		    imx_imx_snvs_check_for_events, (unsigned long) pdata);
+
+	input = devm_input_allocate_device(&pdev->dev);
+	if (!input) {
+		dev_err(&pdev->dev, "failed to allocate the input device\n");
+		return -ENOMEM;
+	}
+
+	input->name = pdev->name;
+	input->phys = "snvs-pwrkey/input0";
+	input->id.bustype = BUS_HOST;
+
+	input_set_capability(input, EV_KEY, pdata->keycode);
+
+	/* input customer action to cancel release timer */
+	error = devm_add_action(&pdev->dev, imx_snvs_pwrkey_act, pdata);
+	if (error) {
+		dev_err(&pdev->dev, "failed to register remove action\n");
+		return error;
+	}
+
+	error = devm_request_irq(&pdev->dev, pdata->irq,
+			       imx_snvs_pwrkey_interrupt,
+			       0, pdev->name, pdev);
+
+	if (error) {
+		dev_err(&pdev->dev, "interrupt not available.\n");
+		return error;
+	}
+
+	error = input_register_device(input);
+	if (error < 0) {
+		dev_err(&pdev->dev, "failed to register input device\n");
+		input_free_device(input);
+		return error;
+	}
+
+	pdata->input = input;
+	platform_set_drvdata(pdev, pdata);
+
+	device_init_wakeup(&pdev->dev, pdata->wakeup);
+
+	return 0;
+}
+
+static int imx_snvs_pwrkey_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev);
+
+	if (device_may_wakeup(&pdev->dev))
+		enable_irq_wake(pdata->irq);
+
+	return 0;
+}
+
+static int imx_snvs_pwrkey_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev);
+
+	if (device_may_wakeup(&pdev->dev))
+		disable_irq_wake(pdata->irq);
+
+	return 0;
+}
+
+static const struct of_device_id imx_snvs_pwrkey_ids[] = {
+	{ .compatible = "fsl,sec-v4.0-pwrkey" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_snvs_pwrkey_ids);
+
+static SIMPLE_DEV_PM_OPS(imx_snvs_pwrkey_pm_ops, imx_snvs_pwrkey_suspend,
+				imx_snvs_pwrkey_resume);
+
+static struct platform_driver imx_snvs_pwrkey_driver = {
+	.driver = {
+		.name = "snvs_pwrkey",
+		.pm     = &imx_snvs_pwrkey_pm_ops,
+		.of_match_table = imx_snvs_pwrkey_ids,
+	},
+	.probe = imx_snvs_pwrkey_probe,
+};
+module_platform_driver(imx_snvs_pwrkey_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor");
+MODULE_DESCRIPTION("i.MX snvs power key Driver");
+MODULE_LICENSE("GPL");
Signature · 1 line
1.9.1
Signature · 1 line
Dmitry
#10 lore raw

PATCH v5 5/6 Document: devicetree: input: imx: i.mx snvs power device tree bindings

In reply to Frank.Li at freescale.com

From: Frank Li <[email protected]>

The snvs-pwrkey is designed to enable POWER key function which controlled by SNVS ONOFF. the driver can report the status of POWER key and wakeup system if pressed after system suspend.

---
 .../devicetree/bindings/crypto/fsl-sec4.txt        | 49 ++++++++++++++++++++++
 .../devicetree/bindings/input/snvs-pwrkey.txt      |  1 +
 2 files changed, 50 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/snvs-pwrkey.txt
Documentation/devicetree/bindings/crypto/fsl-sec4.txt +49 -0
index b21e7c2..71a39c5 100644
@@ -387,6 +387,47 @@ EXAMPLE
 	};
 
 =====================================================================
+System ON/OFF key driver
+
+  The snvs-pwrkey is designed to enable POWER key function which controlled
+  by SNVS ONOFF, the driver can report the status of POWER key and wakeup
+  system if pressed after system suspend.
+
+  - compatible:
+      Usage: required
+      Value type: <string>
+      Definition: Mush include "fsl,sec-v4.0-pwrkey".
+
+  - interrupts:
+      Usage: required
+      Value type: <prop_encoded-array>
+      Definition: The SNVS ON/OFF interrupt number to the CPU(s).
+
+  - linux,keycode:
+      Usage: option
+      Value type: <int>
+      Definition: Keycode to emit, KEY_POWER by default.
+
+  - wakeup:
+      Usage: option
+      Value type: <boo>
+      Definition: Button can wake-up the system.
+
+ - regmap:
+      Usage: required:
+      Value type: <phandle>
+      Definition: this is phandle to the register map node.
+
+EXAMPLE:
+	snvs-pwrkey at 0x020cc000 {
+		compatible = "fsl,sec-v4.0-pwrkey";
+		regmap = <&snvs>;
+		interrupts = <0 4 0x4>
+	        linux,keycode = <116>; /* KEY_POWER */
+		wakeup;
+	};
+
+=====================================================================
 FULL EXAMPLE
 
 	crypto: crypto at 300000 {
@@ -466,6 +507,14 @@ FULL EXAMPLE
 			offset = <0x34>;
 			interrupts = <93 2>;
 		};
+
+		snvs-pwrkey at 0x020cc000 {
+			compatible = "fsl,sec-v4.0-pwrkey";
+			regmap = <&sec_mon>;
+			interrupts = <0 4 0x4>;
+			linux,keycode = <116>; /* KEY_POWER */
+			wakeup;
+		};
 	};
 
 =====================================================================
Documentation/devicetree/bindings/input/snvs-pwrkey.txt new file +1 -0
new file mode 100644
index 0000000..70c1425
@@ -0,0 +1 @@
+See Documentation/devicetree/bindings/crypto/fsl-sec4.txt
Signature · 1 line
1.9.1
#11 lore raw

PATCH v5 6/6 ARM: dts: imx6sx: enable snvs power key

In reply to Frank.Li at freescale.com

From: Frank Li <[email protected]>

enable snvs ONOFF power key support

---
 arch/arm/boot/dts/imx6sx.dtsi | 9 +++++++++
 1 file changed, 9 insertions(+)
arch/arm/boot/dts/imx6sx.dtsi +9 -0
index f8f0819..e391d63 100644
@@ -8,6 +8,7 @@
 
 #include <dt-bindings/clock/imx6sx-clock.h>
 #include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 #include "imx6sx-pinfunc.h"
 #include "skeleton.dtsi"
@@ -679,6 +680,14 @@
 					mask = <0x60>;
 					status = "disabled";
 				};
+
+				snvs_pwrkey: snvs-powerkey {
+					compatible = "fsl,sec-v4.0-pwrkey";
+					regmap = <&snvs>;
+					interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
+					linux,keycode = <KEY_POWER>;
+					wakeup;
+				};
 			};
 
 			epit1: epit at 020d0000 {
Signature · 1 line
1.9.1
#12 lore raw

v5 cover Change snvs rtc and poweroff to use syscon add pwrkey driver

In reply to Frank.Li at freescale.com
Quoted 9 lines · Frank.Li at freescale.com

Frank Li (5):
rtc: arm: imx: snvs: change use syscon to access register
Document: dt: fsl: snvs: change support syscon
ARM: dts: imx: update snvs to use syscon access register
Document: devicetree: input: imx: i.mx snvs power device tree bindings
ARM: dts: imx6sx: enable snvs power key

Robin Gong (1):
input: keyboard: imx: add snvs power key driver

The series looks fine to me now. But I can only apply those two dts patches after the driver and binding parts get landed on mainline. Hopefully, you can get them in during the coming merge window through subsystem trees, so that I can pick up dts part after v4.2-rc1 comes out.

Shawn

#13 lore raw

v5 cover Change snvs rtc and poweroff to use syscon add pwrkey driver

Quoted 8 lines · Shawn Guo
Quoted 7 lines · Frank.Li at freescale.com

Frank Li (5):
rtc: arm: imx: snvs: change use syscon to access register
Document: dt: fsl: snvs: change support syscon
ARM: dts: imx: update snvs to use syscon access register
Document: devicetree: input: imx: i.mx snvs power device tree bindings
ARM: dts: imx6sx: enable snvs power key

Quoted 9 lines
Quoted 2 lines

Robin Gong (1):
input: keyboard: imx: add snvs power key driver

The series looks fine to me now. But I can only apply those two dts patches after the driver and binding parts get landed on mainline. Hopefully, you can get them in during the coming merge window through subsystem trees, so that I can pick up dts part after v4.2-rc1 comes out.

Actually, I was thinking you could take the whole series in your tree (at least, I'm speaking for the RTC part).
Else, I'm wondering who should take the documentation changes.

Signature · 3 lines
Alexandre Belloni, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
#14 lore raw

v5 cover Change snvs rtc and poweroff to use syscon add pwrkey driver

Quoted 19 lines · Alexandre Belloni
Quoted 8 lines · Shawn Guo
Quoted 7 lines · Frank.Li at freescale.com

Frank Li (5):
rtc: arm: imx: snvs: change use syscon to access register
Document: dt: fsl: snvs: change support syscon
ARM: dts: imx: update snvs to use syscon access register
Document: devicetree: input: imx: i.mx snvs power device tree bindings
ARM: dts: imx6sx: enable snvs power key

Quoted 9 lines
Quoted 2 lines

Robin Gong (1):
input: keyboard: imx: add snvs power key driver

The series looks fine to me now. But I can only apply those two dts patches after the driver and binding parts get landed on mainline. Hopefully, you can get them in during the coming merge window through subsystem trees, so that I can pick up dts part after v4.2-rc1 comes out.

Quoted 4 lines

Actually, I was thinking you could take the whole series in your tree (at least, I'm speaking for the RTC part).
Else, I'm wondering who should take the documentation changes.

Dmitry Torokhov:

what's your opinion about input part?

best regards
Frank Li

Quoted 5 lines

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

maintainer
#15 lore raw

v5 cover Change snvs rtc and poweroff to use syscon add pwrkey driver

Quoted 21 lines · Zhi Li
Quoted 19 lines · Alexandre Belloni
Quoted 17 lines · Shawn Guo
Quoted 9 lines · Frank.Li at freescale.com

Frank Li (5):
rtc: arm: imx: snvs: change use syscon to access register
Document: dt: fsl: snvs: change support syscon
ARM: dts: imx: update snvs to use syscon access register
Document: devicetree: input: imx: i.mx snvs power device tree bindings
ARM: dts: imx6sx: enable snvs power key

Robin Gong (1):
input: keyboard: imx: add snvs power key driver

The series looks fine to me now. But I can only apply those two dts patches after the driver and binding parts get landed on mainline. Hopefully, you can get them in during the coming merge window through subsystem trees, so that I can pick up dts part after v4.2-rc1 comes out.

Quoted 8 lines
Quoted 4 lines

Actually, I was thinking you could take the whole series in your tree (at least, I'm speaking for the RTC part).
Else, I'm wondering who should take the documentation changes.

Dmitry Torokhov:

what's your opinion about input part?

I am fine with it merged through Shawn's tree. I added my ack to the mail in question.

Thanks.

Signature · 1 line
Dmitry
#16 lore raw

v5 cover Change snvs rtc and poweroff to use syscon add pwrkey driver

Quoted 27 lines · Dmitry Torokhov
Quoted 26 lines · Zhi Li
Quoted 23 lines · Alexandre Belloni
Quoted 8 lines · Shawn Guo
Quoted 7 lines · Frank.Li at freescale.com

Frank Li (5):
rtc: arm: imx: snvs: change use syscon to access register
Document: dt: fsl: snvs: change support syscon
ARM: dts: imx: update snvs to use syscon access register
Document: devicetree: input: imx: i.mx snvs power device tree bindings
ARM: dts: imx6sx: enable snvs power key

Quoted 9 lines
Quoted 2 lines

Robin Gong (1):
input: keyboard: imx: add snvs power key driver

The series looks fine to me now. But I can only apply those two dts patches after the driver and binding parts get landed on mainline. Hopefully, you can get them in during the coming merge window through subsystem trees, so that I can pick up dts part after v4.2-rc1 comes out.

Actually, I was thinking you could take the whole series in your tree (at least, I'm speaking for the RTC part).
Else, I'm wondering who should take the documentation changes.

Quoted 6 lines
Quoted 3 lines

Dmitry Torokhov:

what's your opinion about input part?

I am fine with it merged through Shawn's tree. I added my ack to the mail in question.

Shawn:

Everyone agree merge through your tree.
Please help pick whole patches

best regards
Frank Li

Quoted 5 lines

Thanks.

--
Dmitry

Applied
#17 lore raw

v5 cover Change snvs rtc and poweroff to use syscon add pwrkey driver

Quoted 4 lines · Zhi Li

Shawn:

Everyone agree merge through your tree.
Please help pick whole patches

Applied, thanks.