Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 02/12] dt-bindings: document s3c24xx controller for external clock output
From: Tomasz Figa @ 2014-02-09  1:54 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <201312131359.00450.heiko@sntech.de>

Hi Heiko,

On 13.12.2013 13:59, Heiko St?bner wrote:
> The clock settings are distributed over a regular register and parts
> of the misccr register.
>
> Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> ---
>   .../bindings/clock/samsung,s3c2410-dclk.txt        |   53 ++++++++++++++++++++
>   1 file changed, 53 insertions(+)
>   create mode 100644 Documentation/devicetree/bindings/clock/samsung,s3c2410-dclk.txt
>
> diff --git a/Documentation/devicetree/bindings/clock/samsung,s3c2410-dclk.txt b/Documentation/devicetree/bindings/clock/samsung,s3c2410-dclk.txt
> new file mode 100644
> index 0000000..0a1f7b1
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/samsung,s3c2410-dclk.txt
> @@ -0,0 +1,53 @@
> +* Samsung S3C24XX External Clock Output Controller
> +
> +The S3C24XX series can generate clock signals on two clock output pads.
> +The clock binding described here is applicable to all SoCs in
> +the s3c24x family.
> +
> +Required Properties:
> +
> +- compatible: should be one of the following.
> +  - "samsung,s3c2410-dclk" - controller in S3C2410 SoCs.
> +  - "samsung,s3c2412-dclk" - controller in S3C2412 SoCs.
> +  - "samsung,s3c2440-dclk" - controller in S3C2440 and S3C2442 SoCs.
> +  - "samsung,s3c2443-dclk" - controller in S3C2443 and later SoCs.
> +- reg: physical base address of the controller and length of memory mapped
> +  region.
> +- #clock-cells: should be 1.
> +- samsung,misccr: phandle to the syscon managing the misccr register, which
> +  holds configuration settings for different soc-components (clocks, usb, ...).

Hmm, looking at the datasheet, DCLK and CLKOUT registers seem to be part 
of the pin controller. I wonder if there is really a need for different 
driver and device node to handle them.

Could this be simply made a part of the s3c24xx pinctrl driver, 
extending it to register also a clock provider under the same DT node?

Best regards,
Tomasz

^ permalink raw reply

* [PATCH 1/3] crypto: Fix the pointer voodoo in unaligned ahash
From: Herbert Xu @ 2014-02-09  1:18 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1755989.ess5rJDo4G@tlendack-t1>

On Tue, Jan 14, 2014 at 01:35:50PM -0600, Tom Lendacky wrote:
> 
> On Tuesday, January 14, 2014 06:33:47 PM Marek Vasut wrote:
> > Add documentation for the pointer voodoo that is happening in crypto/ahash.c
> > in ahash_op_unaligned(). This code is quite confusing, so add a beefy chunk
> > of documentation.
> > 
> > Moreover, make sure the mangled request is completely restored after finishing
> > this unaligned operation. This means restoring all of .result, .priv, .base.data
> > and .base.complete .
> > 
> > Also, remove the crypto_completion_t complete = ... line present in the
> > ahash_op_unaligned_done() function. This type actually declares a function
> > pointer, which is very confusing.
> > 
> > Finally, yet very important nonetheless, make sure the req->priv is free()'d
> > only after the original request is restored in ahash_op_unaligned_done().
> > The req->priv data must not be free()'d before that in ahash_op_unaligned_finish(),
> > since we would be accessing previously free()'d data in ahash_op_unaligned_done()
> > and cause corruption.
> > 
> > Signed-off-by: Marek Vasut <marex@denx.de>
> > Cc: David S. Miller <davem@davemloft.net>
> > Cc: Fabio Estevam <fabio.estevam@freescale.com>
> > Cc: Herbert Xu <herbert@gondor.apana.org.au>
> > Cc: Shawn Guo <shawn.guo@linaro.org>
> > Cc: Tom Lendacky <thomas.lendacky@amd.com>
> > ---
> >  crypto/ahash.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++----------
> >  1 file changed, 54 insertions(+), 11 deletions(-)
> > 
> > diff --git a/crypto/ahash.c b/crypto/ahash.c
> > index a92dc38..5ca8ede 100644
> > --- a/crypto/ahash.c
> > +++ b/crypto/ahash.c
> > @@ -29,6 +29,7 @@
> >  struct ahash_request_priv {
> >  	crypto_completion_t complete;
> >  	void *data;
> > +	void *priv;
> >  	u8 *result;
> >  	void *ubuf[] CRYPTO_MINALIGN_ATTR;
> >  };
> > @@ -200,23 +201,38 @@ static void ahash_op_unaligned_finish(struct ahash_request *req, int err)
> >  	if (!err)
> >  		memcpy(priv->result, req->result,
> >  		       crypto_ahash_digestsize(crypto_ahash_reqtfm(req)));
> > -
> > -	kzfree(priv);
> 
> You can't move/remove this kzfree since a synchronous operation will not take
> the ahash_op_unaligned_done path.  A synchronous operation will never return
> -EINPROGRESS and the effect will be to never free the priv structure.

Marek, did you have a chance to address this?

Thanks,
-- 
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply

* [PATCH 01/12] ARM: S3C24XX: cpufreq-utils: don't write raw values to MPLLCON when using ccf
From: Tomasz Figa @ 2014-02-08 20:23 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <201312131357.19899.heiko@sntech.de>

Hi Heiko,

On 13.12.2013 13:57, Heiko St?bner wrote:
> The s3c24xx cpufreq driver needs to change the mpll speed and was doing
> this by writing raw values from a translation table into the MPLLCON
> register.
>
> Change this to use a regular clk_set_rate call when using the common
> clock framework and only write the raw value in the samsung_clock case.
>
> Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> ---
>   arch/arm/mach-s3c24xx/cpufreq-utils.c |   13 +++++++++++++
>   1 file changed, 13 insertions(+)
>
> diff --git a/arch/arm/mach-s3c24xx/cpufreq-utils.c b/arch/arm/mach-s3c24xx/cpufreq-utils.c
> index 2a0aa56..680a031 100644
> --- a/arch/arm/mach-s3c24xx/cpufreq-utils.c
> +++ b/arch/arm/mach-s3c24xx/cpufreq-utils.c
> @@ -14,6 +14,7 @@
>   #include <linux/errno.h>
>   #include <linux/cpufreq.h>
>   #include <linux/io.h>
> +#include <linux/clk.h>
>
>   #include <mach/map.h>
>   #include <mach/regs-clock.h>
> @@ -60,5 +61,17 @@ void s3c2410_cpufreq_setrefresh(struct s3c_cpufreq_config *cfg)
>    */
>   void s3c2410_set_fvco(struct s3c_cpufreq_config *cfg)
>   {
> +#ifdef CONFIG_SAMSUNG_CLOCK
>   	__raw_writel(cfg->pll.driver_data, S3C2410_MPLLCON);
> +#endif
> +
> +#ifdef CONFIG_COMMON_CLK
> +	struct clk *mpll = clk_get(NULL, "mpll");
> +	if (IS_ERR(mpll))
> +		return;

Wouldn't it be more sensible to get the clock only once, at the time 
cpufreq is initialized? This would avoid going through the list of all 
clocks every CPU frequency change and be more semantically correct.

If there is no good place to put this clk_get() then maybe it could be 
simply called on first call to s3c2410_set_fvco() and the clock saved to 
a static variable.

Best regards,
Tomasz

^ permalink raw reply

* [PATCH] mmc: mmci: rename some extended flags
From: Linus Walleij @ 2014-02-08 20:13 UTC (permalink / raw)
  To: linux-arm-kernel

These four (so far unused) flags are only found in the ST Micro
versions of MMCI, so infix them properly with the _ST_ infix.

Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
 drivers/mmc/host/mmci.h | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 168bc72f7a94..84c0e59b792a 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -38,10 +38,11 @@
 #define MCI_CPSM_INTERRUPT	(1 << 8)
 #define MCI_CPSM_PENDING	(1 << 9)
 #define MCI_CPSM_ENABLE		(1 << 10)
-#define MCI_SDIO_SUSP		(1 << 11)
-#define MCI_ENCMD_COMPL		(1 << 12)
-#define MCI_NIEN		(1 << 13)
-#define MCI_CE_ATACMD		(1 << 14)
+/* Argument flag extenstions in the ST Micro versions */
+#define MCI_ST_SDIO_SUSP	(1 << 11)
+#define MCI_ST_ENCMD_COMPL	(1 << 12)
+#define MCI_ST_NIEN		(1 << 13)
+#define MCI_ST_CE_ATACMD	(1 << 14)
 
 #define MMCIRESPCMD		0x010
 #define MMCIRESPONSE0		0x014
-- 
1.8.5.3

^ permalink raw reply related

* [GIT PULL] clk: mvebu: fixes for v3.14
From: Jason Cooper @ 2014-02-08 17:31 UTC (permalink / raw)
  To: linux-arm-kernel

Mike,

Here's a round of fixes I've had in -next for several days.  It's not
the ideal solution, but a short term one while we hash out a proper
solution for v3.15+.

thx,

Jason.

The following changes since commit 38dbfb59d1175ef458d006556061adeaa8751b72:

  Linus 3.14-rc1 (2014-02-02 16:42:13 -0800)

are available in the git repository at:

  git://git.infradead.org/linux-mvebu.git tags/mvebu-clk-fixes-3.14

for you to fetch changes up to 58d516ae95cbf7eed44f85a01cdfad41f17b4197:

  clk: mvebu: kirkwood: maintain clock init order (2014-02-06 18:07:01 +0000)

----------------------------------------------------------------
mvebu clock fixes for v3.14

 - kirkwood, dove, armada-xp, armada-370
    - force clock init order broken by sorting DT ocp nodes by address
    - fixes boot failures on affected platforms

----------------------------------------------------------------
Sebastian Hesselbarth (4):
      clk: mvebu: armada-370: maintain clock init order
      clk: mvebu: armada-xp: maintain clock init order
      clk: mvebu: dove: maintain clock init order
      clk: mvebu: kirkwood: maintain clock init order

 drivers/clk/mvebu/armada-370.c | 21 ++++++++++-----------
 drivers/clk/mvebu/armada-xp.c  | 20 +++++++++-----------
 drivers/clk/mvebu/dove.c       | 19 +++++++++----------
 drivers/clk/mvebu/kirkwood.c   | 34 ++++++++++++++++------------------
 4 files changed, 44 insertions(+), 50 deletions(-)

^ permalink raw reply

* [GIT PULL] mvebu: phy and ata fixes for v3.14
From: Jason Cooper @ 2014-02-08 17:22 UTC (permalink / raw)
  To: linux-arm-kernel

All,

Here is a small set of fixes for fix a boot hang we are currently
experiencing in the boot farm.  Now that we are probing for the phy, we
didn't take into account optional phys, which causes the hang.  These
patches fix this and restore booting on the affected platforms.

thx,

Jason.


The following changes since commit 38dbfb59d1175ef458d006556061adeaa8751b72:

  Linus 3.14-rc1 (2014-02-02 16:42:13 -0800)

are available in the git repository at:

  git://git.infradead.org/linux-mvebu.git tags/mvebu-phy_ata-fixes-3.14

for you to fetch changes up to 90aa2997029fa623fe9e3ec3a469a00a34130237:

  ata: sata_mv: Fix probe failures with optional phys (2014-02-05 05:48:58 +0000)

----------------------------------------------------------------
mvebu phy and ata fixes for v3.14

 - phy
    - add support for optional phys via NULL

 - ata
    - fix boot hang due to probe failure of optional phys

NOTE:  Series has been Ack'd by both the phy maintainer and the ata maintainer
for going through arm-soc

----------------------------------------------------------------
Andrew Lunn (3):
      drivers: phy: Make NULL a valid phy reference
      drivers: phy: Add support for optional phys
      ata: sata_mv: Fix probe failures with optional phys

 Documentation/phy.txt   | 26 +++++++++++++++------
 drivers/ata/sata_mv.c   |  8 ++++---
 drivers/phy/phy-core.c  | 62 ++++++++++++++++++++++++++++++++++++++++++++++++-
 include/linux/phy/phy.h | 14 +++++++++++
 4 files changed, 99 insertions(+), 11 deletions(-)

^ permalink raw reply

* [PATCH] gpio: mvebu: use chained_irq_{enter, exit} for GIC compatibility
From: Jason Cooper @ 2014-02-08 17:06 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391772559-28357-1-git-send-email-thomas.petazzoni@free-electrons.com>

On Fri, Feb 07, 2014 at 12:29:19PM +0100, Thomas Petazzoni wrote:
> On currently supported SoCs, the GPIO block used on Marvell EBU SoCs
> is always connected to the Marvell MPIC. However, we are going to
> introduce the support for newer Marvell EBU SoCs that use the
> Cortex-A9 core, and therefore use the GIC as their main interrupt
> controller, to which the GPIO block controlled by the gpio-mvebu
> driver is connected.
> 
> The GIC interrupt controller driver uses the fasteoi flow handler. In
> order to ensure that the eoi hook of the GIC driver gets called, the
> GPIO driver should call chained_irq_enter() and chained_irq_exit() in
> its handler. Without this, the first GPIO interrupt locks up the
> system because it doesn't get acked at the GIC level.
> 
> This change is similar to for example commit
> 0d978eb7349941139241a99acf05de6dd49b78d1 ("gpio: davinci: use
> chained_irq_enter/chained_irq_exit API").
> 
> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> ---
>  drivers/gpio/gpio-mvebu.c | 7 +++++++
>  1 file changed, 7 insertions(+)

Acked-by: Jason Cooper <jason@lakedaemon.net>

thx,

Jason.

^ permalink raw reply

* [PATCH] i2c: mv64xxx: Fix locked bus when offload is selected but not used on a message
From: Jason Cooper @ 2014-02-08 17:01 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391770588-1344-1-git-send-email-gregory.clement@free-electrons.com>

On Fri, Feb 07, 2014 at 11:55:54AM +0100, Gregory CLEMENT wrote:
> Offload can be used only on regular transactions and for 1 to byte
> transfers. In the other cases we switch back to usual work flow.
> 
> In this case we need to call mv64xxx_i2c_prepare_for_io() as this
> function is not used when we try to use offloading.
> 
> This commit adds this missing call when offloading have failed in the
> MV64XXX_I2C_ACTION_OFFLOAD_SEND_START case.
> 
> This fix the timeout seen when the the i2c driver try to access an
> address where the device is absent on the Armada XP bases board.
> 
> Cc: stable at vger.kernel.org # v3.12+
> Fixes: 930ab3d403ae (i2c: mv64xxx: Add I2C Transaction Generator support)
> 
> Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
> ---
>  drivers/i2c/busses/i2c-mv64xxx.c | 9 ++++++++-
>  1 file changed, 8 insertions(+), 1 deletion(-)

Acked-by: Jason Cooper <jason@lakedaemon.net>

thx,

Jason.

^ permalink raw reply

* [PATCH v3 1/7] cpufreq: cpufreq-cpu0: allow use of optional boost mode frequencies
From: Nishanth Menon @ 2014-02-08 16:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAJuA9ah9xHZZknW5yfh8v8GS65TLd4uZedigEt4ATdoiy6-1Gg@mail.gmail.com>

On Fri, Feb 7, 2014 at 11:19 PM, Thomas Abraham <ta.omasab@gmail.com> wrote:
>>> --- a/drivers/cpufreq/cpufreq-cpu0.c
>>> +++ b/drivers/cpufreq/cpufreq-cpu0.c
>>> @@ -195,6 +195,9 @@ static int cpu0_cpufreq_probe(struct platform_device *pdev)
>>>                       transition_latency += ret * 1000;
>>>       }
>>>
>>> +     if (of_find_property(cpu_dev->of_node, "boost-frequency", NULL))
>>> +             cpu0_cpufreq_driver.boost_supported = true;
>>> +
>> might as well hide that under the opp api which returns a bool that
>> says if boost is supported or not. that allows the parse to be
>> isolated to a single location.
>
> This is specific to the cpufreq-cpu0 driver. Other cpufreq drivers
> might want to use other methods and additional constraints to
> determine if boost has to be enabled.

struct cpufreq_driver::boost_supported is nothing CPU0 specifc - i am
just saying that the same property "boost-frequency" parsed by in two
places (dev_pm_opp_init_cpufreq_table and cpu0_cpufreq_probe) is a
very bad idea.

you could instead make
int dev_pm_opp_init_cpufreq_table(struct device *dev,
                           struct cpufreq_frequency_table **table)
into:
int dev_pm_opp_init_cpufreq_table(struct device *dev,
                           struct cpufreq_frequency_table **table,
bool *boost_supported);

Then use boost_supported for anything of your driver's choice
(including updating cpufreq_driver::boost_supported).

That allows the property to be isolated into a single function, who
can be modified without impacting other drivers at a later point in
time.

Regards,
Nishanth Menon

^ permalink raw reply

* [PATCH 2/2] input: misc: dts: Add axp20x-pek node to axp20x.dtsi
From: Carlo Caione @ 2014-02-08 16:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391875554-6383-1-git-send-email-carlo@caione.org>

This patch adds a sub-node to axp20x.dtsi

Signed-off-by: Carlo Caione <carlo@caione.org>
---
 arch/arm/boot/dts/axp20x.dtsi | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/arch/arm/boot/dts/axp20x.dtsi b/arch/arm/boot/dts/axp20x.dtsi
index 98d4c7c..3d475e2 100644
--- a/arch/arm/boot/dts/axp20x.dtsi
+++ b/arch/arm/boot/dts/axp20x.dtsi
@@ -6,4 +6,10 @@
 	compatible = "x-powers,axp20x";
 	interrupt-controller;
 	#interrupt-cells = <1>;
+
+	axp20x-pek {
+		compatible = "x-powers,axp20x-pek";
+		interrupts = <33>,  <34>;
+		interrupt-names = "PEK_DBR", "PEK_DBF";
+	};
 };
-- 
1.8.5.3

^ permalink raw reply related

* [PATCH 1/2] input: misc: Add driver for axp20x power enable key
From: Carlo Caione @ 2014-02-08 16:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391875554-6383-1-git-send-email-carlo@caione.org>

This patch add support for the Power Enable Key found on MFD axp202 and
axp209. It is to be considered as sub-node of axp20x MFD driver.

Signed-off-by: Carlo Caione <carlo@caione.org>
---
 drivers/input/misc/Kconfig      |  11 ++
 drivers/input/misc/Makefile     |   1 +
 drivers/input/misc/axp20x-pek.c | 278 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 290 insertions(+)
 create mode 100644 drivers/input/misc/axp20x-pek.c

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 5f4967d..c9438ac 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -384,6 +384,17 @@ config INPUT_RETU_PWRBUTTON
 	  To compile this driver as a module, choose M here. The module will
 	  be called retu-pwrbutton.
 
+config INPUT_AXP20X_PEK
+	tristate "X-Powers AXP20X power button driver"
+	depends on MFD_AXP20X
+	help
+	  Say Y here if you want to enable power key reporting via the
+	  AXP20X PMIC.
+
+	  To compile this driver as a module, choose M here. The module will
+	  be called axp20x-pek.
+
+
 config INPUT_TWL4030_PWRBUTTON
 	tristate "TWL4030 Power button Driver"
 	depends on TWL4030_CORE
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 0ebfb6d..41d5403 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -49,6 +49,7 @@ obj-$(CONFIG_INPUT_POWERMATE)		+= powermate.o
 obj-$(CONFIG_INPUT_PWM_BEEPER)		+= pwm-beeper.o
 obj-$(CONFIG_INPUT_RB532_BUTTON)	+= rb532_button.o
 obj-$(CONFIG_INPUT_RETU_PWRBUTTON)	+= retu-pwrbutton.o
+obj-$(CONFIG_INPUT_AXP20X_PEK)		+= axp20x-pek.o
 obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER)	+= rotary_encoder.o
 obj-$(CONFIG_INPUT_SGI_BTNS)		+= sgi_btns.o
 obj-$(CONFIG_INPUT_SIRFSOC_ONKEY)	+= sirfsoc-onkey.o
diff --git a/drivers/input/misc/axp20x-pek.c b/drivers/input/misc/axp20x-pek.c
new file mode 100644
index 0000000..91c15c4
--- /dev/null
+++ b/drivers/input/misc/axp20x-pek.c
@@ -0,0 +1,278 @@
+/*
+ * axp20x power button driver.
+ *
+ * Copyright (C) 2013 Carlo Caione <carlo@caione.org>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * 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/errno.h>
+#include <linux/irq.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/axp20x.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define AXP20X_PEK_STARTUP_MASK		(0xc0)
+#define AXP20X_PEK_LONGPRESS_MASK	(0x30)
+#define AXP20X_PEK_SHUTDOWN_MASK	(0x03)
+
+static const char const *startup_time[] = { "128mS", "3S" , "1S", "2S" };
+static const char const *longpress_time[] = { "1S", "1.5S" , "2S", "2.5S" };
+static const char const *shutdown_time[] = { "4S", "6S" , "8S", "10S" };
+
+struct axp20x_pek {
+	struct axp20x_dev *axp20x;
+	struct input_dev *input;
+	int irq_dbr;
+	int irq_dbf;
+};
+
+struct axp20x_pek_ext_attr {
+	const char const **str;
+	unsigned int mask;
+};
+
+static struct axp20x_pek_ext_attr axp20x_pek_startup_ext_attr = {
+	.str	= startup_time,
+	.mask	= AXP20X_PEK_STARTUP_MASK,
+};
+
+static struct axp20x_pek_ext_attr axp20x_pek_longpress_ext_attr = {
+	.str	= longpress_time,
+	.mask	= AXP20X_PEK_LONGPRESS_MASK,
+};
+
+static struct axp20x_pek_ext_attr axp20x_pek_shutdown_ext_attr = {
+	.str	= shutdown_time,
+	.mask	= AXP20X_PEK_SHUTDOWN_MASK,
+};
+
+static struct axp20x_pek_ext_attr *get_axp_ext_attr(struct device_attribute *attr)
+{
+	return container_of(attr, struct dev_ext_attribute, attr)->var;
+}
+
+ssize_t axp20x_show_ext_attr(struct device *dev, struct device_attribute *attr,
+			     char *buf)
+{
+	struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
+	struct axp20x_pek_ext_attr *axp20x_ea = get_axp_ext_attr(attr);
+	unsigned int val;
+	int ret, i;
+	int cnt = 0;
+
+	ret = regmap_read(axp20x_pek->axp20x->regmap, AXP20X_PEK_KEY, &val);
+	if (ret != 0)
+		return ret;
+
+	val &= axp20x_ea->mask;
+	val >>= ffs(axp20x_ea->mask) - 1;
+
+	for (i = 0; i < 4; i++) {
+		if (val == i)
+			cnt += sprintf(buf + cnt, "[%s] ", axp20x_ea->str[i]);
+		else
+			cnt += sprintf(buf + cnt, "%s ", axp20x_ea->str[i]);
+	}
+
+	cnt += sprintf(buf + cnt, "\n");
+
+	return cnt;
+}
+
+ssize_t axp20x_store_ext_attr(struct device *dev, struct device_attribute *attr,
+			      const char *buf, size_t count)
+{
+	struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
+	struct axp20x_pek_ext_attr *axp20x_ea = get_axp_ext_attr(attr);
+	char val_str[20];
+	int ret, i;
+	size_t len;
+
+	val_str[sizeof(val_str) - 1] = '\0';
+	strncpy(val_str, buf, sizeof(val_str) - 1);
+	len = strlen(val_str);
+
+	if (len && val_str[len - 1] == '\n')
+		val_str[len - 1] = '\0';
+
+	for (i = 0; i < 4; i++) {
+		if (!strcmp(val_str, axp20x_ea->str[i])) {
+			ret = regmap_update_bits(axp20x_pek->axp20x->regmap,
+						 AXP20X_PEK_KEY,
+						 axp20x_ea->mask, i);
+			if (ret != 0)
+				return -EINVAL;
+			return count;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static struct dev_ext_attribute axp20x_dev_attr_startup = {
+	.attr	= __ATTR(startup, 0644, axp20x_show_ext_attr, axp20x_store_ext_attr),
+	.var	= &axp20x_pek_startup_ext_attr
+};
+
+static struct dev_ext_attribute axp20x_dev_attr_longpress = {
+	__ATTR(longpress, 0644, axp20x_show_ext_attr, axp20x_store_ext_attr),
+	&axp20x_pek_longpress_ext_attr
+};
+
+static struct dev_ext_attribute axp20x_dev_attr_shutdown = {
+	__ATTR(shutdown, 0644, axp20x_show_ext_attr, axp20x_store_ext_attr),
+	&axp20x_pek_shutdown_ext_attr
+};
+
+static struct attribute *dev_attrs[] = {
+	&axp20x_dev_attr_startup.attr.attr,
+	&axp20x_dev_attr_longpress.attr.attr,
+	&axp20x_dev_attr_shutdown.attr.attr,
+	NULL,
+};
+
+static struct attribute_group dev_attr_group = {
+	.attrs	= dev_attrs,
+};
+
+static const struct attribute_group *dev_attr_groups[] = {
+	&dev_attr_group,
+	NULL,
+};
+
+static irqreturn_t axp20x_pek_irq(int irq, void *pwr)
+{
+	struct input_dev *idev = pwr;
+	struct axp20x_pek *axp20x_pek = input_get_drvdata(idev);
+
+	if (irq == axp20x_pek->irq_dbr)
+		input_report_key(idev, KEY_POWER, true);
+	else if (irq == axp20x_pek->irq_dbf)
+		input_report_key(idev, KEY_POWER, false);
+
+	input_sync(idev);
+
+	return IRQ_HANDLED;
+}
+
+static int axp20x_pek_probe(struct platform_device *pdev)
+{
+	struct axp20x_pek *axp20x_pek;
+	struct axp20x_dev *axp20x;
+	struct input_dev *idev;
+	int error;
+
+	axp20x_pek = devm_kzalloc(&pdev->dev, sizeof(struct axp20x_pek),
+				  GFP_KERNEL);
+	if (!axp20x_pek)
+		return -ENOMEM;
+
+	axp20x_pek->axp20x = dev_get_drvdata(pdev->dev.parent);
+	axp20x = axp20x_pek->axp20x;
+
+	axp20x_pek->irq_dbr = platform_get_irq_byname(pdev, "PEK_DBR");
+	if (axp20x_pek->irq_dbr < 0) {
+		dev_err(&pdev->dev, "No IRQ for PEK_DBR, error=%d\n",
+				axp20x_pek->irq_dbr);
+		return axp20x_pek->irq_dbr;
+	}
+	axp20x_pek->irq_dbr = regmap_irq_get_virq(axp20x->regmap_irqc,
+						  axp20x_pek->irq_dbr);
+
+	axp20x_pek->irq_dbf = platform_get_irq_byname(pdev, "PEK_DBF");
+	if (axp20x_pek->irq_dbf < 0) {
+		dev_err(&pdev->dev, "No IRQ for PEK_DBF, error=%d\n",
+				axp20x_pek->irq_dbf);
+		return axp20x_pek->irq_dbf;
+	}
+	axp20x_pek->irq_dbf = regmap_irq_get_virq(axp20x->regmap_irqc,
+						  axp20x_pek->irq_dbf);
+
+	axp20x_pek->input = devm_input_allocate_device(&pdev->dev);
+	if (!axp20x_pek->input)
+		return -ENOMEM;
+
+	idev = axp20x_pek->input;
+
+	idev->name = "axp20x-pek";
+	idev->phys = "m1kbd/input2";
+	idev->dev.parent = &pdev->dev;
+
+	input_set_capability(idev, EV_KEY, KEY_POWER);
+
+	input_set_drvdata(idev, axp20x_pek);
+
+	error = devm_request_threaded_irq(&pdev->dev, axp20x_pek->irq_dbr,
+					  NULL, axp20x_pek_irq, 0,
+					  "axp20x-pek-dbr", idev);
+	if (error) {
+		dev_err(axp20x->dev, "Failed to request dbr IRQ#%d: %d\n",
+			axp20x_pek->irq_dbr, error);
+
+		return error;
+	}
+
+	error = devm_request_threaded_irq(&pdev->dev, axp20x_pek->irq_dbf,
+					  NULL, axp20x_pek_irq, 0,
+					  "axp20x-pek-dbf", idev);
+	if (error) {
+		dev_err(axp20x->dev, "Failed to request dbf IRQ#%d: %d\n",
+			axp20x_pek->irq_dbf, error);
+		return error;
+	}
+
+	idev->dev.groups = dev_attr_groups;
+	error = input_register_device(idev);
+	if (error) {
+		dev_err(axp20x->dev, "Can't register input device: %d\n", error);
+		return error;
+	}
+
+	platform_set_drvdata(pdev, axp20x_pek);
+
+	return 0;
+}
+
+static int axp20x_pek_remove(struct platform_device *pdev)
+{
+	struct axp20x_pek *axp20x_pek = platform_get_drvdata(pdev);
+
+	input_unregister_device(axp20x_pek->input);
+
+	return 0;
+}
+
+static const struct of_device_id axp20x_pek_match[] = {
+	{ .compatible = "x-powers,axp20x-pek", },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, axp20x_pek_match);
+
+static struct platform_driver axp20x_pek_driver = {
+	.probe		= axp20x_pek_probe,
+	.remove		= axp20x_pek_remove,
+	.driver		= {
+		.name		= "axp20x-pek",
+		.owner		= THIS_MODULE,
+		.of_match_table	= axp20x_pek_match,
+	},
+};
+module_platform_driver(axp20x_pek_driver);
+
+MODULE_DESCRIPTION("axp20x Power Button");
+MODULE_AUTHOR("Carlo Caione <carlo@caione.org>");
+MODULE_LICENSE("GPL");
-- 
1.8.5.3

^ permalink raw reply related

* [PATCH 0/2] input: misc: Add support for axp20x Power Enable Key
From: Carlo Caione @ 2014-02-08 16:05 UTC (permalink / raw)
  To: linux-arm-kernel

This patch-set adds support for the button used by PMICs axp202 and axp209.
The button is used to power on/off the SoC connected to PMIC but also it can
automatically identify long/short press.

PEK is intended as subsystem for the MFD driver so it depends on the axp20x
header files.

Bindings documentation has been already included with documentation for the MFD.

Carlo Caione (2):
  input: misc: Add driver for axp20x power enable key
  input: misc: dts: Add axp20x-pek node to axp20x.dtsi

 arch/arm/boot/dts/axp20x.dtsi   |   6 +
 drivers/input/misc/Kconfig      |  11 ++
 drivers/input/misc/Makefile     |   1 +
 drivers/input/misc/axp20x-pek.c | 278 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 296 insertions(+)
 create mode 100644 drivers/input/misc/axp20x-pek.c

-- 
1.8.5.3

^ permalink raw reply

* [PATCH 3/3] mfd: axp20x: Add bindings documentation
From: Carlo Caione @ 2014-02-08 16:03 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391875428-6281-1-git-send-email-carlo@caione.org>

Bindings documentation for the axp20x driver. In this file also two
sub-nodes (PEK and regulators) are documented.

Signed-off-by: Carlo Caione <carlo@caione.org>
---
 Documentation/devicetree/bindings/mfd/axp20x.txt | 87 ++++++++++++++++++++++++
 1 file changed, 87 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/axp20x.txt

diff --git a/Documentation/devicetree/bindings/mfd/axp20x.txt b/Documentation/devicetree/bindings/mfd/axp20x.txt
new file mode 100644
index 0000000..ccea6b8
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/axp20x.txt
@@ -0,0 +1,87 @@
+* axp20x device tree bindings
+
+The axp20x family current members :-
+axp202 (x-powers)
+axp209 (x-powers)
+
+Required properties:
+- compatible : Should be "x-powers,axp20x" (for axp202 and axp209)
+- interrupt-controller : axp20x has its own internal IRQs
+- #interrupt-cells : should be set to 1
+- interrupt-parent : The parent interrupt controller
+- interrupts : Specifies the list of interrupt lines which are handled by
+	       the device in the parent controller's notation
+- reg : Specifies base physical address and size of the registers
+
+Sub-nodes
+* regulators : Contain the regulator nodes. The regulators are bound using
+	       their name as listed here: dcdc2, dcdc3, ldo1, ldo2, ldo3,
+	       ldo4, ldo5.
+	       The bindings details of individual regulator device can be found in:
+	       Documentation/devicetree/bindings/regulator/regulator.txt with the
+	       exception of:
+
+	- dcdc-freq : defines the work frequency of DC-DC where
+		      F=(1+dcdc-freq*5%)*1.5MHz
+
+* axp20x-pek : Power Enable Key
+	- compatible : should be "x-powers,axp20x-pek"
+	- interrupts : two interrupt numbers with order defined by interrupt-names
+		       (one irq number for rising transition of the power key, the
+		       other one for falling transition)
+	- interrupt-names : should be "PEK_DBR" and "PEK_DBF"
+
+Example:
+
+axp {
+	compatible = "x-powers,axp20x";
+	interrupt-controller;
+	#interrupt-cells = <1>;
+
+	axp20x-pek {
+		compatible = "x-powers,axp20x-pek";
+		interrupts = <33>,  <34>;
+		interrupt-names = "PEK_DBR", "PEK_DBF";
+	};
+
+	regulators {
+		dcdc-freq = "8";
+
+		axp_dcdc2: dcdc2 {
+			regulator-min-microvolt = <700000>;
+			regulator-max-microvolt = <2275000>;
+			dcdc-workmode = <0>;
+		};
+
+		axp_dcdc3: dcdc3 {
+			regulator-min-microvolt = <700000>;
+			regulator-max-microvolt = <3500000>;
+			dcdc-workmode = <0>;
+		};
+
+		axp_ldo1: ldo1 {
+			regulator-min-microvolt = <1300000>;
+			regulator-max-microvolt = <1300000>;
+		};
+
+		axp_ldo2: ldo2 {
+			regulator-min-microvolt = <1800000>;
+			regulator-max-microvolt = <3300000>;
+		};
+
+		axp_ldo3: ldo3 {
+			regulator-min-microvolt = <700000>;
+			regulator-max-microvolt = <3500000>;
+		};
+
+		axp_ldo4: ldo4 {
+			regulator-min-microvolt = <1250000>;
+			regulator-max-microvolt = <3300000>;
+		};
+
+		axp_ldo5: ldo5 {
+			regulator-min-microvolt = <1800000>;
+			regulator-max-microvolt = <3300000>;
+		};
+	};
+};
-- 
1.8.5.3

^ permalink raw reply related

* [PATCH 2/3] mfd: axp20x: Add dtsi for axp20x
From: Carlo Caione @ 2014-02-08 16:03 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391875428-6281-1-git-send-email-carlo@caione.org>

This patch adds a new dtsi for supporting axp20x.

Signed-off-by: Carlo Caione <carlo@caione.org>
---
 arch/arm/boot/dts/axp20x.dtsi | 9 +++++++++
 1 file changed, 9 insertions(+)
 create mode 100644 arch/arm/boot/dts/axp20x.dtsi

diff --git a/arch/arm/boot/dts/axp20x.dtsi b/arch/arm/boot/dts/axp20x.dtsi
new file mode 100644
index 0000000..98d4c7c
--- /dev/null
+++ b/arch/arm/boot/dts/axp20x.dtsi
@@ -0,0 +1,9 @@
+/*
+* Integrated Power Management Chip AXP209
+*/
+
+&axp {
+	compatible = "x-powers,axp20x";
+	interrupt-controller;
+	#interrupt-cells = <1>;
+};
-- 
1.8.5.3

^ permalink raw reply related

* [PATCH 1/3] mfd: axp20x: Add mfd driver for axp20x PMIC
From: Carlo Caione @ 2014-02-08 16:03 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391875428-6281-1-git-send-email-carlo@caione.org>

This patch introduces the preliminary support for PMICs X-Powers AXP202
and AXP209. The core contains support only for two sub-modules (PEK
and regulators) that will be added with two different patch-sets.

Signed-off-by: Carlo Caione <carlo@caione.org>
---
 drivers/mfd/Kconfig        |  12 +++
 drivers/mfd/Makefile       |   1 +
 drivers/mfd/axp20x.c       | 251 +++++++++++++++++++++++++++++++++++++++++++++
 include/linux/mfd/axp20x.h | 178 ++++++++++++++++++++++++++++++++
 4 files changed, 442 insertions(+)
 create mode 100644 drivers/mfd/axp20x.c
 create mode 100644 include/linux/mfd/axp20x.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index dd67158..33d38c4 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -59,6 +59,18 @@ config MFD_AAT2870_CORE
 	  additional drivers must be enabled in order to use the
 	  functionality of the device.
 
+config MFD_AXP20X
+	bool "X-Powers AXP20X"
+	select MFD_CORE
+	select REGMAP_I2C
+	select REGMAP_IRQ
+	depends on I2C=y
+	help
+	  If you say Y here you get support for the AXP20X.
+	  This driver provides common support for accessing the device,
+	  additional drivers must be enabled in order to use the
+	  functionality of the device.
+
 config MFD_CROS_EC
 	tristate "ChromeOS Embedded Controller"
 	select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 8a28dc9..371020e 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -101,6 +101,7 @@ obj-$(CONFIG_PMIC_DA9052)	+= da9052-irq.o
 obj-$(CONFIG_PMIC_DA9052)	+= da9052-core.o
 obj-$(CONFIG_MFD_DA9052_SPI)	+= da9052-spi.o
 obj-$(CONFIG_MFD_DA9052_I2C)	+= da9052-i2c.o
+obj-$(CONFIG_MFD_AXP20X)	+= axp20x.o
 
 obj-$(CONFIG_MFD_LP8788)	+= lp8788.o lp8788-irq.o
 
diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
new file mode 100644
index 0000000..efd0cb3
--- /dev/null
+++ b/drivers/mfd/axp20x.c
@@ -0,0 +1,251 @@
+/*
+ * Author: Carlo Caione <carlo@caione.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/axp20x.h>
+#include <linux/mfd/core.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+
+static const struct regmap_range axp20x_writeable_ranges[] = {
+	{
+		.range_min = AXP20X_DATA(0),
+		.range_max = AXP20X_IRQ5_STATE,
+	}, {
+		.range_min = AXP20X_DCDC_MODE,
+		.range_max = AXP20X_FG_RES,
+	},
+};
+
+static const struct regmap_range axp20x_volatile_ranges[] = {
+	{
+		.range_min = AXP20X_IRQ1_EN,
+		.range_max = AXP20X_IRQ5_STATE,
+	},
+};
+
+static const struct regmap_access_table axp20x_writeable_table = {
+	.yes_ranges	= axp20x_writeable_ranges,
+	.n_yes_ranges	= ARRAY_SIZE(axp20x_writeable_ranges),
+};
+
+static const struct regmap_access_table axp20x_volatile_table = {
+	.yes_ranges	= axp20x_volatile_ranges,
+	.n_yes_ranges	= ARRAY_SIZE(axp20x_volatile_ranges),
+};
+
+static struct resource axp20x_pek_resources[] = {
+	{
+		.name	= "PEK_DBR",
+		.start	= AXP20X_IRQ_PEK_RIS_EDGE,
+		.end	= AXP20X_IRQ_PEK_RIS_EDGE,
+		.flags	= IORESOURCE_IRQ,
+	},
+	{
+		.name	= "PEK_DBF",
+		.start	= AXP20X_IRQ_PEK_FAL_EDGE,
+		.end	= AXP20X_IRQ_PEK_FAL_EDGE,
+		.flags	= IORESOURCE_IRQ,
+	},
+
+};
+
+static const struct regmap_config axp20x_regmap_config = {
+	.reg_bits	= 8,
+	.val_bits	= 8,
+	.wr_table	= &axp20x_writeable_table,
+	.volatile_table	= &axp20x_volatile_table,
+	.max_register	= AXP20X_FG_RES,
+	.cache_type	= REGCACHE_RBTREE,
+};
+
+#define AXP20X_IRQ(_irq, _off, _mask) \
+	[AXP20X_IRQ_##_irq] = { .reg_offset = (_off), .mask = BIT(_mask) }
+
+static const struct regmap_irq axp20x_regmap_irqs[] = {
+	AXP20X_IRQ(ACIN_OVER_V,		0, 7),
+	AXP20X_IRQ(ACIN_PLUGIN,		0, 6),
+	AXP20X_IRQ(ACIN_REMOVAL,	0, 5),
+	AXP20X_IRQ(VBUS_OVER_V,		0, 4),
+	AXP20X_IRQ(VBUS_PLUGIN,		0, 3),
+	AXP20X_IRQ(VBUS_REMOVAL,	0, 2),
+	AXP20X_IRQ(VBUS_V_LOW,		0, 1),
+	AXP20X_IRQ(BATT_PLUGIN,		1, 7),
+	AXP20X_IRQ(BATT_REMOVAL,	1, 6),
+	AXP20X_IRQ(BATT_ENT_ACT_MODE,	1, 5),
+	AXP20X_IRQ(BATT_EXIT_ACT_MODE,	1, 4),
+	AXP20X_IRQ(CHARG,		1, 3),
+	AXP20X_IRQ(CHARG_DONE,		1, 2),
+	AXP20X_IRQ(BATT_TEMP_HIGH,	1, 1),
+	AXP20X_IRQ(BATT_TEMP_LOW,	1, 0),
+	AXP20X_IRQ(DIE_TEMP_HIGH,	2, 7),
+	AXP20X_IRQ(CHARG_I_LOW,		2, 6),
+	AXP20X_IRQ(DCDC1_V_LONG,	2, 5),
+	AXP20X_IRQ(DCDC2_V_LONG,	2, 4),
+	AXP20X_IRQ(DCDC3_V_LONG,	2, 3),
+	AXP20X_IRQ(PEK_SHORT,		2, 1),
+	AXP20X_IRQ(PEK_LONG,		2, 0),
+	AXP20X_IRQ(N_OE_PWR_ON,		3, 7),
+	AXP20X_IRQ(N_OE_PWR_OFF,	3, 6),
+	AXP20X_IRQ(VBUS_VALID,		3, 5),
+	AXP20X_IRQ(VBUS_NOT_VALID,	3, 4),
+	AXP20X_IRQ(VBUS_SESS_VALID,	3, 3),
+	AXP20X_IRQ(VBUS_SESS_END,	3, 2),
+	AXP20X_IRQ(LOW_PWR_LVL1,	3, 1),
+	AXP20X_IRQ(LOW_PWR_LVL2,	3, 0),
+	AXP20X_IRQ(TIMER,		4, 7),
+	AXP20X_IRQ(PEK_RIS_EDGE,	4, 6),
+	AXP20X_IRQ(PEK_FAL_EDGE,	4, 5),
+	AXP20X_IRQ(GPIO3_INPUT,		4, 3),
+	AXP20X_IRQ(GPIO2_INPUT,		4, 2),
+	AXP20X_IRQ(GPIO1_INPUT,		4, 1),
+	AXP20X_IRQ(GPIO0_INPUT,		4, 0),
+};
+
+static const struct regmap_irq_chip axp20x_regmap_irq_chip = {
+	.name			= "axp20x_irq_chip",
+	.status_base		= AXP20X_IRQ1_STATE,
+	.ack_base		= AXP20X_IRQ1_STATE,
+	.mask_base		= AXP20X_IRQ1_EN,
+	.num_regs		= 5,
+	.irqs			= axp20x_regmap_irqs,
+	.num_irqs		= ARRAY_SIZE(axp20x_regmap_irqs),
+	.mask_invert		= 1,
+	.init_ack_masked	= 1,
+};
+
+static struct mfd_cell axp20x_cells[] = {
+	{
+		.name		= "axp20x-pek",
+		.of_compatible	= "x-powers,axp20x-pek",
+		.num_resources	= ARRAY_SIZE(axp20x_pek_resources),
+		.resources	= axp20x_pek_resources,
+	}, {
+		.name		= "axp20x-regulator",
+	},
+};
+
+const struct of_device_id axp20x_of_match[] = {
+	{ .compatible = "x-powers,axp20x", .data = (void *)AXP20X },
+	{ },
+};
+
+static struct axp20x_dev *axp20x_pm_power_off;
+static void axp20x_power_off(void)
+{
+	regmap_write(axp20x_pm_power_off->regmap, AXP20X_OFF_CTRL, 0x80);
+}
+
+static int axp20x_i2c_probe(struct i2c_client *i2c,
+			 const struct i2c_device_id *id)
+{
+	struct axp20x_dev *axp20x;
+	const struct of_device_id *of_id;
+	int ret;
+
+	axp20x = devm_kzalloc(&i2c->dev, sizeof(*axp20x), GFP_KERNEL);
+	if (!axp20x)
+		return -ENOMEM;
+
+	of_id = of_match_device(axp20x_of_match, &i2c->dev);
+	if (!of_id) {
+		dev_err(&i2c->dev, "Unable to setup AXP20X data\n");
+		return -ENODEV;
+	}
+	axp20x->variant = (int) of_id->data;
+
+	axp20x->i2c_client = i2c;
+	i2c_set_clientdata(i2c, axp20x);
+
+	axp20x->dev = &i2c->dev;
+	dev_set_drvdata(axp20x->dev, axp20x);
+
+	axp20x->regmap = devm_regmap_init_i2c(i2c, &axp20x_regmap_config);
+	if (IS_ERR(axp20x->regmap)) {
+		ret = PTR_ERR(axp20x->regmap);
+		dev_err(&i2c->dev, "regmap init failed: %d\n", ret);
+		return ret;
+	}
+
+	axp20x->irq = i2c->irq;
+	ret = regmap_add_irq_chip(axp20x->regmap, axp20x->irq,
+				  IRQF_ONESHOT | IRQF_SHARED, -1,
+				  &axp20x_regmap_irq_chip,
+				  &axp20x->regmap_irqc);
+	if (ret != 0) {
+		dev_err(&i2c->dev, "failed to add irq chip: %d\n", ret);
+		return ret;
+	}
+
+	ret = mfd_add_devices(axp20x->dev, -1, axp20x_cells,
+			      ARRAY_SIZE(axp20x_cells), NULL, 0, NULL);
+	if (ret != 0) {
+		dev_err(&i2c->dev, "failed to add MFD devices: %d\n", ret);
+		goto mfd_err;
+	}
+
+	if (!pm_power_off) {
+		axp20x_pm_power_off = axp20x;
+		pm_power_off = axp20x_power_off;
+	}
+
+	dev_info(&i2c->dev, "AXP20X driver loaded\n");
+
+	return 0;
+
+mfd_err:
+	regmap_del_irq_chip(axp20x->irq, axp20x->regmap_irqc);
+
+	return ret;
+}
+
+static int axp20x_i2c_remove(struct i2c_client *i2c)
+{
+	struct axp20x_dev *axp20x = i2c_get_clientdata(i2c);
+
+	if (axp20x == axp20x_pm_power_off) {
+		axp20x_pm_power_off = NULL;
+		pm_power_off = NULL;
+	}
+
+	mfd_remove_devices(axp20x->dev);
+	regmap_del_irq_chip(axp20x->i2c_client->irq, axp20x->regmap_irqc);
+
+	return 0;
+}
+
+static const struct i2c_device_id axp20x_i2c_id[] = {
+	{ "axp20x", AXP20X },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, axp20x_i2c_id);
+
+static struct i2c_driver axp20x_i2c_driver = {
+	.driver = {
+		.name	= "axp20x",
+		.owner	= THIS_MODULE,
+		.of_match_table	= of_match_ptr(axp20x_of_match),
+	},
+	.probe		= axp20x_i2c_probe,
+	.remove		= axp20x_i2c_remove,
+	.id_table	= axp20x_i2c_id,
+};
+
+module_i2c_driver(axp20x_i2c_driver);
+
+MODULE_DESCRIPTION("PMIC MFD core driver for AXP20X");
+MODULE_AUTHOR("Carlo Caione <carlo@caione.org>");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h
new file mode 100644
index 0000000..94d99fd
--- /dev/null
+++ b/include/linux/mfd/axp20x.h
@@ -0,0 +1,178 @@
+/*
+ * Functions to access AXP20X power management chip.
+ *
+ * Copyright (C) 2013, Carlo Caione <carlo@caione.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __LINUX_MFD_AXP20X_H
+#define __LINUX_MFD_AXP20X_H
+
+#define AXP20X				0
+
+#define AXP20X_DATA(m)			(0x04 + (m))
+
+/* Power supply */
+#define AXP20X_PWR_INPUT_STATUS		0x00
+#define AXP20X_PWR_OP_MODE		0x01
+#define AXP20X_USB_OTG_STATUS		0x02
+#define AXP20X_PWR_OUT_CTRL		0x12
+#define AXP20X_DCDC2_V_OUT		0x23
+#define AXP20X_DCDC2_LDO3_V_SCAL	0x25
+#define AXP20X_DCDC3_V_OUT		0x27
+#define AXP20X_LDO24_V_OUT		0x28
+#define AXP20X_LDO3_V_OUT		0x29
+#define AXP20X_VBUS_IPSOUT_MGMT		0x30
+#define AXP20X_V_OFF			0x31
+#define AXP20X_OFF_CTRL			0x32
+#define AXP20X_CHRG_CTRL1		0x33
+#define AXP20X_CHRG_CTRL2		0x34
+#define AXP20X_CHRG_BAK_CTRL		0x35
+#define AXP20X_PEK_KEY			0x36
+#define AXP20X_DCDC_FREQ		0x37
+#define AXP20X_V_LTF_CHRG		0x38
+#define AXP20X_V_HTF_CHRG		0x39
+#define AXP20X_APS_WARN_L1		0x3a
+#define AXP20X_APS_WARN_L2		0x3b
+#define AXP20X_V_LTF_DISCHRG		0x3c
+#define AXP20X_V_HTF_DISCHRG		0x3d
+
+/* Interrupt */
+#define AXP20X_IRQ1_EN			0x40
+#define AXP20X_IRQ2_EN			0x41
+#define AXP20X_IRQ3_EN			0x42
+#define AXP20X_IRQ4_EN			0x43
+#define AXP20X_IRQ5_EN			0x44
+#define AXP20X_IRQ1_STATE		0x48
+#define AXP20X_IRQ2_STATE		0x49
+#define AXP20X_IRQ3_STATE		0x4a
+#define AXP20X_IRQ4_STATE		0x4b
+#define AXP20X_IRQ5_STATE		0x4c
+
+/* ADC */
+#define AXP20X_ACIN_V_ADC_H		0x56
+#define AXP20X_ACIN_V_ADC_L		0x57
+#define AXP20X_ACIN_I_ADC_H		0x58
+#define AXP20X_ACIN_I_ADC_L		0x59
+#define AXP20X_VBUS_V_ADC_H		0x5a
+#define AXP20X_VBUS_V_ADC_L		0x5b
+#define AXP20X_VBUS_I_ADC_H		0x5c
+#define AXP20X_VBUS_I_ADC_L		0x5d
+#define AXP20X_TEMP_ADC_H		0x5e
+#define AXP20X_TEMP_ADC_L		0x5f
+#define AXP20X_TS_IN_H			0x62
+#define AXP20X_TS_IN_L			0x63
+#define AXP20X_GPIO0_V_ADC_H		0x64
+#define AXP20X_GPIO0_V_ADC_L		0x65
+#define AXP20X_GPIO1_V_ADC_H		0x66
+#define AXP20X_GPIO1_V_ADC_L		0x67
+#define AXP20X_PWR_BATT_H		0x70
+#define AXP20X_PWR_BATT_M		0x71
+#define AXP20X_PWR_BATT_L		0x72
+#define AXP20X_BATT_V_H			0x78
+#define AXP20X_BATT_V_L			0x79
+#define AXP20X_BATT_CHRG_I_H		0x7a
+#define AXP20X_BATT_CHRG_I_L		0x7b
+#define AXP20X_BATT_DISCHRG_I_H		0x7c
+#define AXP20X_BATT_DISCHRG_I_L		0x7d
+#define AXP20X_IPSOUT_V_HIGH_H		0x7e
+#define AXP20X_IPSOUT_V_HIGH_L		0x7f
+
+/* Power supply */
+#define AXP20X_DCDC_MODE		0x80
+#define AXP20X_ADC_EN1			0x82
+#define AXP20X_ADC_EN2			0x83
+#define AXP20X_ADC_RATE			0x84
+#define AXP20X_GPIO10_IN_RANGE		0x85
+#define AXP20X_GPIO1_ADC_IRQ_RIS	0x86
+#define AXP20X_GPIO1_ADC_IRQ_FAL	0x87
+#define AXP20X_TIMER_CTRL		0x8a
+#define AXP20X_VBUS_MON			0x8b
+#define AXP20X_OVER_TMP			0x8f
+
+/* GPIO */
+#define AXP20X_GPIO0_CTRL		0x90
+#define AXP20X_LDO5_V_OUT		0x91
+#define AXP20X_GPIO1_CTRL		0x92
+#define AXP20X_GPIO2_CTRL		0x93
+#define AXP20X_GPIO20_SS		0x94
+#define AXP20X_GPIO3_CTRL		0x95
+
+/* Battery */
+#define AXP20X_CHRG_CC_31_24		0xb0
+#define AXP20X_CHRG_CC_23_16		0xb1
+#define AXP20X_CHRG_CC_15_8		0xb2
+#define AXP20X_CHRG_CC_7_0		0xb3
+#define AXP20X_DISCHRG_CC_31_24		0xb4
+#define AXP20X_DISCHRG_CC_23_16		0xb5
+#define AXP20X_DISCHRG_CC_15_8		0xb6
+#define AXP20X_DISCHRG_CC_7_0		0xb7
+#define AXP20X_CC_CTRL			0xb8
+#define AXP20X_FG_RES			0xb9
+
+/* Regulators IDs */
+enum {
+	AXP20X_LDO1 = 0,
+	AXP20X_LDO2,
+	AXP20X_LDO3,
+	AXP20X_LDO4,
+	AXP20X_LDO5,
+	AXP20X_DCDC2,
+	AXP20X_DCDC3,
+	AXP20X_REG_ID_MAX,
+};
+
+/* IRQs */
+enum {
+	AXP20X_IRQ_ACIN_OVER_V = 1,
+	AXP20X_IRQ_ACIN_PLUGIN,
+	AXP20X_IRQ_ACIN_REMOVAL,
+	AXP20X_IRQ_VBUS_OVER_V,
+	AXP20X_IRQ_VBUS_PLUGIN,
+	AXP20X_IRQ_VBUS_REMOVAL,
+	AXP20X_IRQ_VBUS_V_LOW,
+	AXP20X_IRQ_BATT_PLUGIN,
+	AXP20X_IRQ_BATT_REMOVAL,
+	AXP20X_IRQ_BATT_ENT_ACT_MODE,
+	AXP20X_IRQ_BATT_EXIT_ACT_MODE,
+	AXP20X_IRQ_CHARG,
+	AXP20X_IRQ_CHARG_DONE,
+	AXP20X_IRQ_BATT_TEMP_HIGH,
+	AXP20X_IRQ_BATT_TEMP_LOW,
+	AXP20X_IRQ_DIE_TEMP_HIGH,
+	AXP20X_IRQ_CHARG_I_LOW,
+	AXP20X_IRQ_DCDC1_V_LONG,
+	AXP20X_IRQ_DCDC2_V_LONG,
+	AXP20X_IRQ_DCDC3_V_LONG,
+	AXP20X_IRQ_PEK_SHORT = 22,
+	AXP20X_IRQ_PEK_LONG,
+	AXP20X_IRQ_N_OE_PWR_ON,
+	AXP20X_IRQ_N_OE_PWR_OFF,
+	AXP20X_IRQ_VBUS_VALID,
+	AXP20X_IRQ_VBUS_NOT_VALID,
+	AXP20X_IRQ_VBUS_SESS_VALID,
+	AXP20X_IRQ_VBUS_SESS_END,
+	AXP20X_IRQ_LOW_PWR_LVL1,
+	AXP20X_IRQ_LOW_PWR_LVL2,
+	AXP20X_IRQ_TIMER,
+	AXP20X_IRQ_PEK_RIS_EDGE,
+	AXP20X_IRQ_PEK_FAL_EDGE,
+	AXP20X_IRQ_GPIO3_INPUT,
+	AXP20X_IRQ_GPIO2_INPUT,
+	AXP20X_IRQ_GPIO1_INPUT,
+	AXP20X_IRQ_GPIO0_INPUT,
+};
+
+struct axp20x_dev {
+	struct device			*dev;
+	struct i2c_client		*i2c_client;
+	struct regmap			*regmap;
+	struct regmap_irq_chip_data	*regmap_irqc;
+	int				variant;
+	int				irq;
+};
+
+#endif /* __LINUX_MFD_AXP20X_H */
-- 
1.8.5.3

^ permalink raw reply related

* [PATCH 0/3] mfd: axp20x: Add support for PMIC axp202 and axp209
From: Carlo Caione @ 2014-02-08 16:03 UTC (permalink / raw)
  To: linux-arm-kernel

This set of patches add prelinary support for PMIC X-Powers axp202 and axp209.
Only two subsystem are supported at the moment: power enable key (PEK) and
regulators. Drivers for these two sub-system will be submitted in later
patch-sets.

Carlo Caione (3):
  mfd: axp20x: Add mfd driver for axp20x PMIC
  mfd: axp20x: Add dtsi for axp20x
  mfd: axp20x: Add bindings documentation

 Documentation/devicetree/bindings/mfd/axp20x.txt |  87 ++++++++
 arch/arm/boot/dts/axp20x.dtsi                    |   9 +
 drivers/mfd/Kconfig                              |  12 ++
 drivers/mfd/Makefile                             |   1 +
 drivers/mfd/axp20x.c                             | 251 +++++++++++++++++++++++
 include/linux/mfd/axp20x.h                       | 178 ++++++++++++++++
 6 files changed, 538 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/axp20x.txt
 create mode 100644 arch/arm/boot/dts/axp20x.dtsi
 create mode 100644 drivers/mfd/axp20x.c
 create mode 100644 include/linux/mfd/axp20x.h

-- 
1.8.5.3

^ permalink raw reply

* [PATCH 02/21] IRQ: Orion: Fix getting generic chip pointer.
From: Jason Cooper @ 2014-02-08 15:45 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <52F55F19.3060800@gmail.com>

On Fri, Feb 07, 2014 at 11:32:57PM +0100, Sebastian Hesselbarth wrote:
> On 02/07/2014 12:41 AM, Andrew Lunn wrote:
> >Enabling SPARSE_IRQ shows up a bug in the irq-orion bridge interrupt
> >handler. The bridge interrupt is implemented using a single generic
> >chip. Thus the parameter passed to irq_get_domain_generic_chip()
> >should always be zero.
> >
> >Signed-off-by: Andrew Lunn <andrew@lunn.ch>
> 
> Indeed, I remember we talked about it: irq_get_domain_generic_chip takes
> hwirqs, so passing 0 is sane here.
> 
> Acked-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>

Added and pushed to mvebu-next/irqchip-fixes

thx,

Jason.

^ permalink raw reply

* [PATCH] pci: Add support for creating a generic host_bridge from device tree
From: Liviu Dudau @ 2014-02-08 14:22 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CACoXjcnTHgtBDesqFUOca1nf34HvNGAV0vP9AEB5xV1mvcRGfw@mail.gmail.com>

On Sat, Feb 08, 2014 at 12:21:56AM +0000, Tanmay Inamdar wrote:
> On Thu, Feb 6, 2014 at 2:18 AM, Liviu Dudau <Liviu.Dudau@arm.com> wrote:
> > On Wed, Feb 05, 2014 at 10:26:27PM +0000, Tanmay Inamdar wrote:
> >> Hello Liviu,
> >>
> >> I did not get the first email of this particular patch on any of
> >> subscribed mailing lists (don't know why), hence replying here.
> >
> > Strange, it shows in the MARC and GMANE archive for linux-pci, probably
> > a hickup on your receiving side?
> >
> >>
> >> +struct pci_host_bridge *
> >> +pci_host_bridge_of_init(struct device *parent, int busno, struct pci_ops *ops,
> >> + void *host_data, struct list_head *resources)
> >> +{
> >> + struct pci_bus *root_bus;
> >> + struct pci_host_bridge *bridge;
> >> +
> >> + /* first parse the host bridge bus ranges */
> >> + if (pci_host_bridge_of_get_ranges(parent->of_node, resources))
> >> + return NULL;
> >> +
> >> + /* then create the root bus */
> >> + root_bus = pci_create_root_bus(parent, busno, ops, host_data, resources);
> >> + if (!root_bus)
> >> + return NULL;
> >> +
> >> + bridge = to_pci_host_bridge(root_bus->bridge);
> >> +
> >> + return bridge;
> >> +}
> >>
> >> You are keeping the domain_nr inside pci_host_bridge structure. In
> >> above API, domain_nr is required in 'pci_find_bus' function called
> >> from 'pci_create_root_bus'. Since the bridge is allocated after
> >> creating root bus, 'pci_find_bus' always gets domain_nr as 0. This
> >> will cause problem for scanning multiple domains.
> >
> > Good catch. I was switching between creating a pci_controller in arch/arm64 and
> > adding the needed bits in pci_host_bridge. After internal review I've decided to
> > add the domain_nr to pci_host_bridge, but forgot to update the code everywhere.
> >
> > Thanks for reviewing this, will fix in v2.
> >
> > Do you find porting to the new API straight forward?
>
> It is quite straight forward for MEM regions but for IO regions it is
> not. You always assume IO resource starting at 0x0. IMO, this will
> cause problem for systems with multiple ports / IO windows. You can
> take a look at 'drivers/pci/host/pcie-designware.c'.
>
> Also the manipulations of addresses for IO_RESOURCES can be dangerous.
> It can make some value negative. For example if my PCI IO range starts
> in 32 bit address range say 0x8000_0000 with CPU address
> 0xb0_8000_0000, window->offset after manipulation will become
> negative.
>
> Personally I would like to do get all the PCI and CPU addresses from
> my device tree as is and do the 'pci_add_resource_offset'. This will
> help me setup my regions correctly without any further arithmetic.
> Please let me know what you think.

Yes, that parsing code of ranges is incorrect in light of the current discussion. I
will try to propose a fix in the v2 series.

Regarding your PCI IO range: if your bus address starts at 0x8000_0000, would
that not cause problems with devices that use less than 32bits for address
decoding? It is a rather unusual setup, I believe the x86 world (even PCI spec?)
mandates IO bus ranges in the first 16MB of address range?

Best regards,
Liviu

>
> >
> > Best regards,
> > Liviu
> >
> >>
> >>
> >> On Mon, Feb 3, 2014 at 10:46 AM, Arnd Bergmann <arnd@arndb.de> wrote:
> >> > On Monday 03 February 2014 18:33:48 Liviu Dudau wrote:
> >> >> +/**
> >> >> + * pci_host_bridge_of_get_ranges - Parse PCI host bridge resources from DT
> >> >> + * @dev: device node of the host bridge having the range property
> >> >> + * @resources: list where the range of resources will be added after DT parsing
> >> >> + *
> >> >> + * This function will parse the "ranges" property of a PCI host bridge device
> >> >> + * node and setup the resource mapping based on its content. It is expected
> >> >> + * that the property conforms with the Power ePAPR document.
> >> >> + *
> >> >> + * Each architecture will then apply their filtering based on the limitations
> >> >> + * of each platform. One general restriction seems to be the number of IO space
> >> >> + * ranges, the PCI framework makes intensive use of struct resource management,
> >> >> + * and for IORESOURCE_IO types they can only be requested if they are contained
> >> >> + * within the global ioport_resource, so that should be limited to one IO space
> >> >> + * range.
> >> >
> >> > Actually we have quite a different set of restrictions around I/O space on ARM32
> >> > at the moment: Each host bridge can have its own 64KB range in an arbitrary
> >> > location on MMIO space, and the total must not exceed 2MB of I/O space.
> >> >
> >> >> + */
> >> >> +static int pci_host_bridge_of_get_ranges(struct device_node *dev,
> >> >> +                                     struct list_head *resources)
> >> >> +{
> >> >> +     struct resource *res;
> >> >> +     struct of_pci_range range;
> >> >> +     struct of_pci_range_parser parser;
> >> >> +     int err;
> >> >> +
> >> >> +     pr_info("PCI host bridge %s ranges:\n", dev->full_name);
> >> >> +
> >> >> +     /* Check for ranges property */
> >> >> +     err = of_pci_range_parser_init(&parser, dev);
> >> >> +     if (err)
> >> >> +             return err;
> >> >> +
> >> >> +     pr_debug("Parsing ranges property...\n");
> >> >> +     for_each_of_pci_range(&parser, &range) {
> >> >> +             /* Read next ranges element */
> >> >> +             pr_debug("pci_space: 0x%08x pci_addr:0x%016llx ",
> >> >> +                             range.pci_space, range.pci_addr);
> >> >> +             pr_debug("cpu_addr:0x%016llx size:0x%016llx\n",
> >> >> +                                     range.cpu_addr, range.size);
> >> >> +
> >> >> +             /* If we failed translation or got a zero-sized region
> >> >> +              * (some FW try to feed us with non sensical zero sized regions
> >> >> +              * such as power3 which look like some kind of attempt
> >> >> +              * at exposing the VGA memory hole) then skip this range
> >> >> +              */
> >> >> +             if (range.cpu_addr == OF_BAD_ADDR || range.size == 0)
> >> >> +                     continue;
> >> >> +
> >> >> +             res = kzalloc(sizeof(struct resource), GFP_KERNEL);
> >> >> +             if (!res) {
> >> >> +                     err = -ENOMEM;
> >> >> +                     goto bridge_ranges_nomem;
> >> >> +             }
> >> >> +
> >> >> +             of_pci_range_to_resource(&range, dev, res);
> >> >> +
> >> >> +             pci_add_resource_offset(resources, res,
> >> >> +                             range.cpu_addr - range.pci_addr);
> >> >> +     }
> >> >
> >> > I believe of_pci_range_to_resource() will return the MMIO aperture for the
> >> > I/O space window here, which is not what you are supposed to pass into
> >> > pci_add_resource_offset.
> >> >
> >> >> +EXPORT_SYMBOL(pci_host_bridge_of_init);
> >> >
> >> > EXPORT_SYMBOL_GPL
> >> >
> >> >> diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
> >> >> index 6e34498..16febae 100644
> >> >> --- a/drivers/pci/probe.c
> >> >> +++ b/drivers/pci/probe.c
> >> >> @@ -1787,6 +1787,17 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
> >> >>       list_for_each_entry_safe(window, n, resources, list) {
> >> >>               list_move_tail(&window->list, &bridge->windows);
> >> >>               res = window->res;
> >> >> +             /*
> >> >> +              * IO resources are stored in the kernel with a CPU start
> >> >> +              * address of zero. Adjust the data accordingly and remember
> >> >> +              * the offset
> >> >> +              */
> >> >> +             if (resource_type(res) == IORESOURCE_IO) {
> >> >> +                     bridge->io_offset = res->start;
> >> >> +                     res->end -= res->start;
> >> >> +                     window->offset -= res->start;
> >> >> +                     res->start = 0;
> >> >> +             }
> >> >>               offset = window->offset;
> >> >>               if (res->flags & IORESOURCE_BUS)
> >> >
> >> > Won't this break all existing host bridges?
> >> >
> >> >         Arnd
> >> >
> >> > _______________________________________________
> >> > linux-arm-kernel mailing list
> >> > linux-arm-kernel at lists.infradead.org
> >> > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> >>
> >
> > --
> > ====================
> > | I would like to |
> > | fix the world,  |
> > | but they're not |
> > | giving me the   |
> >  \ source code!  /
> >   ---------------
> >     ?\_(?)_/?
> >
> > -- IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium.  Thank you.
> >
> > ARM Limited, Registered office 110 Fulbourn Road, Cambridge CB1 9NJ, Registered in England & Wales, Company No:  2557590
> > ARM Holdings plc, Registered office 110 Fulbourn Road, Cambridge CB1 9NJ, Registered in England & Wales, Company No:  2548782
> >
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pci" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>

--
====================
| I would like to |
| fix the world,  |
| but they're not |
| giving me the   |
 \ source code!  /
  ---------------
    ?\_(?)_/?

-- IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium.  Thank you.

ARM Limited, Registered office 110 Fulbourn Road, Cambridge CB1 9NJ, Registered in England & Wales, Company No:  2557590
ARM Holdings plc, Registered office 110 Fulbourn Road, Cambridge CB1 9NJ, Registered in England & Wales, Company No:  2548782

^ permalink raw reply

* [PATCH] ARM: OMAP1: nokia770: enable tahvo-usb
From: Aaro Koskinen @ 2014-02-08 13:48 UTC (permalink / raw)
  To: linux-arm-kernel

Add platform data for tahvo-usb. This is the last missing piece to get
Tahvo USB working with 3.14.

Signed-off-by: Aaro Koskinen <aaro.koskinen@iki.fi>
---
 arch/arm/mach-omap1/board-nokia770.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/mach-omap1/board-nokia770.c b/arch/arm/mach-omap1/board-nokia770.c
index 91449c5cb70f..85089d821982 100644
--- a/arch/arm/mach-omap1/board-nokia770.c
+++ b/arch/arm/mach-omap1/board-nokia770.c
@@ -156,6 +156,7 @@ static struct omap_usb_config nokia770_usb_config __initdata = {
 	.register_dev	= 1,
 	.hmc_mode	= 16,
 	.pins[0]	= 6,
+	.extcon		= "tahvo-usb",
 };
 
 #if defined(CONFIG_MMC_OMAP) || defined(CONFIG_MMC_OMAP_MODULE)
-- 
1.8.5.3

^ permalink raw reply related

* [PATCH RESEND v3] clk: return probe defer when DT clock not yet ready
From: Jean-Francois Moine @ 2014-02-08 13:25 UTC (permalink / raw)
  To: linux-arm-kernel

At probe time, a clock device may not be ready when some other device
wants to use it.

This patch lets the functions clk_get/devm_clk_get return a probe defer
when the clock is defined in the DT but not yet available.

Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
resend v3:
	- base kernel 3.14.0-rc1
	- __clk_get() failure is fixed
resend v2: remove ASoc from subject (thank you, Mark)
resend: - patch subject change from
   [PATCH v3 1/2] ASoC: kirkwood: clk: probe defer when clock not yet ready
	- base kernel 3.13.0-rc1
v2: fix __clk_get() failure (from Russell King)
---
 drivers/clk/clk.c    | 2 +-
 drivers/clk/clkdev.c | 2 ++
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 5517944..32d84e9 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -2472,7 +2472,7 @@ EXPORT_SYMBOL_GPL(of_clk_del_provider);
 struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec)
 {
 	struct of_clk_provider *provider;
-	struct clk *clk = ERR_PTR(-ENOENT);
+	struct clk *clk = ERR_PTR(-EPROBE_DEFER);
 
 	/* Check if we have such a provider in our array */
 	list_for_each_entry(provider, &of_clk_providers, link) {
diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c
index 48f6721..a360b2e 100644
--- a/drivers/clk/clkdev.c
+++ b/drivers/clk/clkdev.c
@@ -167,6 +167,8 @@ struct clk *clk_get(struct device *dev, const char *con_id)
 		clk = of_clk_get_by_name(dev->of_node, con_id);
 		if (!IS_ERR(clk))
 			return clk;
+		if (PTR_ERR(clk) == -EPROBE_DEFER)
+			return clk;
 	}
 
 	return clk_get_sys(dev_id, con_id);
-- 
1.9.0.rc3

^ permalink raw reply related

* [PATCH 2/2] serial: atmel: evaluate linux,stdout-path property
From: Jean-Christophe PLAGNIOL-VILLARD @ 2014-02-08 11:53 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1391860433-5343-1-git-send-email-plagnioj@jcrosoft.com>

if the driver is built as module skip it as add_preferred_console is not
exported

Cc: linux-serial at vger.kernel.org
Cc: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
---
 drivers/tty/serial/atmel_serial.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index a49f10d..2ade64e 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -2044,6 +2044,19 @@ static struct uart_ops atmel_pops = {
 #endif
 };
 
+static void atmel_of_preferred_console(struct atmel_uart_port *atmel_port,
+				       struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct uart_port *port = &atmel_port->uart;
+	char* option;
+
+	if (!np || !of_device_is_stdout_path(np, &option))
+		return;
+
+	add_preferred_console(ATMEL_DEVICENAME, port->line, option);
+}
+
 /*
  * Configure the port from the platform device resource info.
  */
@@ -2059,6 +2072,9 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port,
 
 	atmel_init_rs485(atmel_port, pdev);
 
+	if (IS_BUILTIN(CONFIG_SERIAL_ATMEL))
+		atmel_of_preferred_console(atmel_port, pdev);
+
 	port->iotype		= UPIO_MEM;
 	port->flags		= UPF_BOOT_AUTOCONF;
 	port->ops		= &atmel_pops;
-- 
1.9.rc1

^ permalink raw reply related

* [PATCH 1/2] OF: update stdout-path parsing
From: Jean-Christophe PLAGNIOL-VILLARD @ 2014-02-08 11:53 UTC (permalink / raw)
  To: linux-arm-kernel

Today we only check if the linux,stdout-path property is present and retreive
the node. But the DT specification allow to pass the serial options too as
this
	linux,stdout-path = &UART0;

or with options

	inux,stdout-path = "/plb at 0/serial at 84000000:115200";

So update the parsing to support and also the backward compatible stdout-path
property.

Cc: linux-serial at vger.kernel.org
Cc: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
---
 drivers/of/base.c  | 57 +++++++++++++++++++++++++++++++++++++++++++++---------
 include/linux/of.h |  4 ++--
 2 files changed, 50 insertions(+), 11 deletions(-)

diff --git a/drivers/of/base.c b/drivers/of/base.c
index ff85450..ab9ff2f 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -34,6 +34,7 @@ EXPORT_SYMBOL(of_allnodes);
 struct device_node *of_chosen;
 struct device_node *of_aliases;
 static struct device_node *of_stdout;
+static char *of_stdout_option;
 
 DEFINE_MUTEX(of_aliases_mutex);
 
@@ -1783,6 +1784,45 @@ static void of_alias_add(struct alias_prop *ap, struct device_node *np,
 }
 
 /**
+ * of_stdout_path_scan - check if a device node matches the
+ *                           linux,stdout-path property and parse it
+ */
+static void of_stdout_path_scan(void)
+{
+	const char *name;
+	const char *tmp;
+	const char *tmp_option;
+
+	name = of_get_property(of_chosen, "linux,stdout-path", NULL);
+	if (!name)
+		name = of_get_property(of_chosen, "stdout-path", NULL);
+
+	if (!name)
+		return;
+
+	tmp_option = strchr(name, ':');
+
+	if (!tmp_option) {
+		of_stdout = of_find_node_by_path(name);
+		return;
+	}
+
+	tmp = kstrndup(name, strlen(name) - strlen(tmp_option), GFP_KERNEL);
+	if (!tmp)
+		return;
+
+	of_stdout = of_find_node_by_path(tmp);
+	if (!of_stdout)
+		goto out;
+
+	tmp_option++;
+	of_stdout_option = kstrdup(tmp_option, GFP_KERNEL);
+
+out:
+	kfree(tmp);
+}
+
+/**
  * of_alias_scan - Scan all properties of 'aliases' node
  *
  * The function scans all the properties of 'aliases' node and populate
@@ -1800,13 +1840,8 @@ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align))
 	if (of_chosen == NULL)
 		of_chosen = of_find_node_by_path("/chosen at 0");
 
-	if (of_chosen) {
-		const char *name;
-
-		name = of_get_property(of_chosen, "linux,stdout-path", NULL);
-		if (name)
-			of_stdout = of_find_node_by_path(name);
-	}
+	if (of_chosen)
+		of_stdout_path_scan();
 
 	of_aliases = of_find_node_by_path("/aliases");
 	if (!of_aliases)
@@ -1923,13 +1958,17 @@ EXPORT_SYMBOL_GPL(of_prop_next_string);
  *                            linux,stdout-path property
  *
  * Check if this device node matches the linux,stdout-path property
- * in the chosen node. return true if yes, false otherwise.
+ * in the chosen node. return true if yes, false otherwise with the option if
+ * requested.
  */
-int of_device_is_stdout_path(struct device_node *dn)
+int of_device_is_stdout_path(struct device_node *dn, char** option)
 {
 	if (!of_stdout)
 		return false;
 
+	if (option)
+		*option = of_stdout_option;
+
 	return of_stdout == dn;
 }
 EXPORT_SYMBOL_GPL(of_device_is_stdout_path);
diff --git a/include/linux/of.h b/include/linux/of.h
index 70c64ba..a8afe44 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -352,7 +352,7 @@ const __be32 *of_prop_next_u32(struct property *prop, const __be32 *cur,
  */
 const char *of_prop_next_string(struct property *prop, const char *cur);
 
-int of_device_is_stdout_path(struct device_node *dn);
+int of_device_is_stdout_path(struct device_node *dn, char** option);
 
 #else /* CONFIG_OF */
 
@@ -542,7 +542,7 @@ static inline int of_machine_is_compatible(const char *compat)
 	return 0;
 }
 
-static inline int of_device_is_stdout_path(struct device_node *dn)
+static inline int of_device_is_stdout_path(struct device_node *dn, char** option)
 {
 	return 0;
 }
-- 
1.9.rc1

^ permalink raw reply related

* [PATCH v3] pwm: add CSR SiRFSoC PWM driver
From: Barry Song @ 2014-02-08 10:23 UTC (permalink / raw)
  To: linux-arm-kernel

From: Rongjun Ying <Rongjun.ying@csr.com>

PWM controller of CSR SiRFSoC can generate 7 independent outputs. Each output
duty cycle can be adjusted by setting the corresponding wait & hold registers.

Supports 7 independent channel output: 6 for external(channel0-5) and 1 for
internal(channel6).

Supports wide frequency range: divide by 2 to 65536*2 of source clock.

Signed-off-by: Rongjun Ying <Rongjun.ying@csr.com>
Signed-off-by: Huayi Li <Huayi.Li@csr.com>
Signed-off-by: Barry Song <Baohua.Song@csr.com>
---
 -v3:
 add "depends on" COMPILE_TEST according to Arnd's feedback;
 move the pwm clock source to dts according to Arnd's feedback;
 add lost dt-binding document

 Documentation/devicetree/bindings/pwm/pwm-sirf.txt |   17 +
 arch/arm/boot/dts/atlas6.dtsi                      |    3 +-
 arch/arm/boot/dts/prima2.dtsi                      |    3 +-
 drivers/pwm/Kconfig                                |    9 +
 drivers/pwm/Makefile                               |    1 +
 drivers/pwm/pwm-sirf.c                             |  308 ++++++++++++++++++++
 6 files changed, 339 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/pwm/pwm-sirf.txt
 create mode 100644 drivers/pwm/pwm-sirf.c

diff --git a/Documentation/devicetree/bindings/pwm/pwm-sirf.txt b/Documentation/devicetree/bindings/pwm/pwm-sirf.txt
new file mode 100644
index 0000000..4b10109
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/pwm-sirf.txt
@@ -0,0 +1,17 @@
+SiRF prima2 & atlas6 PWM drivers
+
+Required properties:
+- compatible: "sirf,prima2-pwm"
+- reg: physical base address and length of the controller's registers
+- #pwm-cells: should be 2.  The first cell specifies the per-chip index
+  of the PWM to use and the second cell is the period in nanoseconds.
+- clocks: from common clock binding: the 1st clock is for PWM controller
+  the 2nd clock is the source to generate PWM waves
+
+Example:
+pwm: pwm at b0130000 {
+	compatible = "sirf,prima2-pwm";
+	#pwm-cells = <2>;
+	reg = <0xb0130000 0x10000>;
+	clocks = <&clks 21>, <&clks 1>;
+};
diff --git a/arch/arm/boot/dts/atlas6.dtsi b/arch/arm/boot/dts/atlas6.dtsi
index f8674bc..5a09815 100644
--- a/arch/arm/boot/dts/atlas6.dtsi
+++ b/arch/arm/boot/dts/atlas6.dtsi
@@ -614,8 +614,9 @@
 
 			pwm at b0130000 {
 				compatible = "sirf,prima2-pwm";
+				#pwm-cells = <2>;
 				reg = <0xb0130000 0x10000>;
-				clocks = <&clks 21>;
+				clocks = <&clks 21>, <&clks 1>;
 			};
 
 			efusesys at b0140000 {
diff --git a/arch/arm/boot/dts/prima2.dtsi b/arch/arm/boot/dts/prima2.dtsi
index 0e21993..3439e48 100644
--- a/arch/arm/boot/dts/prima2.dtsi
+++ b/arch/arm/boot/dts/prima2.dtsi
@@ -642,8 +642,9 @@
 
 			pwm at b0130000 {
 				compatible = "sirf,prima2-pwm";
+				#pwm-cells = <2>;
 				reg = <0xb0130000 0x10000>;
-				clocks = <&clks 21>;
+				clocks = <&clks 21>, <&clks 1>;
 			};
 
 			efusesys at b0140000 {
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 22f2f28..f2165eb 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -176,6 +176,15 @@ config PWM_SAMSUNG
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-samsung.
 
+config PWM_SIRF
+	tristate "SiRF PWM support"
+	depends on (ARCH_SIRF || COMPILE_TEST) && COMMON_CLK && OF
+	help
+	  Generic PWM framework driver for SiRF SoC.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-sirf.
+
 config PWM_SPEAR
 	tristate "STMicroelectronics SPEAr PWM support"
 	depends on PLAT_SPEAR
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index d8906ec..aa222eb 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_PWM_PUV3)		+= pwm-puv3.o
 obj-$(CONFIG_PWM_PXA)		+= pwm-pxa.o
 obj-$(CONFIG_PWM_RENESAS_TPU)	+= pwm-renesas-tpu.o
 obj-$(CONFIG_PWM_SAMSUNG)	+= pwm-samsung.o
+obj-$(CONFIG_PWM_SIRF)		+= pwm-sirf.o
 obj-$(CONFIG_PWM_SPEAR)		+= pwm-spear.o
 obj-$(CONFIG_PWM_TEGRA)		+= pwm-tegra.o
 obj-$(CONFIG_PWM_TIECAP)	+= pwm-tiecap.o
diff --git a/drivers/pwm/pwm-sirf.c b/drivers/pwm/pwm-sirf.c
new file mode 100644
index 0000000..b717de0
--- /dev/null
+++ b/drivers/pwm/pwm-sirf.c
@@ -0,0 +1,308 @@
+/*
+ * SIRF serial SoC PWM device core driver
+ *
+ * Copyright (c) 2014 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/pwm.h>
+#include <linux/of.h>
+#include <linux/io.h>
+
+#define SIRF_PWM_SELECT_PRECLK			0x0
+#define SIRF_PWM_OE				0x4
+#define SIRF_PWM_ENABLE_PRECLOCK		0x8
+#define SIRF_PWM_ENABLE_POSTCLOCK		0xC
+#define SIRF_PWM_GET_WAIT_OFFSET(n)		(0x10 + 0x8*n)
+#define SIRF_PWM_GET_HOLD_OFFSET(n)		(0x14 + 0x8*n)
+
+#define SIRF_PWM_TR_STEP(n)			(0x48 + 0x8*n)
+#define SIRF_PWM_STEP_HOLD(n)			(0x4c + 0x8*n)
+
+#define SRC_FIELD_SIZE				3
+#define BYPASS_MODE_BIT				21
+#define TRANS_MODE_SELECT_BIT			7
+
+#define SIRF_PWM_CHL_NUM			7
+#define SIRF_PWM_BLS_GRP_NUM			16
+
+struct sirf_pwm {
+	void __iomem		*base;
+	struct clk		*clk;
+	struct pwm_chip		chip;
+	unsigned long		src_clk_rate;
+};
+
+#define to_sirf_chip(chip)	container_of(chip, struct sirf_pwm, chip)
+
+static unsigned int sirf_pwm_ns_to_cycles(struct pwm_chip *chip, unsigned int time_ns)
+{
+	struct sirf_pwm *spwm = to_sirf_chip(chip);
+	u64 dividend;
+	unsigned int cycle;
+
+	dividend = spwm->src_clk_rate * time_ns + NSEC_PER_SEC / 2;
+	do_div(dividend, NSEC_PER_SEC);
+
+	cycle = dividend & 0xFFFFFFFFUL;
+
+	return cycle > 1 ? cycle : 1;
+}
+
+static int sirf_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+		int duty_ns, int period_ns)
+{
+	unsigned int period_cycles, high_cycles, low_cycles;
+	unsigned int val;
+	struct sirf_pwm *spwm = to_sirf_chip(chip);
+
+	period_cycles = sirf_pwm_ns_to_cycles(chip, period_ns);
+
+	high_cycles = sirf_pwm_ns_to_cycles(chip, duty_ns);
+	low_cycles = period_cycles - high_cycles;
+
+	if (period_cycles == 1) {
+		/* bypass mode */
+		val = readl(spwm->base + SIRF_PWM_SELECT_PRECLK);
+		val |= 0x1 << (BYPASS_MODE_BIT + pwm->hwpwm);
+		writel(val, spwm->base + SIRF_PWM_SELECT_PRECLK);
+		dev_warn(chip->dev, "period is too short!\n");
+	} else {
+		/* divider mode */
+		val = readl(spwm->base + SIRF_PWM_SELECT_PRECLK);
+		val &= ~(0x1 << (BYPASS_MODE_BIT + pwm->hwpwm));
+		writel(val, spwm->base + SIRF_PWM_SELECT_PRECLK);
+
+		if (high_cycles == period_cycles) {
+			high_cycles--;
+			low_cycles = 1;
+		}
+
+		writel(high_cycles, spwm->base + SIRF_PWM_GET_WAIT_OFFSET(pwm->hwpwm));
+		writel(low_cycles, spwm->base + SIRF_PWM_GET_HOLD_OFFSET(pwm->hwpwm));
+	}
+
+	return 0;
+}
+
+static int sirf_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+	struct sirf_pwm *spwm = to_sirf_chip(chip);
+	unsigned int val;
+
+	/* disable preclock */
+	val = readl(spwm->base + SIRF_PWM_ENABLE_PRECLOCK);
+	val &= ~(1 << pwm->hwpwm);
+	writel(val, spwm->base + SIRF_PWM_ENABLE_PRECLOCK);
+
+	/* select preclock source must after disable preclk*/
+	val = readl(spwm->base + SIRF_PWM_SELECT_PRECLK);
+	val &= ~(0x7 << (SRC_FIELD_SIZE * pwm->hwpwm));
+	writel(val, spwm->base + SIRF_PWM_SELECT_PRECLK);
+	/* wait for some time */
+	udelay(100);
+
+	/* enable preclock */
+	val = readl(spwm->base + SIRF_PWM_ENABLE_PRECLOCK);
+	val |= (1 << pwm->hwpwm);
+	writel(val, spwm->base + SIRF_PWM_ENABLE_PRECLOCK);
+
+	/* enable post clock*/
+	val = readl(spwm->base + SIRF_PWM_ENABLE_POSTCLOCK);
+	val |= (1 << pwm->hwpwm);
+	writel(val, spwm->base + SIRF_PWM_ENABLE_POSTCLOCK);
+
+	/* enable output */
+	val = readl(spwm->base + SIRF_PWM_OE);
+	val |= 1 << pwm->hwpwm;
+	val &= ~(1 << (pwm->hwpwm + TRANS_MODE_SELECT_BIT));
+
+	writel(val, spwm->base + SIRF_PWM_OE);
+
+	return 0;
+}
+
+static void sirf_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+	unsigned int val;
+	struct sirf_pwm *spwm = to_sirf_chip(chip);
+	/* disable output */
+	val = readl(spwm->base + SIRF_PWM_OE);
+	val &= ~(1 << pwm->hwpwm);
+	writel(val, spwm->base + SIRF_PWM_OE);
+
+	/* disable postclock */
+	val = readl(spwm->base + SIRF_PWM_ENABLE_POSTCLOCK);
+	val &= ~(1 << pwm->hwpwm);
+	writel(val, spwm->base + SIRF_PWM_ENABLE_POSTCLOCK);
+
+	/* disable preclock */
+	val = readl(spwm->base + SIRF_PWM_ENABLE_PRECLOCK);
+	val &= ~(1 << pwm->hwpwm);
+	writel(val, spwm->base + SIRF_PWM_ENABLE_PRECLOCK);
+}
+
+static struct pwm_ops sirf_pwm_ops = {
+	.enable = sirf_pwm_enable,
+	.disable = sirf_pwm_disable,
+	.config = sirf_pwm_config,
+	.owner = THIS_MODULE,
+};
+
+static int sirf_pwm_probe(struct platform_device *pdev)
+{
+	struct sirf_pwm *spwm;
+	struct resource *mem_res;
+	struct clk *clk_pwm_src;
+	int ret;
+
+	spwm = devm_kzalloc(&pdev->dev, sizeof(struct sirf_pwm),
+			GFP_KERNEL);
+	if (!spwm)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, spwm);
+
+	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	spwm->base = devm_ioremap_resource(&pdev->dev, mem_res);
+	if (!spwm->base)
+		return -ENOMEM;
+
+	/*
+	 * the 1st clock is for PWM controller
+	 */
+	spwm->clk = of_clk_get(pdev->dev.of_node, 0);
+	if (IS_ERR(spwm->clk)) {
+		dev_err(&pdev->dev, "Get PWM controller clock failed.\n");
+		return PTR_ERR(spwm->clk);
+	}
+	clk_prepare_enable(spwm->clk);
+
+	/*
+	 * the 2nd clock is the source to generate PWM waves
+	 * it is the OSC on SiRFSoC
+	 */
+	clk_pwm_src = of_clk_get(pdev->dev.of_node, 1);
+	if (IS_ERR(clk_pwm_src)) {
+		dev_err(&pdev->dev, "Get PWM source clock failed.\n");
+		return PTR_ERR(clk_pwm_src);
+	}
+	spwm->src_clk_rate = clk_get_rate(clk_pwm_src);
+	clk_put(clk_pwm_src);
+
+	spwm->chip.dev = &pdev->dev;
+	spwm->chip.ops = &sirf_pwm_ops;
+	spwm->chip.base = 0;
+	spwm->chip.npwm = SIRF_PWM_CHL_NUM;
+
+	ret = pwmchip_add(&spwm->chip);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to register pwm\n");
+		clk_disable_unprepare(spwm->clk);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int sirf_pwm_remove(struct platform_device *pdev)
+{
+	struct sirf_pwm *spwm = platform_get_drvdata(pdev);
+	clk_disable_unprepare(spwm->clk);
+	clk_put(spwm->clk);
+
+	pwmchip_remove(&spwm->chip);
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int sirf_pwm_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct sirf_pwm *spwm = platform_get_drvdata(pdev);
+
+	clk_disable_unprepare(spwm->clk);
+
+	return 0;
+}
+
+static void sirf_pwm_config_restore(struct sirf_pwm *spwm)
+{
+	struct pwm_device *pwm;
+	int i;
+
+	for (i = 0; i < spwm->chip.npwm; i++) {
+		pwm = &spwm->chip.pwms[i];
+		/*
+		 * while restoring from hibernation, state of pwm is enabled,
+		 * but PWM hardware is not re-enabled
+		 */
+		if (test_bit(PWMF_REQUESTED, &pwm->flags) &&
+		     test_bit(PWMF_ENABLED, &pwm->flags))
+			sirf_pwm_enable(&spwm->chip, pwm);
+	}
+}
+
+static int sirf_pwm_resume(struct device *dev)
+{
+	struct sirf_pwm *spwm = dev_get_drvdata(dev);
+
+	clk_prepare_enable(spwm->clk);
+
+	sirf_pwm_config_restore(spwm);
+
+	return 0;
+}
+
+static int sirf_pwm_restore(struct device *dev)
+{
+	struct sirf_pwm *spwm = dev_get_drvdata(dev);
+
+	/* back from hibernation, clock is already enabled */
+	sirf_pwm_config_restore(spwm);
+
+	return 0;
+}
+
+#else
+#define sirf_pwm_resume NULL
+#define sirf_pwm_suspend NULL
+#define sirf_pwm_restore NULL
+#endif
+
+static const struct dev_pm_ops sirf_pwm_pm_ops = {
+	.suspend = sirf_pwm_suspend,
+	.resume = sirf_pwm_resume,
+	.restore = sirf_pwm_restore,
+};
+
+static const struct of_device_id sirf_pwm_of_match[] = {
+	{ .compatible = "sirf,prima2-pwm", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, sirf_pwm_of_match);
+
+static struct platform_driver sirf_pwm_driver = {
+	.driver = {
+		.name = "prima2-pwm",
+		.owner = THIS_MODULE,
+		.pm = &sirf_pwm_pm_ops,
+		.of_match_table = sirf_pwm_of_match,
+	},
+	.probe = sirf_pwm_probe,
+	.remove = sirf_pwm_remove,
+};
+
+module_platform_driver(sirf_pwm_driver);
+
+MODULE_DESCRIPTION("SIRF serial SoC PWM device core driver");
+MODULE_AUTHOR("RongJun Ying <Rongjun.Ying@csr.com>, "
+	"Huayi Li <huayi.li@csr.com>");
+MODULE_LICENSE("GPL v2");
-- 
1.7.5.4

^ permalink raw reply related

* [PATCH v2 2/2] Documentation: devicetree: Add boost-frequency binding to list boost mode frequency
From: Thomas Abraham @ 2014-02-08  6:47 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAGo_u6quxExJOyY8mvCw5sDSuz=_j0YqJ6FJW+FAdaxUeqzN9Q@mail.gmail.com>

On Sat, Feb 8, 2014 at 1:11 AM, Nishanth Menon <nm@ti.com> wrote:
> On Fri, Feb 7, 2014 at 12:02 PM, Sudeep Holla <Sudeep.Holla@arm.com> wrote:
>> On 07/02/14 17:37, Nishanth Menon wrote:
>>> On Fri, Feb 7, 2014 at 11:31 AM, Sudeep Holla <Sudeep.Holla@arm.com> wrote:
>>
>> [...]
>>
>>>> Yes I think its counter-intuitive as it's visible to the userspace(list of
>>>> frequencies and the boost parameters are exposed through sysfs)
>>>
>>> That will be a different problem -> as currently every single
>>> frequency in the cpufreq list has ability to be marked as boost
>>> frequency - if userspace does not maintain that, then, IMHO, fix the
>>> userspace :D
>>>
>>
>> /sys/devices/system/cpu/cpu*/cpufreq/scaling_available_frequencies gives
>> the list of frequencies based on the state of the boost feature at anytime.
>>
>> Intuitively the list without boost shouldn't have any frequency above the range
>> when it's enabled :), that's what I was referring to. So I am not talking about
>> any issue with user-space maintenance.
> Fair enough - but i still think it has nothing to do with dt binding
> itself -> and i think the discussion we've had should be good for the
> binding provided in this patch.. I hope.. if documentation needs a bit
> of better explanation to prevent a repeat of the same discussion at a
> later point of time, now might be a good time to add it in.

The term boost and over-clocking have been described in the bindings
document as being the same. Since the term over-clocking refers to
running a CPU beyond normal operating frequencies, I tend to agree
with Sudeep that it is not intuitive if a normal operating frequency
is greater than a boost mode frequency.

Otherwise, when userspace does "echo 1 >
/sys/devices/system/cpu/cpufreq/boost", what is it supposed to mean. I
think the original intent of boost mode patches was to allow CPU to
operate at frequencies greater than the normal operating frequencies.

Lukasz, how would you want this to be handled?

Thanks,
Thomas.

>
> Regards,
> Nishanth Menon

^ permalink raw reply

* [PATCH v2 2/2] Documentation: devicetree: Add boost-frequency binding to list boost mode frequency
From: Thomas Abraham @ 2014-02-08  6:47 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <52F51FCD.5000009@arm.com>

On Fri, Feb 7, 2014 at 11:32 PM, Sudeep Holla <Sudeep.Holla@arm.com> wrote:
> On 07/02/14 17:37, Nishanth Menon wrote:
>> On Fri, Feb 7, 2014 at 11:31 AM, Sudeep Holla <Sudeep.Holla@arm.com> wrote:
>
> [...]
>
>>> Yes I think its counter-intuitive as it's visible to the userspace(list of
>>> frequencies and the boost parameters are exposed through sysfs)
>>
>> That will be a different problem -> as currently every single
>> frequency in the cpufreq list has ability to be marked as boost
>> frequency - if userspace does not maintain that, then, IMHO, fix the
>> userspace :D
>>
>
> /sys/devices/system/cpu/cpu*/cpufreq/scaling_available_frequencies gives
> the list of frequencies based on the state of the boost feature at anytime.

The list of frequencies in
/sys/devices/system/cpu/cpu*/cpufreq/scaling_available_frequencies
does not change based in the state of the boost feature (enabled or
disabled). But the scaling_max_frequency and scaling_min_frequency are
updated based on the set of available + boost frequencies available.

>
> Intuitively the list without boost shouldn't have any frequency above the range
> when it's enabled :), that's what I was referring to. So I am not talking about
> any issue with user-space maintenance.
>
> Regards,
> Sudeep
>

^ permalink raw reply


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