* [PATCH V2 2/2] ARM: dts: OMAP2+: Add PMU nodes
From: Jon Hunter @ 2013-01-11 13:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <50F01256.7030701@ti.com>
Hi Benoit,
On 01/11/2013 07:23 AM, Benoit Cousson wrote:
> Hi Jon,
>
> On 12/17/2012 06:49 PM, Jon Hunter wrote:
>> Add PMU nodes for OMAP2, OMAP3 and OMAP4460 devices.
>>
>> Please note that the node for OMAP4460 has been placed in a separate
>> header file for OMAP4460, because the node is not compatible with
>> OMAP4430. The node for OMAP4430 is not included because PMU is not
>> currently supported on OMAP4430 due to the absence of a cross-trigger
>> interface driver.
>>
>> Signed-off-by: Jon Hunter <jon-hunter@ti.com>
>
> I've just applied this patch in my for_3.9/dts branch.
>
> I'm wondering if there is any dependency with the previous patch? If
> Tony ack it I can take it as well.
I have been thinking about the best way to handle that. May be best for
you to take both if Tony can ack the first.
Cheers
Jon
^ permalink raw reply
* [PATCHv2 11/11] Documentation: Add ARMv8 to arch_timer devicetree
From: Santosh Shilimkar @ 2013-01-11 13:52 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1357747640-18594-12-git-send-email-mark.rutland@arm.com>
On Wednesday 09 January 2013 09:37 PM, Mark Rutland wrote:
At least a line of change log will do :-)
> Signed-off-by: Mark Rutland <mark.rutland@arm.com>
> Acked-by: Catalin Marinas <catalin.marinas@arm.com>
> Acked-by: Marc Zyngier <marc.zyngier@arm.com>
> ---
Acked-by: Santosh Shilimkar<santosh.shilimkar@ti.com>
^ permalink raw reply
* [PATCHv2 10/11] arm64: move from arm_generic to arm_arch_timer
From: Santosh Shilimkar @ 2013-01-11 13:50 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1357747640-18594-11-git-send-email-mark.rutland@arm.com>
On Wednesday 09 January 2013 09:37 PM, Mark Rutland wrote:
> The arch_timer driver supports a superset of the functionality of the
> arm_generic driver, and is not tied to a particular arch.
>
> This patch moves arm64 to use the arch_timer driver, gaining additional
> functionality in doing so, and removes the (now unused) arm_generic
> driver. Timer-related hooks specific to arm64 are moved into
> arch/arm64/kernel/time.c.
>
> Signed-off-by: Mark Rutland <mark.rutland@arm.com>
> Acked-by: Catalin Marinas <catalin.marinas@arm.com>
> Acked-by: Marc Zyngier <marc.zyngier@arm.com>
> ---
Nice.
Acked-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
^ permalink raw reply
* [PATCHv2 09/11] arm: arch_timer: move core to drivers/clocksource
From: Santosh Shilimkar @ 2013-01-11 13:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1357747640-18594-10-git-send-email-mark.rutland@arm.com>
On Wednesday 09 January 2013 09:37 PM, Mark Rutland wrote:
> The core functionality of the arch_timer driver is not directly tied to
> anything under arch/arm, and can be split out.
>
> This patch factors out the core of the arch_timer driver, so it can be
> shared with other architectures. A couple of functions are added so
> that architecture-specific code can interact with the driver without
> needing to touch its internals.
>
> The ARM_ARCH_TIMER config variable is moved out to
> drivers/clocksource/Kconfig, existing uses in arch/arm are replaced with
> USE_ARM_ARCH_TIMER, which selects it.
>
> Signed-off-by: Mark Rutland <mark.rutland@arm.com>
> Acked-by: Catalin Marinas <catalin.marinas@arm.com>
> Acked-by: Marc Zyngier <marc.zyngier@arm.com>
> ---
> arch/arm/Kconfig | 3 +-
> arch/arm/include/asm/arch_timer.h | 19 +--
> arch/arm/kernel/arch_timer.c | 375 ++--------------------------------
> arch/arm/mach-omap2/Kconfig | 2 +-
> drivers/clocksource/Kconfig | 3 +
> drivers/clocksource/Makefile | 1 +
> drivers/clocksource/arm_arch_timer.c | 374 +++++++++++++++++++++++++++++++++
> include/clocksource/arm_arch_timer.h | 63 ++++++
> 8 files changed, 465 insertions(+), 375 deletions(-)
> create mode 100644 drivers/clocksource/arm_arch_timer.c
> create mode 100644 include/clocksource/arm_arch_timer.h
>
It would have been easy if you have formated the patch with -C option.
That will just leave the delta changes only and hiding the file
movement related diff.
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index f95ba14..487696a 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -1567,9 +1567,10 @@ config HAVE_ARM_SCU
> help
> This option enables support for the ARM system coherency unit
>
> -config ARM_ARCH_TIMER
> +config USE_ARM_ARCH_TIMER
> bool "Architected timer support"
> depends on CPU_V7
> + select ARM_ARCH_TIMER
> help
> This option enables support for the ARM architected timer
>
How about HAVE_ARM_ARCH_TIMER in-line with HAVE_ARM_TWD. No strong
opinion though.
Regards
Santosh
^ permalink raw reply
* [GIT PULL] clockevent driver cleanup for 3.9
From: Shawn Guo @ 2013-01-11 13:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20130110124132.GA8680@S2101-09.ap.freescale.net>
Arnd, Olof,
On Thu, Jan 10, 2013 at 08:41:35PM +0800, Shawn Guo wrote:
> The following changes since commit a49f0d1ea3ec94fc7cf33a7c36a16343b74bd565:
>
> Linux 3.8-rc1 (2012-12-21 17:19:00 -0800)
>
> are available in the git repository at:
>
> git://git.linaro.org/people/shawnguo/linux-2.6.git tags/clean-clkevt-3.9
>
> for you to fetch changes up to ab8e73c98749a58c242b187c9799db70cd903157:
>
> clocksource: use clockevents_config_and_register() where possible (2013-01-10 20:24:30 +0800)
>
Please ignore this pull request. I will refresh it to fix the error [1]
reported by Fengguang.
Shawn
[1] http://thread.gmane.org/gmane.linux.ports.arm.kernel/209152
> ----------------------------------------------------------------
> As John has showed zero interest to collect clock event driver patches,
> I send both patches through arm-soc tree here.
>
> ----------------------------------------------------------------
> Shawn Guo (2):
> ARM: use clockevents_config_and_register() where possible
> clocksource: use clockevents_config_and_register() where possible
^ permalink raw reply
* [PATCHv2 08/11] arm: arch_timer: add arch_counter_set_user_access
From: Santosh Shilimkar @ 2013-01-11 13:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1357747640-18594-9-git-send-email-mark.rutland@arm.com>
On Wednesday 09 January 2013 09:37 PM, Mark Rutland wrote:
> Several bits in CNTKCTL reset to 0, including PL0VTEN. For platforms
> using the generic timer which wish to have a fast gettimeofday vDSO
> implementation, these bits must be set to 1 by the kernel. On other
> platforms, the bootloader might enable userspace access when we don't
> want it.
>
> This patch adds arch_counter_set_user_access, which sets the PL0 access
> permissions to that required by the platform. For arm, this currently
minor nit.
s/arm/ARM
> means disabling all userspace access.
>
> Signed-off-by: Mark Rutland <mark.rutland@arm.com>
> ---
> arch/arm/include/asm/arch_timer.h | 11 +++++++++++
> arch/arm/kernel/arch_timer.c | 2 ++
> 2 files changed, 13 insertions(+), 0 deletions(-)
>
> diff --git a/arch/arm/include/asm/arch_timer.h b/arch/arm/include/asm/arch_timer.h
> index 701f2b7..05e3593 100644
> --- a/arch/arm/include/asm/arch_timer.h
> +++ b/arch/arm/include/asm/arch_timer.h
> @@ -108,6 +108,17 @@ static inline u64 arch_counter_get_cntvct(void)
> return cval;
> }
>
> +static inline void __cpuinit arch_counter_set_user_access(void)
> +{
> + u32 cntkctl;
> +
> + asm volatile("mrc p15, 0, %0, c14, c1, 0" : "=r" (cntkctl));
> +
> + /* disable user access to everything */
> + cntkctl &= ~((3 << 8) | (7 << 0));
> +
> + asm volatile("mcr p15, 0, %0, c14, c1, 0" : : "r" (cntkctl));
> +}
>
> #else
> static inline int arch_timer_of_register(void)
> diff --git a/arch/arm/kernel/arch_timer.c b/arch/arm/kernel/arch_timer.c
> index 834d347..4f39e68 100644
> --- a/arch/arm/kernel/arch_timer.c
> +++ b/arch/arm/kernel/arch_timer.c
> @@ -155,6 +155,8 @@ static int __cpuinit arch_timer_setup(struct clock_event_device *clk)
> enable_percpu_irq(arch_timer_ppi[PHYS_NONSECURE_PPI], 0);
> }
>
> + arch_counter_set_user_access();
So how do you expect platform to enabled the user-space access in case
they want to access it for some cases.
Regards
Santosh
^ permalink raw reply
* [shawnguo:cleanup/clockevents 2/2] ERROR: "clockevents_config_and_register" [drivers/clocksource/cs5535-clockevt.ko] undefined!
From: Shawn Guo @ 2013-01-11 13:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20130111122424.GB23972@localhost>
On Fri, Jan 11, 2013 at 08:24:24PM +0800, Fengguang Wu wrote:
>
> Hi Shawn,
>
> FYI, kernel build failed on
>
> tree: git://git.linaro.org/people/shawnguo/linux-2.6.git cleanup/clockevents
> head: ab8e73c98749a58c242b187c9799db70cd903157
> commit: ab8e73c98749a58c242b187c9799db70cd903157 [2/2] clocksource: use clockevents_config_and_register() where possible
> config: i386-randconfig-b030 (attached as .config)
>
> All error/warnings:
>
> >> ERROR: "clockevents_config_and_register" [drivers/clocksource/cs5535-clockevt.ko] undefined!
>
I will propose a patch to export clockevents_config_and_register() to
fix the error.
Thanks Fengguang.
Shawn
^ permalink raw reply
* [PATCHv2 07/11] arm: arch_timer: divorce from local_timer api
From: Santosh Shilimkar @ 2013-01-11 13:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1357747640-18594-8-git-send-email-mark.rutland@arm.com>
On Wednesday 09 January 2013 09:37 PM, Mark Rutland wrote:
> Currently, the arch_timer driver is tied to the arm port, as it relies
> on code in arch/arm/smp.c to setup and teardown timers as cores are
> hotplugged on and off. The timer is registered through an arm-specific
> registration mechanism, preventing sharing the driver with the arm64
> port.
>
> This patch moves the driver to using a cpu notifier instead, making it
> easier to port.
>
> Signed-off-by: Mark Rutland <mark.rutland@arm.com>
> Acked-by: Catalin Marinas <catalin.marinas@arm.com>
> Acked-by: Marc Zyngier <marc.zyngier@arm.com>
> ---
This is really a nit idea. I think we should do the same
for ARM gic code.
For the patch,
Acked-by : Santosh Shilimkar<santosh.shilimkar@ti.com>
^ permalink raw reply
* [PATCH 1/9] mailbox: OMAP: introduce mailbox framework
From: Omar Ramirez Luna @ 2013-01-11 13:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <50ED62B3.50000@st.com>
On Wed, Jan 9, 2013 at 6:29 AM, Loic PALLARDY <loic.pallardy@st.com> wrote:
> Hi Vaibhav,
>
> On 01/09/2013 01:11 PM, Bedia, Vaibhav wrote:
>> Hi Loic,
>>
>> On Fri, Dec 21, 2012 at 16:23:24, Loic PALLARDY wrote:
>>>
>>>
>>> On 12/21/2012 11:49 AM, Bedia, Vaibhav wrote:
>>>> On Fri, Dec 21, 2012 at 14:24:26, Loic PALLARDY wrote:
>>>>>
>>>> I have a few patches which are dependent on this patch series.
>>>> Could you please keep me in cc for the future versions.
>>>>
>>> Sure, I'll.
>>>
>>
>> When do you plan to post an updated version of these patches?
> I'm synchronizing with Omar to include TI RPMsg and tidspbridge patches
> in next update.
> So I plan update for end of the week, beginning of next week.
Here are my patches, I didn't post them to the list since they are
meant to be squashed, I also prepared a squashed version of the
original set, in case it is easier to take that one, all rebased into
3.8-rc1. Branches: mailbox-3.8-rc1-separate-changes and
mailbox-3.8-rc1, respectively.
>From tree: https://github.com/omarrmz/upstream-wip
Cheers,
Omar
^ permalink raw reply
* [PATCH RFT 3/3] ARM: tegra: dts: seaboard: enable keyboard
From: Laxman Dewangan @ 2013-01-11 13:33 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1357911185-11048-1-git-send-email-ldewangan@nvidia.com>
Enable tegra based keyboard controller and populate the key matrix for
seaboard. The key matrix was originally on driver code which is removed
to have clean driver. The key mapping is now passed through dts file.
Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
---
Requesting for testing on seaboard.
I generated this patch as Stephen suggested to have one patch for dt file
entry for keys on seaboard.
arch/arm/boot/dts/tegra20-seaboard.dts | 138 ++++++++++++++++++++++++++++++++
1 files changed, 138 insertions(+), 0 deletions(-)
diff --git a/arch/arm/boot/dts/tegra20-seaboard.dts b/arch/arm/boot/dts/tegra20-seaboard.dts
index f9e3ad4..b22522d 100644
--- a/arch/arm/boot/dts/tegra20-seaboard.dts
+++ b/arch/arm/boot/dts/tegra20-seaboard.dts
@@ -612,6 +612,144 @@
};
};
+ kbc {
+ status = "okay";
+ nvidia,debounce-delay-ms = <640>;
+ nvidia,repeat-delay-ms = <1>;
+ nvidia,kbc-row-pins = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15>;
+ nvidia,kbc-col-pins = <16 17 18 19 20 21 22 23>;
+ linux,keymap = <0x00020011 /* KEY_W */
+ 0x0003001F /* KEY_S */
+ 0x0004001E /* KEY_A */
+ 0x0005002C /* KEY_Z */
+ 0x000701d0 /* KEY_FN */
+
+ 0x0107007D /* KEY_LEFTMETA */
+ 0x02060064 /* KEY_RIGHTALT */
+ 0x02070038 /* KEY_LEFTALT */
+
+ 0x03000006 /* KEY_5 */
+ 0x03010005 /* KEY_4 */
+ 0x03020013 /* KEY_R */
+ 0x03030012 /* KEY_E */
+ 0x03040021 /* KEY_F */
+ 0x03050020 /* KEY_D */
+ 0x0306002D /* KEY_X */
+
+ 0x04000008 /* KEY_7 */
+ 0x04010007 /* KEY_6 */
+ 0x04020014 /* KEY_T */
+ 0x04030023 /* KEY_H */
+ 0x04040022 /* KEY_G */
+ 0x0405002F /* KEY_V */
+ 0x0406002E /* KEY_C */
+ 0x04070039 /* KEY_SPACE */
+
+ 0x0500000A /* KEY_9 */
+ 0x05010009 /* KEY_8 */
+ 0x05020016 /* KEY_U */
+ 0x05030015 /* KEY_Y */
+ 0x05040024 /* KEY_J */
+ 0x05050031 /* KEY_N */
+ 0x05060030 /* KEY_B */
+ 0x0507002B /* KEY_BACKSLASH */
+
+ 0x0600000C /* KEY_MINUS */
+ 0x0601000B /* KEY_0 */
+ 0x06020018 /* KEY_O */
+ 0x06030017 /* KEY_I */
+ 0x06040026 /* KEY_L */
+ 0x06050025 /* KEY_K */
+ 0x06060033 /* KEY_COMMA */
+ 0x06070032 /* KEY_M */
+
+ 0x0701000D /* KEY_EQUAL */
+ 0x0702001B /* KEY_RIGHTBRACE */
+ 0x0703001C /* KEY_ENTER */
+ 0x0707008B /* KEY_MENU */
+
+ 0x08040036 /* KEY_RIGHTSHIFT */
+ 0x0805002A /* KEY_LEFTSHIFT */
+
+ 0x09050061 /* KEY_RIGHTCTRL */
+ 0x0907001B /* KEY_LEFTCTRL */
+
+ 0x0B00001A /* KEY_LEFTBRACE */
+ 0x0B010019 /* KEY_P */
+ 0x0B020028 /* KEY_APOSTROPHE */
+ 0x0B030027 /* KEY_SEMICOLON */
+ 0x0B040035 /* KEY_SLASH */
+ 0x0B050034 /* KEY_DOT */
+
+ 0x0C000044 /* KEY_F10 */
+ 0x0C010043 /* KEY_F9 */
+ 0x0C02000E /* KEY_BACKSPACE */
+ 0x0C030004 /* KEY_3 */
+ 0x0C040003 /* KEY_2 */
+ 0x0C050067 /* KEY_UP */
+ 0x0C0600D2 /* KEY_PRINT */
+ 0x0C070077 /* KEY_PAUSE */
+
+ 0x0D00006E /* KEY_INSERT */
+ 0x0D01006F /* KEY_DELETE */
+ 0x0D030068 /* KEY_PAGEUP */
+ 0x0D04006D /* KEY_PAGEDOWN */
+ 0x0D05006A /* KEY_RIGHT */
+ 0x0D06006C /* KEY_DOWN */
+ 0x0D070069 /* KEY_LEFT */
+
+ 0x0E000057 /* KEY_F11 */
+ 0x0E010058 /* KEY_F12 */
+ 0x0E020042 /* KEY_F8 */
+ 0x0E030010 /* KEY_Q */
+ 0x0E04003E /* KEY_F4 */
+ 0x0E05003D /* KEY_F3 */
+ 0x0E060002 /* KEY_1 */1
+ 0x0E070041 /* KEY_F7 */
+
+ 0x0F000001 /* KEY_ESC */
+ 0x0F010029 /* KEY_GRAVE */
+ 0x0F02003F /* KEY_F5 */
+ 0x0F03000F /* KEY_TAB */
+ 0x0F04003B /* KEY_F1 */
+ 0x0F05003C /* KEY_F2 */
+ 0x0F06003A /* KEY_CAPSLOCK */
+ 0x0F070040 /* KEY_F6 */
+
+ /* Software Handled Function Keys */
+ 0x14000047 /* KEY_KP7 */
+
+ 0x15000049 /* KEY_KP9 */
+ 0x15010048 /* KEY_KP8 */
+ 0x1502004B /* KEY_KP4 */
+ 0x1504004F /* KEY_KP1 */
+
+ 0x1601004E /* KEY_KPSLASH */
+ 0x1602004D /* KEY_KP6 */
+ 0x1603004C /* KEY_KP5 */
+ 0x16040051 /* KEY_KP3 */
+ 0x16050050 /* KEY_KP2 */
+ 0x16070052 /* KEY_KP0 */
+
+ 0x1B010037 /* KEY_KPASTERISK */
+ 0x1B03004A /* KEY_KPMINUS */
+ 0x1B04004E /* KEY_KPPLUS */
+ 0x1B050053 /* KEY_KPDOT */
+
+ 0x1C050073 /* KEY_VOLUMEUP */
+
+ 0x1D030066 /* KEY_HOME */
+ 0x1D04006B /* KEY_END */
+ 0x1D0500E0 /* KEY_BRIGHTNESSDOWN */
+ 0x1D060072 /* KEY_VOLUMEDOWN */
+ 0x1D0700E1 /* KEY_BRIGHTNESSUP */
+
+ 0x1E000045 /* KEY_NUMLOCK */
+ 0x1E010046 /* KEY_SCROLLLOCK */
+ 0x1E020071 /* KEY_MUTE */
+
+ 0x1F04008A>; /* KEY_HELP */
+ };
regulators {
compatible = "simple-bus";
#address-cells = <1>;
--
1.7.1.1
^ permalink raw reply related
* [PATCH 2/3] ARM: tegra: config: enable KEYBOARD_TEGRA
From: Laxman Dewangan @ 2013-01-11 13:33 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1357911185-11048-1-git-send-email-ldewangan@nvidia.com>
Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
---
arch/arm/configs/tegra_defconfig | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/arch/arm/configs/tegra_defconfig b/arch/arm/configs/tegra_defconfig
index 254f31f..e350afb 100644
--- a/arch/arm/configs/tegra_defconfig
+++ b/arch/arm/configs/tegra_defconfig
@@ -107,6 +107,7 @@ CONFIG_BRCMFMAC=m
CONFIG_RT2X00=y
CONFIG_RT2800USB=m
CONFIG_INPUT_EVDEV=y
+CONFIG_KEYBOARD_TEGRA=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_MPU3050=y
# CONFIG_LEGACY_PTYS is not set
--
1.7.1.1
^ permalink raw reply related
* [PATCH 1/3] ARM: DT: tegra: add DT entry for KBC controller
From: Laxman Dewangan @ 2013-01-11 13:33 UTC (permalink / raw)
To: linux-arm-kernel
NVIDIA's Tegra SoCs have the matrix keyboard controller which
supports 16x8 type of matrix. The number of rows and columns
are configurable.
Add DT entry for KBC controller.
Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
---
arch/arm/boot/dts/tegra20.dtsi | 7 +++++++
arch/arm/boot/dts/tegra30.dtsi | 7 +++++++
2 files changed, 14 insertions(+), 0 deletions(-)
diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi
index fe35c72..133d00d 100644
--- a/arch/arm/boot/dts/tegra20.dtsi
+++ b/arch/arm/boot/dts/tegra20.dtsi
@@ -352,6 +352,13 @@
status = "disabled";
};
+ kbc {
+ compatible = "nvidia,tegra20-kbc";
+ reg = <0x7000e200 0x100>;
+ interrupts = <0 85 0x04>;
+ status = "disabled";
+ };
+
pmc {
compatible = "nvidia,tegra20-pmc";
reg = <0x7000e400 0x400>;
diff --git a/arch/arm/boot/dts/tegra30.dtsi b/arch/arm/boot/dts/tegra30.dtsi
index f534a50..d060e46 100644
--- a/arch/arm/boot/dts/tegra30.dtsi
+++ b/arch/arm/boot/dts/tegra30.dtsi
@@ -368,6 +368,13 @@
status = "disabled";
};
+ kbc {
+ compatible = "nvidia,tegra30-kbc", "nvidia,tegra20-kbc";
+ reg = <0x7000e200 0x100>;
+ interrupts = <0 85 0x04>;
+ status = "disabled";
+ };
+
pmc {
compatible = "nvidia,tegra20-pmc", "nvidia,tegra30-pmc";
reg = <0x7000e400 0x400>;
--
1.7.1.1
^ permalink raw reply related
* [PATCHv2 06/11] arm: arch_timer: factor out register accessors
From: Santosh Shilimkar @ 2013-01-11 13:32 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1357747640-18594-7-git-send-email-mark.rutland@arm.com>
On Wednesday 09 January 2013 09:37 PM, Mark Rutland wrote:
> Currently the arch_timer register accessors are thrown together with
> the main driver, preventing us from porting the driver to other
> architectures.
>
> This patch moves the register accessors into a header file, as with
> the arm64 version. Constants required by the accessors are also moved.
>
> Additionally isbs are added in arch_timer_get_cnt{v,p}ct to prevent
> the cpu from speculating the reads and returning stale values.
>
> Signed-off-by: Mark Rutland <mark.rutland@arm.com>
> Acked-by: Catalin Marinas <catalin.marinas@arm.com>
> Acked-by: Marc Zyngier <marc.zyngier@arm.com>
> ---
> arch/arm/include/asm/arch_timer.h | 101 +++++++++++++++++++++++++++++++++++++
> arch/arm/kernel/arch_timer.c | 92 ---------------------------------
> 2 files changed, 101 insertions(+), 92 deletions(-)
>
> diff --git a/arch/arm/include/asm/arch_timer.h b/arch/arm/include/asm/arch_timer.h
> index d40229d..701f2b7 100644
> --- a/arch/arm/include/asm/arch_timer.h
> +++ b/arch/arm/include/asm/arch_timer.h
> @@ -1,13 +1,114 @@
> #ifndef __ASMARM_ARCH_TIMER_H
> #define __ASMARM_ARCH_TIMER_H
>
> +#include <asm/barrier.h>
> #include <asm/errno.h>
> +
> #include <linux/clocksource.h>
> +#include <linux/types.h>
>
> #ifdef CONFIG_ARM_ARCH_TIMER
> int arch_timer_of_register(void);
> int arch_timer_sched_clock_init(void);
> struct timecounter *arch_timer_get_timecounter(void);
> +
> +#define ARCH_TIMER_CTRL_ENABLE (1 << 0)
> +#define ARCH_TIMER_CTRL_IT_MASK (1 << 1)
> +#define ARCH_TIMER_CTRL_IT_STAT (1 << 2)
> +
> +#define ARCH_TIMER_REG_CTRL 0
> +#define ARCH_TIMER_REG_TVAL 1
> +
> +#define ARCH_TIMER_PHYS_ACCESS 0
> +#define ARCH_TIMER_VIRT_ACCESS 1
> +
> +/*
> + * These register accessors are marked inline so the compiler can
> + * nicely work out which register we want, and chuck away the rest of
> + * the code. At least it does so with a recent GCC (4.6.3).
> + */
> +static inline void arch_timer_reg_write(const int access, const int reg, u32 val)
> +{
> + if (access == ARCH_TIMER_PHYS_ACCESS) {
> + switch (reg) {
> + case ARCH_TIMER_REG_CTRL:
> + asm volatile("mcr p15, 0, %0, c14, c2, 1" : : "r" (val));
> + break;
> + case ARCH_TIMER_REG_TVAL:
> + asm volatile("mcr p15, 0, %0, c14, c2, 0" : : "r" (val));
> + break;
> + }
> + }
> +
> + if (access == ARCH_TIMER_VIRT_ACCESS) {
> + switch (reg) {
> + case ARCH_TIMER_REG_CTRL:
> + asm volatile("mcr p15, 0, %0, c14, c3, 1" : : "r" (val));
> + break;
> + case ARCH_TIMER_REG_TVAL:
> + asm volatile("mcr p15, 0, %0, c14, c3, 0" : : "r" (val));
> + break;
> + }
> + }
> +
> + isb();
> +}
The isb() additions is actually a sepoerate fix. I suggest you to split
the subject patch into 1) Movement of header data 2) isb() additions.
Feel free to add my ack on updated patches if you agree.
Regards,
Santosh
^ permalink raw reply
* i.MX6 kernel uncompress: how to output uncompress messages?
From: Shawn Guo @ 2013-01-11 13:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <50F00C6B.5000105@de.bosch.com>
Dirk,
On Fri, Jan 11, 2013 at 01:58:19PM +0100, Dirk Behme wrote:
> Hi,
>
> using an older, non-multiplatform kernel which still uses
>
> arch/arm/mach-imx/include/mach/uncompress.h
>
> I wonder how the uncompress is supposed to set the 'uart_base' for i.MX6?
>
> Looking in
>
> http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=blob;f=arch/arm/mach-imx/include/mach/uncompress.h;h=477971b009308a929c53c19ce40c4b35c425d623;hb=69ac71d370b21cc52a2afd06f3a6d6d1da5edc75#l70
>
> there doesn't seem to be any i.MX6 related case part?
>
> This results in no
>
> Uncompressing Linux... done, booting the kernle.
>
> messages output on i.MX6. And with this the associated error
> messages in case anything goes wrong with the uncompression aren't
> output, either.
>
> Adding a hack like
>
> uart_base = MX6Q_UART4_BASE_ADDR;
>
> outputs these uncompression messages.
>
> What's the supposed clean way to do this?
>
Please check this [1] out. I plan send it for 3.9.
Shawn
[1] http://thread.gmane.org/gmane.linux.ports.arm.kernel/205000
^ permalink raw reply
* [PATCHv2 05/11] arm: arch_timer: split cntfrq accessor
From: Santosh Shilimkar @ 2013-01-11 13:27 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1357747640-18594-6-git-send-email-mark.rutland@arm.com>
On Wednesday 09 January 2013 09:37 PM, Mark Rutland wrote:
> The CNTFRQ register is not duplicated for physical and virtual timers,
> and accessing it as if it were is confusing.
>
> Instead, use a separate accessor which doesn't take the access type
> as a parameter.
>
> Signed-off-by: Mark Rutland <mark.rutland@arm.com>
> Acked-by: Catalin Marinas <catalin.marinas@arm.com>
> Acked-by: Marc Zyngier <marc.zyngier@arm.com>
> ---
> arch/arm/kernel/arch_timer.c | 17 +++++++++--------
> 1 files changed, 9 insertions(+), 8 deletions(-)
>
> diff --git a/arch/arm/kernel/arch_timer.c b/arch/arm/kernel/arch_timer.c
> index 0d2681c..fc87d3d 100644
> --- a/arch/arm/kernel/arch_timer.c
> +++ b/arch/arm/kernel/arch_timer.c
> @@ -51,8 +51,7 @@ static bool arch_timer_use_virtual = true;
> #define ARCH_TIMER_CTRL_IT_STAT (1 << 2)
>
> #define ARCH_TIMER_REG_CTRL 0
> -#define ARCH_TIMER_REG_FREQ 1
> -#define ARCH_TIMER_REG_TVAL 2
> +#define ARCH_TIMER_REG_TVAL 1
>
> #define ARCH_TIMER_PHYS_ACCESS 0
> #define ARCH_TIMER_VIRT_ACCESS 1
> @@ -101,9 +100,6 @@ static inline u32 arch_timer_reg_read(const int access, const int reg)
> case ARCH_TIMER_REG_TVAL:
> asm volatile("mrc p15, 0, %0, c14, c2, 0" : "=r" (val));
> break;
> - case ARCH_TIMER_REG_FREQ:
> - asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (val));
> - break;
> }
> }
>
> @@ -121,6 +117,13 @@ static inline u32 arch_timer_reg_read(const int access, const int reg)
> return val;
> }
>
> +static inline u32 arch_timer_get_cntfrq(void)
> +{
> + u32 val;
> + asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (val));
> + return val;
> +}
> +
> static inline u64 arch_counter_get_cntpct(void)
> {
> u64 cval;
> @@ -253,9 +256,7 @@ static int arch_timer_available(void)
> u32 freq;
>
> if (arch_timer_rate == 0) {
> - freq = arch_timer_reg_read(ARCH_TIMER_PHYS_ACCESS,
> - ARCH_TIMER_REG_FREQ);
> -
> + freq = arch_timer_get_cntfrq();
Not related to this patch a new line here will be good.
> /* Check the timer frequency. */
> if (freq == 0) {
> pr_warn("Architected timer frequency not available\n");
>
Otherwise patch looks fine to me.
Acked-by: Santosh Shilimkar<santosh.shilimkar@ti.com>
^ permalink raw reply
* [PATCH 1'/2] ARM: tegra20: Add cpu node
From: Hiroshi Doyu @ 2013-01-11 13:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1357909914-28994-2-git-send-email-hdoyu@nvidia.com>
Add cpu node for tegra20.
Signed-off-by: Hiroshi Doyu <hdoyu@nvidia.com>
---
Update: To be honest, T20 doesn't have four cores but two:)
---
arch/arm/boot/dts/tegra20.dtsi | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi
index 6e13d13..10dfb83 100644
--- a/arch/arm/boot/dts/tegra20.dtsi
+++ b/arch/arm/boot/dts/tegra20.dtsi
@@ -430,6 +430,23 @@
status = "disabled";
};
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu at 0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a9";
+ reg = <0>;
+ };
+
+ cpu at 1 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a9";
+ reg = <1>;
+ };
+ };
+
pmu {
compatible = "arm,cortex-a9-pmu";
interrupts = <0 56 0x04
--
1.7.9.5
^ permalink raw reply related
* [PATCH V2 2/2] ARM: dts: OMAP2+: Add PMU nodes
From: Benoit Cousson @ 2013-01-11 13:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1355766548-5702-3-git-send-email-jon-hunter@ti.com>
Hi Jon,
On 12/17/2012 06:49 PM, Jon Hunter wrote:
> Add PMU nodes for OMAP2, OMAP3 and OMAP4460 devices.
>
> Please note that the node for OMAP4460 has been placed in a separate
> header file for OMAP4460, because the node is not compatible with
> OMAP4430. The node for OMAP4430 is not included because PMU is not
> currently supported on OMAP4430 due to the absence of a cross-trigger
> interface driver.
>
> Signed-off-by: Jon Hunter <jon-hunter@ti.com>
I've just applied this patch in my for_3.9/dts branch.
I'm wondering if there is any dependency with the previous patch? If
Tony ack it I can take it as well.
Regards,
Benoit
> ---
> arch/arm/boot/dts/omap2.dtsi | 5 +++++
> arch/arm/boot/dts/omap3.dtsi | 6 ++++++
> arch/arm/boot/dts/omap4-panda-es.dts | 2 ++
> arch/arm/boot/dts/omap4460.dtsi | 18 ++++++++++++++++++
> 4 files changed, 31 insertions(+)
> create mode 100644 arch/arm/boot/dts/omap4460.dtsi
>
> diff --git a/arch/arm/boot/dts/omap2.dtsi b/arch/arm/boot/dts/omap2.dtsi
> index 761c4b6..27f5ea1 100644
> --- a/arch/arm/boot/dts/omap2.dtsi
> +++ b/arch/arm/boot/dts/omap2.dtsi
> @@ -26,6 +26,11 @@
> };
> };
>
> + pmu {
> + compatible = "arm,arm1136-pmu";
> + interrupts = <3>;
> + };
> +
> soc {
> compatible = "ti,omap-infra";
> mpu {
> diff --git a/arch/arm/boot/dts/omap3.dtsi b/arch/arm/boot/dts/omap3.dtsi
> index 1acc261..6c63118 100644
> --- a/arch/arm/boot/dts/omap3.dtsi
> +++ b/arch/arm/boot/dts/omap3.dtsi
> @@ -26,6 +26,12 @@
> };
> };
>
> + pmu {
> + compatible = "arm,cortex-a8-pmu";
> + interrupts = <3>;
> + ti,hwmods = "debugss";
> + };
> +
> /*
> * The soc node represents the soc top level view. It is uses for IPs
> * that are not memory mapped in the MPU view or for the MPU itself.
> diff --git a/arch/arm/boot/dts/omap4-panda-es.dts b/arch/arm/boot/dts/omap4-panda-es.dts
> index 73bc1a6..2a6e344 100644
> --- a/arch/arm/boot/dts/omap4-panda-es.dts
> +++ b/arch/arm/boot/dts/omap4-panda-es.dts
> @@ -5,7 +5,9 @@
> * it under the terms of the GNU General Public License version 2 as
> * published by the Free Software Foundation.
> */
> +
> /include/ "omap4-panda.dts"
> +/include/ "omap4460.dtsi"
>
> /* Audio routing is differnet between PandaBoard4430 and PandaBoardES */
> &sound {
> diff --git a/arch/arm/boot/dts/omap4460.dtsi b/arch/arm/boot/dts/omap4460.dtsi
> new file mode 100644
> index 0000000..0a1d38b
> --- /dev/null
> +++ b/arch/arm/boot/dts/omap4460.dtsi
> @@ -0,0 +1,18 @@
> +/*
> + * Device Tree Source for OMAP4460 SoC
> + *
> + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
> + *
> + * This file is licensed under the terms of the GNU General Public License
> + * version 2. This program is licensed "as is" without any warranty of any
> + * kind, whether express or implied.
> + */
> +
> +/ {
> + pmu {
> + compatible = "arm,cortex-a9-pmu";
> + interrupts = <0 54 0x4>,
> + <0 55 0x4>;
> + ti,hwmods = "debugss";
> + };
> +};
>
^ permalink raw reply
* [PATCHv2 04/11] arm: arch_timer: standardise counter reading
From: Santosh Shilimkar @ 2013-01-11 13:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1357747640-18594-5-git-send-email-mark.rutland@arm.com>
On Wednesday 09 January 2013 09:37 PM, Mark Rutland wrote:
> We're currently inconsistent with respect to our accesses to the
> physical and virtual counters, mixing and matching the two.
>
> This patch introduces and uses a function for accessing the correct
> counter based on whether we're using physical or virtual interrupts.
> All current accesses to the counter accessors are redirected through
> it.
>
> Signed-off-by: Mark Rutland <mark.rutland@arm.com>
> Acked-by: Catalin Marinas <catalin.marinas@arm.com>
> Acked-by: Marc Zyngier <marc.zyngier@arm.com>
> ---
> arch/arm/kernel/arch_timer.c | 48 ++++++++++-------------------------------
> 1 files changed, 12 insertions(+), 36 deletions(-)
>
> diff --git a/arch/arm/kernel/arch_timer.c b/arch/arm/kernel/arch_timer.c
> index 498c29f..0d2681c 100644
> --- a/arch/arm/kernel/arch_timer.c
> +++ b/arch/arm/kernel/arch_timer.c
> @@ -272,51 +272,32 @@ static int arch_timer_available(void)
> return 0;
> }
>
> -static u32 notrace arch_counter_get_cntpct32(void)
> +u64 arch_timer_read_counter(void)
> {
> - cycle_t cnt = arch_counter_get_cntpct();
> -
> - /*
> - * The sched_clock infrastructure only knows about counters
> - * with at most 32bits. Forget about the upper 24 bits for the
> - * time being...
> - */
> - return (u32)cnt;
> + if (arch_timer_use_virtual)
> + return arch_counter_get_cntvct();
> + else
> + return arch_counter_get_cntpct();
> }
>
[...]
> @@ -489,18 +470,13 @@ int __init arch_timer_of_register(void)
>
> int __init arch_timer_sched_clock_init(void)
> {
> - u32 (*cnt32)(void);
> int err;
>
> err = arch_timer_available();
> if (err)
> return err;
>
> - if (arch_timer_use_virtual)
> - cnt32 = arch_counter_get_cntvct32;
> - else
> - cnt32 = arch_counter_get_cntpct32;
> -
> - setup_sched_clock(cnt32, 32, arch_timer_rate);
> + setup_sched_clock(arch_timer_read_counter32,
> + 32, arch_timer_rate);
> return 0;
> }
>
I think the original idea had merit since the check was needed
in init code instead of proposed one which has if check for
every counter read function. No ?
^ permalink raw reply
* [PATCHv2 03/11] arm: arch_timer: use u64/u32 for register data
From: Santosh Shilimkar @ 2013-01-11 13:19 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1357747640-18594-4-git-send-email-mark.rutland@arm.com>
On Wednesday 09 January 2013 09:37 PM, Mark Rutland wrote:
> To ensure the correct size of types, use u64 for the return value of
> arch_timer_get_cnt{p,v}ct, and u32 for arch_timer_rate, matching the
> size of the registers these values are taken from. While we're changing
> them anyway, simplify the implementation of arch_timer_get_cnt{p,v}ct.
>
> Signed-off-by: Mark Rutland <mark.rutland@arm.com>
> Acked-by: Catalin Marinas <catalin.marinas@arm.com>
> Acked-by: Marc Zyngier <marc.zyngier@arm.com>
> ---
Acked-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
^ permalink raw reply
* [PATCH 2/4] RFC: drm/lcdc: add support for LCD panels (v2)
From: Sascha Hauer @ 2013-01-11 13:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1357704685-3600-3-git-send-email-robdclark@gmail.com>
Hi Rob,
On Tue, Jan 08, 2013 at 10:11:23PM -0600, Rob Clark wrote:
> Add an output panel driver for LCD panels. Tested with LCD3 cape on
> beaglebone.
>
> TODO: need some way to control the appropriate backlight device
> TODO: probably want to make the DT bindings more generic for panel-info
>
> v1: original
> v2: s/of_find_node_by_name()/of_get_child_by_name()/ from Pantelis
> Antoniou
>
> Signed-off-by: Rob Clark <robdclark@gmail.com>
> +static enum drm_connector_status panel_connector_detect(
> + struct drm_connector *connector,
> + bool force)
> +{
> + return connector_status_connected;
> +}
> +
> +/* maybe this should be helper in drm-core? */
> +static inline void copy_timings_to_drm(struct drm_display_mode *mode,
> + struct display_timing *timing)
> +{
This actually is a helper already and is called of_get_drm_display_mode.
> + mode->clock = timing->pixelclock.typ / 1000;
> +
> + mode->hdisplay = timing->hactive.typ;
> + mode->hsync_start = mode->hdisplay + timing->hfront_porch.typ;
> + mode->hsync_end = mode->hsync_start + timing->hsync_len.typ;
> + mode->htotal = mode->hsync_end + timing->hback_porch.typ;
> +
> + mode->vdisplay = timing->vactive.typ;
> + mode->vsync_start = mode->vdisplay + timing->vfront_porch.typ;
> + mode->vsync_end = mode->vsync_start + timing->vsync_len.typ;
> + mode->vtotal = mode->vsync_end + timing->vback_porch.typ;
> +
> + mode->flags = 0;
> +
> + if (timing->interlaced)
> + mode->flags |= DRM_MODE_FLAG_INTERLACE;
> +
> + if (timing->doublescan)
> + mode->flags |= DRM_MODE_FLAG_DBLSCAN;
> +
> + if (timing->hsync_pol_active)
> + mode->flags |= DRM_MODE_FLAG_PHSYNC;
> + else
> + mode->flags |= DRM_MODE_FLAG_NHSYNC;
> +
> + if (timing->vsync_pol_active)
> + mode->flags |= DRM_MODE_FLAG_PVSYNC;
> + else
> + mode->flags |= DRM_MODE_FLAG_NVSYNC;
> +}
> +
[...]
> +
> +static struct of_device_id panel_of_match[];
> +
> +static int __devinit panel_probe(struct platform_device *pdev)
> +{
> + struct device_node *node = pdev->dev.of_node;
> + struct panel_module *panel_mod;
> + struct lcdc_module *mod;
> + struct pinctrl *pinctrl;
> + int ret = -EINVAL;
> +
> + /* bail out early if no DT data: */
> + if (!of_match_device(panel_of_match, &pdev->dev)) {
> + dev_err(&pdev->dev, "device-tree data is missing\n");
> + return -ENXIO;
> + }
You wouldn't be here if the device didn't match. How about
if (!node)
return -ENXIO;
instead which prevents you from trying to probe the device when
somebody registered a device without dt support?
Same for the other platform devices in this series.
Sascha
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply
* [PATCH 18/18] power: ab8500_fg: Remove pointless round_jiffies() call
From: Lee Jones @ 2013-01-11 13:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1357909986-9262-1-git-send-email-lee.jones@linaro.org>
As HZ is a full-second, there is little point in rounding it.
Let's save a few cycles by using HZ directly.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
drivers/power/ab8500_fg.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index 4bc2c2d..a22f6ce 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -1854,7 +1854,7 @@ static void ab8500_fg_check_hw_failure_work(struct work_struct *work)
}
/* Not yet recovered from ovv, reschedule this test */
queue_delayed_work(di->fg_wq, &di->fg_check_hw_failure_work,
- round_jiffies(HZ));
+ HZ);
} else {
dev_dbg(di->dev, "Battery recovered from OVV\n");
di->flags.bat_ovv = false;
--
1.7.9.5
^ permalink raw reply related
* [PATCH 17/18] Power: ab8500_fg: Overflow in current calculation
From: Lee Jones @ 2013-01-11 13:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1357909986-9262-1-git-send-email-lee.jones@linaro.org>
From: Paer-Olof Haakansson <par-olof.hakansson@stericsson.com>
When calculating the average current the nominator will
overflow when the charging current is high.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Henrik S?lver <henrik.solver@stericsson.com>
Reviewed-by: Par-Olof HAKANSSON <par-olof.hakansson@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
Tested-by: Par-Olof HAKANSSON <par-olof.hakansson@stericsson.com>
---
drivers/power/ab8500_fg.c | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index 845e64f..4bc2c2d 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -805,12 +805,9 @@ static void ab8500_fg_acc_cur_work(struct work_struct *work)
/*
* Convert to unit value in mA
- * Full scale input voltage is
- * 66.660mV => LSB = 66.660mV/(4096*res) = 1.627mA
- * Given a 250ms conversion cycle time the LSB corresponds
- * to 112.9 nAh. Convert to current by dividing by the conversion
+ * by dividing by the conversion
* time in hours (= samples / (3600 * 4)h)
- * 112.9nAh assumes 10mOhm, but fg_res is in 0.1mOhm
+ * and multiply with 1000
*/
di->avg_curr = (val * QLSB_NANO_AMP_HOURS_X10 * 36) /
(1000 * di->bm->fg_res * (di->fg_samples / 4));
@@ -821,6 +818,8 @@ static void ab8500_fg_acc_cur_work(struct work_struct *work)
queue_work(di->fg_wq, &di->fg_work);
+ dev_dbg(di->dev, "fg_res: %d, fg_samples: %d, gasg: %d, accu_charge: %d \n",
+ di->bm->fg_res, di->fg_samples, val, di->accu_charge);
return;
exit:
dev_err(di->dev,
--
1.7.9.5
^ permalink raw reply related
* [PATCH 16/18] power: ab8500_charger: Adds support for legacy USB chargers
From: Lee Jones @ 2013-01-11 13:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1357909986-9262-1-git-send-email-lee.jones@linaro.org>
From: Marcus Cooper <marcus.xm.cooper@stericsson.com>
A Legacy USB charger should be handled directly by the charger
driver.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Marcus Cooper <marcus.xm.cooper@stericsson.com>
Reviewed-by: Karl KOMIEROWSKI <karl.komierowski@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
drivers/power/ab8500_charger.c | 79 ++++++++++++++++++++++++++++++++--------
1 file changed, 63 insertions(+), 16 deletions(-)
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 2769e91..0957240 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -88,6 +88,9 @@
/* Step up/down delay in us */
#define STEP_UDELAY 1000
+/* Wait for enumeration before charging in ms */
+#define WAIT_FOR_USB_ENUMERATION 5 * 1000
+
#define CHARGER_STATUS_POLL 10 /* in ms */
/* UsbLineStatus register - usb types */
@@ -201,6 +204,7 @@ struct ab8500_charger_usb_state {
* charger is enabled
* @vbat Battery voltage
* @old_vbat Previously measured battery voltage
+ * @usb_device_is_unrecognised USB device is unrecognised by the hardware
* @autopower Indicate if we should have automatic pwron after pwrloss
* @autopower_cfg platform specific power config support for "pwron after pwrloss"
* @parent: Pointer to the struct ab8500
@@ -219,6 +223,7 @@ struct ab8500_charger_usb_state {
* @check_usbchgnotok_work: Work for checking USB charger not ok status
* @kick_wd_work: Work for kicking the charger watchdog in case
* of ABB rev 1.* due to the watchog logic bug
+ * @attach_work: Work for checking the usb enumeration
* @ac_charger_attached_work: Work for checking if AC charger is still
* connected
* @usb_charger_attached_work: Work for checking if USB charger is still
@@ -243,6 +248,7 @@ struct ab8500_charger {
bool vddadc_en_usb;
int vbat;
int old_vbat;
+ bool usb_device_is_unrecognised;
bool autopower;
bool autopower_cfg;
struct ab8500 *parent;
@@ -260,6 +266,7 @@ struct ab8500_charger {
struct delayed_work check_hw_failure_work;
struct delayed_work check_usbchgnotok_work;
struct delayed_work kick_wd_work;
+ struct delayed_work attach_work;
struct delayed_work ac_charger_attached_work;
struct delayed_work usb_charger_attached_work;
struct work_struct ac_work;
@@ -597,6 +604,8 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
{
int ret = 0;
+ di->usb_device_is_unrecognised = false;
+
switch (link_status) {
case USB_STAT_STD_HOST_NC:
case USB_STAT_STD_HOST_C_NS:
@@ -642,9 +651,15 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
di->max_usb_in_curr);
break;
+ case USB_STAT_NOT_CONFIGURED:
+ if (di->vbus_detected) {
+ di->usb_device_is_unrecognised = true;
+ dev_dbg(di->dev, "USB Type - Legacy charger.\n");
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P5;
+ break;
+ }
case USB_STAT_HM_IDGND:
case USB_STAT_NOT_VALID_LINK:
- case USB_STAT_NOT_CONFIGURED:
dev_err(di->dev, "USB Type - Charging not allowed\n");
di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
ret = -ENXIO;
@@ -1912,6 +1927,29 @@ static void ab8500_charger_detect_usb_type_work(struct work_struct *work)
}
/**
+ * ab8500_charger_usb_link_attach_work() - delayd work to detect USB type
+ * @work: pointer to the work_struct structure
+ *
+ * Detect the type of USB plugged
+ */
+static void ab8500_charger_usb_link_attach_work(struct work_struct *work)
+{
+ struct ab8500_charger *di =
+ container_of(work, struct ab8500_charger, attach_work.work);
+ int ret;
+
+ /* Update maximum input current if USB enumeration is not detected */
+ if (!di->usb.charger_online) {
+ ret = ab8500_charger_set_vbus_in_curr(di, di->max_usb_in_curr);
+ if (ret)
+ return;
+ }
+
+ ab8500_charger_set_usb_connected(di, true);
+ ab8500_power_supply_changed(di, &di->usb_chg.psy);
+}
+
+/**
* ab8500_charger_usb_link_status_work() - work to detect USB type
* @work: pointer to the work_struct structure
*
@@ -1937,23 +1975,29 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
di->vbus_detected = 0;
ab8500_charger_set_usb_connected(di, false);
ab8500_power_supply_changed(di, &di->usb_chg.psy);
- } else {
- di->vbus_detected = 1;
- ret = ab8500_charger_read_usb_type(di);
- if (!ret) {
- /* Update maximum input current */
- ret = ab8500_charger_set_vbus_in_curr(di,
- di->max_usb_in_curr);
- if (ret)
- return;
+ return;
+ }
- ab8500_charger_set_usb_connected(di, true);
- ab8500_power_supply_changed(di, &di->usb_chg.psy);
- } else if (ret == -ENXIO) {
- /* No valid charger type detected */
- ab8500_charger_set_usb_connected(di, false);
- ab8500_power_supply_changed(di, &di->usb_chg.psy);
+ di->vbus_detected = 1;
+ ret = ab8500_charger_read_usb_type(di);
+ if (!ret) {
+ if (di->usb_device_is_unrecognised) {
+ dev_dbg(di->dev,
+ "Potential Legacy Charger device. "
+ "Delay work for %d msec for USB enum "
+ "to finish",
+ WAIT_FOR_USB_ENUMERATION);
+ queue_delayed_work(di->charger_wq,
+ &di->attach_work,
+ msecs_to_jiffies(WAIT_FOR_USB_ENUMERATION));
+ } else {
+ queue_delayed_work(di->charger_wq,
+ &di->attach_work, 0);
}
+ } else if (ret == -ENXIO) {
+ /* No valid charger type detected */
+ ab8500_charger_set_usb_connected(di, false);
+ ab8500_power_supply_changed(di, &di->usb_chg.psy);
}
}
@@ -2916,6 +2960,9 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
INIT_DEFERRABLE_WORK(&di->check_vbat_work,
ab8500_charger_check_vbat_work);
+ INIT_DELAYED_WORK(&di->attach_work,
+ ab8500_charger_usb_link_attach_work);
+
/* Init work for charger detection */
INIT_WORK(&di->usb_link_status_work,
ab8500_charger_usb_link_status_work);
--
1.7.9.5
^ permalink raw reply related
* [PATCH 15/18] power: ab8500_charger: remove unused defines.
From: Lee Jones @ 2013-01-11 13:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1357909986-9262-1-git-send-email-lee.jones@linaro.org>
From: Marcus Cooper <marcus.xm.cooper@stericsson.com>
Cleanup of the ab8500_charger driver.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Marcus Cooper <marcus.xm.cooper@stericsson.com>
Reviewed-by: Karl KOMIEROWSKI <karl.komierowski@stericsson.com>
---
drivers/power/ab8500_charger.c | 5 -----
1 file changed, 5 deletions(-)
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 30193ea..2769e91 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -115,11 +115,6 @@ enum ab8500_charger_link_status {
USB_STAT_CARKIT_1,
USB_STAT_CARKIT_2,
USB_STAT_ACA_DOCK_CHARGER,
- USB_STAT_SAMSUNG_USB_PHY_DIS,
- USB_STAT_SAMSUNG_USB_PHY_ENA,
- USB_STAT_SAMSUNG_UART_PHY_DIS,
- USB_STAT_SAMSUNG_UART_PHY_ENA,
- USB_STAT_MOTOROLA_USB_PHY_ENA,
};
enum ab8500_usb_state {
--
1.7.9.5
^ permalink raw reply related
* [PATCH 14/18] power: pm2301: Add pm2301 charger
From: Lee Jones @ 2013-01-11 13:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1357909986-9262-1-git-send-email-lee.jones@linaro.org>
From: Michel JAOUEN <michel.jaouen@stericsson.com>
PM2301 AC charger driver based on 9540 platform.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Rajkumar Kasirajan <rajkumar.kasirajan@stericsson.com>
Signed-off-by: Loic Pallardy <loic.pallardy@stericsson.com>
Reviewed-by: Michel JAOUEN <michel.jaouen@stericsson.com>
Tested-by: Michel JAOUEN <michel.jaouen@stericsson.com>
---
drivers/power/Kconfig | 15 +
drivers/power/Makefile | 1 +
drivers/power/ab8500_charger.c | 8 +-
drivers/power/pm2301_charger.c | 1599 ++++++++++++++++++++++++++++++++++++++++
include/linux/pm2301_charger.h | 60 ++
5 files changed, 1681 insertions(+), 2 deletions(-)
create mode 100644 drivers/power/pm2301_charger.c
create mode 100644 include/linux/pm2301_charger.h
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 263499f..0830779 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -346,6 +346,21 @@ config AB8500_BM
help
Say Y to include support for AB8500 battery management.
+config CHARGER_PM2301
+ bool "PM2301 Battery Charger Driver"
+ depends on AB8500_BM
+ help
+ Say Y to include support for PM2301 charger driver.
+
+config PM2XXX_DEEP_DEBUG
+ bool "PM2XXX Deep Debug"
+ depends on DEEP_DEBUG && CHARGER_PM2301
+ default n
+ help
+ Deep Debug interface provides an access to all registers.
+ It allows to read or write directly a register.
+ Say Y to include support for Deep Debug.
+ If unsure, say N.
endif # POWER_SUPPLY
source "drivers/power/avs/Kconfig"
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 070c73d..8130786 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -46,6 +46,7 @@ obj-$(CONFIG_CHARGER_LP8727) += lp8727_charger.o
obj-$(CONFIG_CHARGER_LP8788) += lp8788-charger.o
obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o
obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o
+obj-$(CONFIG_CHARGER_PM2301) += pm2301_charger.o
obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index ff18617..30193ea 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -2795,7 +2795,9 @@ static int __devexit ab8500_charger_remove(struct platform_device *pdev)
flush_scheduled_work();
power_supply_unregister(&di->usb_chg.psy);
+#if !defined(CONFIG_CHARGER_PM2301)
power_supply_unregister(&di->ac_chg.psy);
+#endif
platform_set_drvdata(pdev, NULL);
return 0;
@@ -2954,14 +2956,14 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
dev_err(di->dev, "failed to initialize ABB registers\n");
goto free_regulator;
}
-
+#if !defined(CONFIG_CHARGER_PM2301)
/* Register AC charger class */
ret = power_supply_register(di->dev, &di->ac_chg.psy);
if (ret) {
dev_err(di->dev, "failed to register AC charger\n");
goto free_regulator;
}
-
+#endif
/* Register USB charger class */
ret = power_supply_register(di->dev, &di->usb_chg.psy);
if (ret) {
@@ -3048,7 +3050,9 @@ put_usb_phy:
free_usb:
power_supply_unregister(&di->usb_chg.psy);
free_ac:
+#if !defined(CONFIG_CHARGER_PM2301)
power_supply_unregister(&di->ac_chg.psy);
+#endif
free_regulator:
regulator_put(di->regu);
free_charger_wq:
diff --git a/drivers/power/pm2301_charger.c b/drivers/power/pm2301_charger.c
new file mode 100644
index 0000000..b579e4d
--- /dev/null
+++ b/drivers/power/pm2301_charger.c
@@ -0,0 +1,1599 @@
+/*
+ * Power supply driver for ST Ericsson pm2xxx_charger charger
+ *
+ * Copyright 2012 ST Ericsson.
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/completion.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <linux/kobject.h>
+#include <linux/mfd/ab8500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500-bm.h>
+#include <linux/mfd/abx500/ab8500-gpadc.h>
+#include <linux/mfd/abx500/ux500_chargalg.h>
+#include <linux/pm2301_charger.h>
+
+#ifdef CONFIG_PM2XXX_DEEP_DEBUG
+#include <linux/debugfs.h>
+#include <linux/ux500_deepdebug.h>
+#endif
+
+#define MAIN_WDOG_ENA 0x01
+#define MAIN_WDOG_KICK 0x02
+#define MAIN_WDOG_DIS 0x00
+#define CHARG_WD_KICK 0x01
+#define MAIN_CH_ENA 0x01
+#define MAIN_CH_NO_OVERSHOOT_ENA_N 0x02
+#define MAIN_CH_DET 0x01
+#define MAIN_CH_CV_ON 0x04
+#define OTP_ENABLE_WD 0x01
+
+#define MAIN_CH_INPUT_CURR_SHIFT 4
+
+#define LED_INDICATOR_PWM_ENA 0x01
+#define LED_INDICATOR_PWM_DIS 0x00
+#define LED_IND_CUR_5MA 0x04
+#define LED_INDICATOR_PWM_DUTY_252_256 0xBF
+
+/* HW failure constants */
+#define MAIN_CH_TH_PROT 0x02
+#define MAIN_CH_NOK 0x01
+
+/* Watchdog timeout constant */
+#define WD_TIMER 0x30 /* 4min */
+#define WD_KICK_INTERVAL (60 * HZ)
+
+/* Lowest charger voltage is 3.39V -> 0x4E */
+#define LOW_VOLT_REG 0x4E
+
+#define PM2XXX_BATT_CTRL_REG1 0x00
+#define PM2XXX_BATT_CTRL_REG2 0x01
+#define PM2XXX_BATT_CTRL_REG3 0x02
+#define PM2XXX_BATT_CTRL_REG4 0x03
+#define PM2XXX_BATT_CTRL_REG5 0x04
+#define PM2XXX_BATT_CTRL_REG6 0x05
+#define PM2XXX_BATT_CTRL_REG7 0x06
+#define PM2XXX_BATT_CTRL_REG8 0x07
+#define PM2XXX_NTC_CTRL_REG1 0x08
+#define PM2XXX_NTC_CTRL_REG2 0x09
+#define PM2XXX_BATT_CTRL_REG9 0x0A
+#define PM2XXX_BATT_STAT_REG1 0x0B
+#define PM2XXX_INP_VOLT_VPWR2 0x11
+#define PM2XXX_INP_DROP_VPWR2 0x13
+#define PM2XXX_INP_VOLT_VPWR1 0x15
+#define PM2XXX_INP_DROP_VPWR1 0x17
+#define PM2XXX_INP_MODE_VPWR 0x18
+#define PM2XXX_BATT_WD_KICK 0x70
+#define PM2XXX_DEV_VER_STAT 0x0C
+#define PM2XXX_THERM_WARN_CTRL_REG 0x20
+#define PM2XXX_BATT_DISC_REG 0x21
+#define PM2XXX_BATT_LOW_LEV_COMP_REG 0x22
+#define PM2XXX_BATT_LOW_LEV_VAL_REG 0x23
+#define PM2XXX_I2C_PAD_CTRL_REG 0x24
+#define PM2XXX_SW_CTRL_REG 0x26
+#define PM2XXX_LED_CTRL_REG 0x28
+
+#define PM2XXX_REG_INT1 0x40
+#define PM2XXX_MASK_REG_INT1 0x50
+#define PM2XXX_SRCE_REG_INT1 0x60
+#define PM2XXX_REG_INT2 0x41
+#define PM2XXX_MASK_REG_INT2 0x51
+#define PM2XXX_SRCE_REG_INT2 0x61
+#define PM2XXX_REG_INT3 0x42
+#define PM2XXX_MASK_REG_INT3 0x52
+#define PM2XXX_SRCE_REG_INT3 0x62
+#define PM2XXX_REG_INT4 0x43
+#define PM2XXX_MASK_REG_INT4 0x53
+#define PM2XXX_SRCE_REG_INT4 0x63
+#define PM2XXX_REG_INT5 0x44
+#define PM2XXX_MASK_REG_INT5 0x54
+#define PM2XXX_SRCE_REG_INT5 0x64
+#define PM2XXX_REG_INT6 0x45
+#define PM2XXX_MASK_REG_INT6 0x55
+#define PM2XXX_SRCE_REG_INT6 0x65
+
+#define VPWR_OVV 0x0
+#define VSYSTEM_OVV 0x1
+
+/* control Reg 1 */
+#define PM2XXX_CH_RESUME_EN 0x1
+#define PM2XXX_CH_RESUME_DIS 0x0
+
+/* control Reg 2 */
+#define PM2XXX_CH_AUTO_RESUME_EN 0X2
+#define PM2XXX_CH_AUTO_RESUME_DIS 0X0
+#define PM2XXX_CHARGER_ENA 0x4
+#define PM2XXX_CHARGER_DIS 0x0
+
+/* control Reg 3 */
+#define PM2XXX_CH_WD_CC_PHASE_OFF 0x0
+#define PM2XXX_CH_WD_CC_PHASE_5MIN 0x1
+#define PM2XXX_CH_WD_CC_PHASE_10MIN 0x2
+#define PM2XXX_CH_WD_CC_PHASE_30MIN 0x3
+#define PM2XXX_CH_WD_CC_PHASE_60MIN 0x4
+#define PM2XXX_CH_WD_CC_PHASE_120MIN 0x5
+#define PM2XXX_CH_WD_CC_PHASE_240MIN 0x6
+#define PM2XXX_CH_WD_CC_PHASE_360MIN 0x7
+
+#define PM2XXX_CH_WD_CV_PHASE_OFF (0x0<<3)
+#define PM2XXX_CH_WD_CV_PHASE_5MIN (0x1<<3)
+#define PM2XXX_CH_WD_CV_PHASE_10MIN (0x2<<3)
+#define PM2XXX_CH_WD_CV_PHASE_30MIN (0x3<<3)
+#define PM2XXX_CH_WD_CV_PHASE_60MIN (0x4<<3)
+#define PM2XXX_CH_WD_CV_PHASE_120MIN (0x5<<3)
+#define PM2XXX_CH_WD_CV_PHASE_240MIN (0x6<<3)
+#define PM2XXX_CH_WD_CV_PHASE_360MIN (0x7<<3)
+
+/* control Reg 4 */
+#define PM2XXX_CH_WD_PRECH_PHASE_OFF 0x0
+#define PM2XXX_CH_WD_PRECH_PHASE_1MIN 0x1
+#define PM2XXX_CH_WD_PRECH_PHASE_5MIN 0x2
+#define PM2XXX_CH_WD_PRECH_PHASE_10MIN 0x3
+#define PM2XXX_CH_WD_PRECH_PHASE_30MIN 0x4
+#define PM2XXX_CH_WD_PRECH_PHASE_60MIN 0x5
+#define PM2XXX_CH_WD_PRECH_PHASE_120MIN 0x6
+#define PM2XXX_CH_WD_PRECH_PHASE_240MIN 0x7
+
+/* control Reg 5 */
+#define PM2XXX_CH_WD_AUTO_TIMEOUT_NONE 0x0
+#define PM2XXX_CH_WD_AUTO_TIMEOUT_20MIN 0x1
+
+/* control Reg 6 */
+#define PM2XXX_DIR_CH_CC_CURRENT_200MA 0x0
+#define PM2XXX_DIR_CH_CC_CURRENT_400MA 0x2
+#define PM2XXX_DIR_CH_CC_CURRENT_600MA 0x3
+#define PM2XXX_DIR_CH_CC_CURRENT_800MA 0x4
+#define PM2XXX_DIR_CH_CC_CURRENT_1000MA 0x5
+#define PM2XXX_DIR_CH_CC_CURRENT_1200MA 0x6
+#define PM2XXX_DIR_CH_CC_CURRENT_1400MA 0x7
+#define PM2XXX_DIR_CH_CC_CURRENT_1600MA 0x8
+#define PM2XXX_DIR_CH_CC_CURRENT_1800MA 0x9
+#define PM2XXX_DIR_CH_CC_CURRENT_2000MA 0xA
+#define PM2XXX_DIR_CH_CC_CURRENT_2200MA 0xB
+#define PM2XXX_DIR_CH_CC_CURRENT_2400MA 0xC
+#define PM2XXX_DIR_CH_CC_CURRENT_2600MA 0xD
+#define PM2XXX_DIR_CH_CC_CURRENT_2800MA 0xE
+#define PM2XXX_DIR_CH_CC_CURRENT_3000MA 0xF
+
+#define PM2XXX_CH_PRECH_CURRENT_25MA (0x0<<4)
+#define PM2XXX_CH_PRECH_CURRENT_50MA (0x1<<4)
+#define PM2XXX_CH_PRECH_CURRENT_75MA (0x2<<4)
+#define PM2XXX_CH_PRECH_CURRENT_100MA (0x3<<4)
+
+#define PM2XXX_CH_EOC_CURRENT_100MA (0x0<<6)
+#define PM2XXX_CH_EOC_CURRENT_150MA (0x1<<6)
+#define PM2XXX_CH_EOC_CURRENT_300MA (0x2<<6)
+#define PM2XXX_CH_EOC_CURRENT_400MA (0x3<<6)
+
+/* control Reg 7 */
+#define PM2XXX_CH_PRECH_VOL_2_5 0x0
+#define PM2XXX_CH_PRECH_VOL_2_7 0x1
+#define PM2XXX_CH_PRECH_VOL_2_9 0x2
+#define PM2XXX_CH_PRECH_VOL_3_1 0x3
+
+#define PM2XXX_CH_VRESUME_VOL_3_2 (0x0<<2)
+#define PM2XXX_CH_VRESUME_VOL_3_4 (0x1<<2)
+#define PM2XXX_CH_VRESUME_VOL_3_6 (0x2<<2)
+#define PM2XXX_CH_VRESUME_VOL_3_8 (0x3<<2)
+
+/* control Reg 8 */
+#define PM2XXX_CH_VOLT_3_5 0x0
+#define PM2XXX_CH_VOLT_3_5225 0x1
+#define PM2XXX_CH_VOLT_3_6 0x4
+#define PM2XXX_CH_VOLT_3_7 0x8
+#define PM2XXX_CH_VOLT_4_0 0x14
+#define PM2XXX_CH_VOLT_4_175 0x1B
+#define PM2XXX_CH_VOLT_4_2 0x1C
+#define PM2XXX_CH_VOLT_4_275 0x1F
+#define PM2XXX_CH_VOLT_4_3 0x20
+
+/*NTC control register 1*/
+#define PM2XXX_BTEMP_HIGH_TH_45 0x0
+#define PM2XXX_BTEMP_HIGH_TH_50 0x1
+#define PM2XXX_BTEMP_HIGH_TH_55 0x2
+#define PM2XXX_BTEMP_HIGH_TH_60 0x3
+#define PM2XXX_BTEMP_HIGH_TH_65 0x4
+
+#define PM2XXX_BTEMP_LOW_TH_N5 (0x0<<3)
+#define PM2XXX_BTEMP_LOW_TH_0 (0x1<<3)
+#define PM2XXX_BTEMP_LOW_TH_5 (0x2<<3)
+#define PM2XXX_BTEMP_LOW_TH_10 (0x3<<3)
+
+/*NTC control register 2*/
+#define PM2XXX_NTC_BETA_COEFF_3477 0x0
+#define PM2XXX_NTC_BETA_COEFF_3964 0x1
+
+#define PM2XXX_NTC_RES_10K (0x0<<2)
+#define PM2XXX_NTC_RES_47K (0x1<<2)
+#define PM2XXX_NTC_RES_100K (0x2<<2)
+#define PM2XXX_NTC_RES_NO_NTC (0x3<<2)
+
+/* control Reg 9 */
+#define PM2XXX_CH_CC_MODEDROP_EN 1
+#define PM2XXX_CH_CC_MODEDROP_DIS 0
+
+#define PM2XXX_CH_CC_REDUCED_CURRENT_100MA (0x0<<1)
+#define PM2XXX_CH_CC_REDUCED_CURRENT_200MA (0x1<<1)
+#define PM2XXX_CH_CC_REDUCED_CURRENT_400MA (0x2<<1)
+#define PM2XXX_CH_CC_REDUCED_CURRENT_IDENT (0x3<<1)
+
+#define PM2XXX_CHARCHING_INFO_DIS (0<<3)
+#define PM2XXX_CHARCHING_INFO_EN (1<<3)
+
+#define PM2XXX_CH_150MV_DROP_300MV (0<<4)
+#define PM2XXX_CH_150MV_DROP_150MV (1<<4)
+
+
+/* charger status register */
+#define PM2XXX_CHG_STATUS_OFF 0x0
+#define PM2XXX_CHG_STATUS_ON 0x1
+#define PM2XXX_CHG_STATUS_FULL 0x2
+#define PM2XXX_CHG_STATUS_ERR 0x3
+#define PM2XXX_CHG_STATUS_WAIT 0x4
+#define PM2XXX_CHG_STATUS_NOBAT 0x5
+
+/* Input charger voltage VPWR2 */
+#define PM2XXX_VPWR2_OVV_6_0 0x0
+#define PM2XXX_VPWR2_OVV_6_3 0x1
+#define PM2XXX_VPWR2_OVV_10 0x2
+#define PM2XXX_VPWR2_OVV_NONE 0x3
+
+/* Input charger voltage VPWR1 */
+#define PM2XXX_VPWR1_OVV_6_0 0x0
+#define PM2XXX_VPWR1_OVV_6_3 0x1
+#define PM2XXX_VPWR1_OVV_10 0x2
+#define PM2XXX_VPWR1_OVV_NONE 0x3
+
+/* Battery low level comparator control register */
+#define PM2XXX_VBAT_LOW_MONITORING_DIS 0x0
+#define PM2XXX_VBAT_LOW_MONITORING_ENA 0x1
+
+/* Battery low level value control register */
+#define PM2XXX_VBAT_LOW_LEVEL_2_3 0x0
+#define PM2XXX_VBAT_LOW_LEVEL_2_4 0x1
+#define PM2XXX_VBAT_LOW_LEVEL_2_5 0x2
+#define PM2XXX_VBAT_LOW_LEVEL_2_6 0x3
+#define PM2XXX_VBAT_LOW_LEVEL_2_7 0x4
+#define PM2XXX_VBAT_LOW_LEVEL_2_8 0x5
+#define PM2XXX_VBAT_LOW_LEVEL_2_9 0x6
+#define PM2XXX_VBAT_LOW_LEVEL_3_0 0x7
+#define PM2XXX_VBAT_LOW_LEVEL_3_1 0x8
+#define PM2XXX_VBAT_LOW_LEVEL_3_2 0x9
+#define PM2XXX_VBAT_LOW_LEVEL_3_3 0xA
+#define PM2XXX_VBAT_LOW_LEVEL_3_4 0xB
+#define PM2XXX_VBAT_LOW_LEVEL_3_5 0xC
+#define PM2XXX_VBAT_LOW_LEVEL_3_6 0xD
+#define PM2XXX_VBAT_LOW_LEVEL_3_7 0xE
+#define PM2XXX_VBAT_LOW_LEVEL_3_8 0xF
+#define PM2XXX_VBAT_LOW_LEVEL_3_9 0x10
+#define PM2XXX_VBAT_LOW_LEVEL_4_0 0x11
+#define PM2XXX_VBAT_LOW_LEVEL_4_1 0x12
+#define PM2XXX_VBAT_LOW_LEVEL_4_2 0x13
+
+/* SW CTRL */
+#define PM2XXX_SWCTRL_HW 0x0
+#define PM2XXX_SWCTRL_SW 0x1
+
+
+/* LED Driver Control */
+#define PM2XXX_LED_CURRENT_2_5MA (0X0<<2)
+#define PM2XXX_LED_CURRENT_1MA (0X1<<2)
+#define PM2XXX_LED_CURRENT_5MA (0X2<<2)
+#define PM2XXX_LED_CURRENT_10MA (0X3<<2)
+
+#define PM2XXX_LED_SELECT_EN (0X0<<1)
+#define PM2XXX_LED_SELECT_DIS (0X1<<1)
+
+#define PM2XXX_ANTI_OVERSHOOT_DIS 0X0
+#define PM2XXX_ANTI_OVERSHOOT_EN 0X1
+
+#define to_pm2xxx_charger_ac_device_info(x) container_of((x), \
+ struct pm2xxx_charger, ac_chg)
+
+static int pm2xxx_interrupt_registers[] = {
+ PM2XXX_REG_INT1 ,
+ PM2XXX_REG_INT2 ,
+ PM2XXX_REG_INT3 ,
+ PM2XXX_REG_INT4 ,
+ PM2XXX_REG_INT5 ,
+ PM2XXX_REG_INT6 ,
+};
+
+enum pm2xxx_reg_int1 {
+ PM2XXX_INT1_ITVBATDISCONNECT = 0x02,
+ PM2XXX_INT1_ITVBATLOWR = 0x04,
+ PM2XXX_INT1_ITVBATLOWF = 0x08,
+};
+
+enum pm2xxx_mask_reg_int1 {
+ PM2XXX_INT1_M_ITVBATDISCONNECT = 0x02,
+ PM2XXX_INT1_M_ITVBATLOWR = 0x04,
+ PM2XXX_INT1_M_ITVBATLOWF = 0x08,
+};
+
+enum pm2xxx_source_reg_int1 {
+ PM2XXX_INT1_S_ITVBATDISCONNECT = 0x02,
+ PM2XXX_INT1_S_ITVBATLOWR = 0x04,
+ PM2XXX_INT1_S_ITVBATLOWF = 0x08,
+};
+
+enum pm2xxx_reg_int2 {
+ PM2XXX_INT2_ITVPWR2PLUG = 0x01,
+ PM2XXX_INT2_ITVPWR2UNPLUG = 0x02,
+ PM2XXX_INT2_ITVPWR1PLUG = 0x04,
+ PM2XXX_INT2_ITVPWR1UNPLUG = 0x08,
+};
+
+enum pm2xxx_mask_reg_int2 {
+ PM2XXX_INT2_M_ITVPWR2PLUG = 0x01,
+ PM2XXX_INT2_M_ITVPWR2UNPLUG = 0x02,
+ PM2XXX_INT2_M_ITVPWR1PLUG = 0x04,
+ PM2XXX_INT2_M_ITVPWR1UNPLUG = 0x08,
+};
+
+enum pm2xxx_source_reg_int2 {
+ PM2XXX_INT2_S_ITVPWR2PLUG = 0x03,
+ PM2XXX_INT2_S_ITVPWR1PLUG = 0x0c,
+};
+
+enum pm2xxx_reg_int3 {
+ PM2XXX_INT3_ITCHPRECHARGEWD = 0x01,
+ PM2XXX_INT3_ITCHCCWD = 0x02,
+ PM2XXX_INT3_ITCHCVWD = 0x04,
+ PM2XXX_INT3_ITAUTOTIMEOUTWD = 0x08,
+};
+
+enum pm2xxx_mask_reg_int3 {
+ PM2XXX_INT3_M_ITCHPRECHARGEWD = 0x01,
+ PM2XXX_INT3_M_ITCHCCWD = 0x02,
+ PM2XXX_INT3_M_ITCHCVWD = 0x04,
+ PM2XXX_INT3_M_ITAUTOTIMEOUTWD = 0x08,
+};
+
+enum pm2xxx_source_reg_int3 {
+ PM2XXX_INT3_S_ITCHPRECHARGEWD = 0x01,
+ PM2XXX_INT3_S_ITCHCCWD = 0x02,
+ PM2XXX_INT3_S_ITCHCVWD = 0x04,
+ PM2XXX_INT3_S_ITAUTOTIMEOUTWD = 0x08,
+};
+
+enum pm2xxx_reg_int4 {
+ PM2XXX_INT4_ITBATTEMPCOLD = 0x01,
+ PM2XXX_INT4_ITBATTEMPHOT = 0x02,
+ PM2XXX_INT4_ITVPWR2OVV = 0x04,
+ PM2XXX_INT4_ITVPWR1OVV = 0x08,
+ PM2XXX_INT4_ITCHARGINGON = 0x10,
+ PM2XXX_INT4_ITVRESUME = 0x20,
+ PM2XXX_INT4_ITBATTFULL = 0x40,
+ PM2XXX_INT4_ITCVPHASE = 0x80,
+};
+
+enum pm2xxx_mask_reg_int4 {
+ PM2XXX_INT4_M_ITBATTEMPCOLD = 0x01,
+ PM2XXX_INT4_M_ITBATTEMPHOT = 0x02,
+ PM2XXX_INT4_M_ITVPWR2OVV = 0x04,
+ PM2XXX_INT4_M_ITVPWR1OVV = 0x08,
+ PM2XXX_INT4_M_ITCHARGINGON = 0x10,
+ PM2XXX_INT4_M_ITVRESUME = 0x20,
+ PM2XXX_INT4_M_ITBATTFULL = 0x40,
+ PM2XXX_INT4_M_ITCVPHASE = 0x80,
+};
+
+enum pm2xxx_source_reg_int4 {
+ PM2XXX_INT4_S_ITBATTEMPCOLD = 0x01,
+ PM2XXX_INT4_S_ITBATTEMPHOT = 0x02,
+ PM2XXX_INT4_S_ITVPWR2OVV = 0x04,
+ PM2XXX_INT4_S_ITVPWR1OVV = 0x08,
+ PM2XXX_INT4_S_ITCHARGINGON = 0x10,
+ PM2XXX_INT4_S_ITVRESUME = 0x20,
+ PM2XXX_INT4_S_ITBATTFULL = 0x40,
+ PM2XXX_INT4_S_ITCVPHASE = 0x80,
+};
+
+enum pm2xxx_reg_int5 {
+ PM2XXX_INT5_ITTHERMALSHUTDOWNRISE = 0x01,
+ PM2XXX_INT5_ITTHERMALSHUTDOWNFALL = 0x02,
+ PM2XXX_INT5_ITTHERMALWARNINGRISE = 0x04,
+ PM2XXX_INT5_ITTHERMALWARNINGFALL = 0x08,
+ PM2XXX_INT5_ITVSYSTEMOVV = 0x10,
+};
+
+enum pm2xxx_mask_reg_int5 {
+ PM2XXX_INT5_M_ITTHERMALSHUTDOWNRISE = 0x01,
+ PM2XXX_INT5_M_ITTHERMALSHUTDOWNFALL = 0x02,
+ PM2XXX_INT5_M_ITTHERMALWARNINGRISE = 0x04,
+ PM2XXX_INT5_M_ITTHERMALWARNINGFALL = 0x08,
+ PM2XXX_INT5_M_ITVSYSTEMOVV = 0x10,
+};
+
+enum pm2xxx_source_reg_int5 {
+ PM2XXX_INT5_S_ITTHERMALSHUTDOWNRISE = 0x01,
+ PM2XXX_INT5_S_ITTHERMALSHUTDOWNFALL = 0x02,
+ PM2XXX_INT5_S_ITTHERMALWARNINGRISE = 0x04,
+ PM2XXX_INT5_S_ITTHERMALWARNINGFALL = 0x08,
+ PM2XXX_INT5_S_ITVSYSTEMOVV = 0x10,
+};
+
+enum pm2xxx_reg_int6 {
+ PM2XXX_INT6_ITVPWR2DROP = 0x01,
+ PM2XXX_INT6_ITVPWR1DROP = 0x02,
+ PM2XXX_INT6_ITVPWR2VALIDRISE = 0x04,
+ PM2XXX_INT6_ITVPWR2VALIDFALL = 0x08,
+ PM2XXX_INT6_ITVPWR1VALIDRISE = 0x10,
+ PM2XXX_INT6_ITVPWR1VALIDFALL = 0x20,
+};
+
+enum pm2xxx_mask_reg_int6 {
+ PM2XXX_INT6_M_ITVPWR2DROP = 0x01,
+ PM2XXX_INT6_M_ITVPWR1DROP = 0x02,
+ PM2XXX_INT6_M_ITVPWR2VALIDRISE = 0x04,
+ PM2XXX_INT6_M_ITVPWR2VALIDFALL = 0x08,
+ PM2XXX_INT6_M_ITVPWR1VALIDRISE = 0x10,
+ PM2XXX_INT6_M_ITVPWR1VALIDFALL = 0x20,
+};
+
+enum pm2xxx_source_reg_int6 {
+ PM2XXX_INT6_S_ITVPWR2DROP = 0x01,
+ PM2XXX_INT6_S_ITVPWR1DROP = 0x02,
+ PM2XXX_INT6_S_ITVPWR2VALIDRISE = 0x04,
+ PM2XXX_INT6_S_ITVPWR2VALIDFALL = 0x08,
+ PM2XXX_INT6_S_ITVPWR1VALIDRISE = 0x10,
+ PM2XXX_INT6_S_ITVPWR1VALIDFALL = 0x20,
+};
+
+static enum power_supply_property pm2xxx_charger_ac_props[] = {
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_AVG,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+};
+
+static int pm2xxx_charger_voltage_map[] = {
+ 3500 ,
+ 3525 ,
+ 3550 ,
+ 3575 ,
+ 3600 ,
+ 3625 ,
+ 3650 ,
+ 3675 ,
+ 3700 ,
+ 3725 ,
+ 3750 ,
+ 3775 ,
+ 3800 ,
+ 3825 ,
+ 3850 ,
+ 3875 ,
+ 3900 ,
+ 3925 ,
+ 3950 ,
+ 3975 ,
+ 4000 ,
+ 4025 ,
+ 4050 ,
+ 4075 ,
+ 4100 ,
+ 4125 ,
+ 4150 ,
+ 4175 ,
+ 4200 ,
+ 4225 ,
+ 4250 ,
+ 4275 ,
+ 4300 ,
+};
+
+static int pm2xxx_charger_current_map[] = {
+ 200 ,
+ 200 ,
+ 400 ,
+ 600 ,
+ 800 ,
+ 1000 ,
+ 1200 ,
+ 1400 ,
+ 1600 ,
+ 1800 ,
+ 2000 ,
+ 2200 ,
+ 2400 ,
+ 2600 ,
+ 2800 ,
+ 3000 ,
+};
+
+#ifdef CONFIG_PM2XXX_DEEP_DEBUG
+/* PM2XXX registers list */
+static struct ddbg_register ddbg_pm2xxx_registers[] = {
+ DDBG_REG("PM2XXX_BATT_CTRL_REG1", PM2XXX_BATT_CTRL_REG1, DDBG_RW),
+ DDBG_REG("PM2XXX_BATT_CTRL_REG2", PM2XXX_BATT_CTRL_REG2, DDBG_RW),
+ DDBG_REG("PM2XXX_BATT_CTRL_REG3", PM2XXX_BATT_CTRL_REG3, DDBG_RW),
+ DDBG_REG("PM2XXX_BATT_CTRL_REG4", PM2XXX_BATT_CTRL_REG4, DDBG_RW),
+ DDBG_REG("PM2XXX_BATT_CTRL_REG5", PM2XXX_BATT_CTRL_REG5, DDBG_RW),
+ DDBG_REG("PM2XXX_BATT_CTRL_REG6", PM2XXX_BATT_CTRL_REG6, DDBG_RW),
+ DDBG_REG("PM2XXX_BATT_CTRL_REG7", PM2XXX_BATT_CTRL_REG7, DDBG_RW),
+ DDBG_REG("PM2XXX_BATT_CTRL_REG8", PM2XXX_BATT_CTRL_REG8, DDBG_RW),
+ DDBG_REG("PM2XXX_NTC_CTRL_REG1", PM2XXX_NTC_CTRL_REG1, DDBG_RW),
+ DDBG_REG("PM2XXX_NTC_CTRL_REG2", PM2XXX_NTC_CTRL_REG2, DDBG_RW),
+ DDBG_REG("PM2XXX_BATT_CTRL_REG9", PM2XXX_BATT_CTRL_REG9, DDBG_RW),
+ DDBG_REG("PM2XXX_BATT_STAT_REG1", PM2XXX_BATT_STAT_REG1, DDBG_RO),
+ DDBG_REG("PM2XXX_INP_VOLT_VPWR2", PM2XXX_INP_VOLT_VPWR2, DDBG_RW),
+ DDBG_REG("PM2XXX_INP_DROP_VPWR2", PM2XXX_INP_DROP_VPWR2, DDBG_RW),
+ DDBG_REG("PM2XXX_INP_VOLT_VPWR1", PM2XXX_INP_VOLT_VPWR1, DDBG_RW),
+ DDBG_REG("PM2XXX_INP_DROP_VPWR1", PM2XXX_INP_DROP_VPWR1, DDBG_RW),
+ DDBG_REG("PM2XXX_INP_MODE_VPWR", PM2XXX_INP_MODE_VPWR, DDBG_RW),
+ DDBG_REG("PM2XXX_BATT_WD_KICK", PM2XXX_BATT_WD_KICK, DDBG_RW),
+ DDBG_REG("PM2XXX_DEV_VER_STAT", PM2XXX_DEV_VER_STAT, DDBG_RO),
+ DDBG_REG("PM2XXX_THERM_WARN_CTRL", PM2XXX_THERM_WARN_CTRL_REG, DDBG_RW),
+ DDBG_REG("PM2XXX_BAT_DISC_REG", PM2XXX_BATT_DISC_REG, DDBG_RW),
+ DDBG_REG("PM2XXX_BAT_LWLEV_CMP", PM2XXX_BATT_LOW_LEV_COMP_REG, DDBG_RW),
+ DDBG_REG("PM2XXX_BAT_LWLEV_VAL", PM2XXX_BATT_LOW_LEV_VAL_REG, DDBG_RW),
+ DDBG_REG("PM2XXX_I2C_PAD_CTRL_REG", PM2XXX_I2C_PAD_CTRL_REG, DDBG_RW),
+ DDBG_REG("PM2XXX_SW_CTRL_REG", PM2XXX_SW_CTRL_REG, DDBG_RW),
+ DDBG_REG("PM2XXX_LED_CTRL_REG", PM2XXX_LED_CTRL_REG, DDBG_RO),
+ DDBG_REG("PM2XXX_REG_INT1", PM2XXX_REG_INT1, DDBG_RO),
+ DDBG_REG("PM2XXX_MASK_REG_INT1", PM2XXX_MASK_REG_INT1, DDBG_RW),
+ DDBG_REG("PM2XXX_SRCE_REG_INT1", PM2XXX_SRCE_REG_INT1, DDBG_RO),
+ DDBG_REG("PM2XXX_REG_INT2", PM2XXX_REG_INT2, DDBG_RO),
+ DDBG_REG("PM2XXX_MASK_REG_INT2", PM2XXX_MASK_REG_INT2, DDBG_RW),
+ DDBG_REG("PM2XXX_SRCE_REG_INT2", PM2XXX_SRCE_REG_INT2, DDBG_RO),
+ DDBG_REG("PM2XXX_REG_INT3", PM2XXX_REG_INT3, DDBG_RO),
+ DDBG_REG("PM2XXX_MASK_REG_INT3", PM2XXX_MASK_REG_INT3, DDBG_RW),
+ DDBG_REG("PM2XXX_SRCE_REG_INT3", PM2XXX_SRCE_REG_INT3, DDBG_RO),
+ DDBG_REG("PM2XXX_REG_INT4", PM2XXX_REG_INT4, DDBG_RO),
+ DDBG_REG("PM2XXX_MASK_REG_INT4", PM2XXX_MASK_REG_INT4, DDBG_RW),
+ DDBG_REG("PM2XXX_SRCE_REG_INT4", PM2XXX_SRCE_REG_INT4, DDBG_RO),
+ DDBG_REG("PM2XXX_REG_INT5", PM2XXX_REG_INT5, DDBG_RO),
+ DDBG_REG("PM2XXX_MASK_REG_INT5", PM2XXX_MASK_REG_INT5, DDBG_RW),
+ DDBG_REG("PM2XXX_SRCE_REG_INT5", PM2XXX_SRCE_REG_INT5, DDBG_RO),
+ DDBG_REG("PM2XXX_REG_INT6", PM2XXX_REG_INT6, DDBG_RO),
+ DDBG_REG("PM2XXX_MASK_REG_INT6", PM2XXX_MASK_REG_INT6, DDBG_RW),
+ DDBG_REG("PM2XXX_SRCE_REG_INT6", PM2XXX_SRCE_REG_INT6, DDBG_RO),
+ DDBG_REG_NULL,
+};
+
+static struct pm2xxx_charger *pm2xxx_ddbg;
+#endif
+
+struct pm2xxx_irq {
+ char *name;
+ irqreturn_t (*isr)(int irq, void *data);
+};
+
+struct pm2xxx_charger_info {
+ int charger_connected;
+ int charger_online;
+ int charger_voltage;
+ int cv_active;
+ bool wd_expired;
+};
+
+struct pm2xxx_charger_event_flags {
+ bool mainextchnotok;
+ bool main_thermal_prot;
+ bool ovv;
+ bool chgwdexp;
+};
+
+struct pm2xxx_config {
+ struct i2c_client *pm2xxx_i2c;
+ struct i2c_device_id *pm2xxx_id;
+};
+
+struct pm2xxx_charger {
+ struct device *dev;
+ u8 chip_id;
+ bool vddadc_en_ac;
+ struct pm2xxx_config config;
+ bool ac_conn;
+ unsigned int gpio_irq;
+ int vbat;
+ int old_vbat;
+ int failure_case;
+ int failure_input_ovv;
+ int pm2_int[6];
+ struct ab8500_gpadc *gpadc;
+ struct regulator *regu;
+ struct pm2xxx_bm_data *bat;
+ struct mutex lock;
+ struct ab8500 *parent;
+ struct pm2xxx_charger_info ac;
+ struct pm2xxx_charger_platform_data *pdata;
+ struct workqueue_struct *charger_wq;
+ struct delayed_work check_vbat_work;
+ struct work_struct ac_work;
+ struct work_struct check_main_thermal_prot_work;
+ struct ux500_charger ac_chg;
+ struct pm2xxx_charger_event_flags flags;
+#ifdef CONFIG_PM2XXX_DEEP_DEBUG
+ struct dentry *ddbg_dir_pm2xxx;
+#endif
+};
+
+static const struct i2c_device_id pm2xxx_ident[] = {
+ { "pm2301", 0 },
+ { }
+};
+
+static int __pm2xxx_read(struct pm2xxx_charger *pm2, int reg, int num, u8 *data)
+{
+ int ret;
+
+ ret = i2c_smbus_read_i2c_block_data(pm2->config.pm2xxx_i2c, reg,
+ num, (u8 *)data);
+ if (ret < 0)
+ dev_err(pm2->dev, "Error reading %d regs at %d\n", num, reg);
+
+ return ret;
+}
+
+static int __pm2xxx_write(struct pm2xxx_charger *pm2, int reg, int num, u8 data)
+{
+ int ret;
+
+ ret = i2c_smbus_write_i2c_block_data(pm2->config.pm2xxx_i2c, reg,
+ num, &data);
+ if (ret < 0)
+ dev_err(pm2->dev, "Error writing %d regs at %d\n", num, reg);
+
+ return ret;
+
+}
+
+static int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg)
+{
+ u8 val;
+
+ __pm2xxx_read(pm2, reg, 1, &val);
+
+ return val;
+}
+
+static int pm2xxx_reg_write(struct pm2xxx_charger *pm2, int reg, u8 val)
+{
+ int ret;
+
+ ret = __pm2xxx_write(pm2, reg, 1, val);
+
+ return ret;
+}
+
+int pm2xxx_charging_enable_mngt(struct pm2xxx_charger *pm2)
+{
+ int ret;
+
+ /* Enable charging */
+ ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG2,
+ (PM2XXX_CH_AUTO_RESUME_EN | PM2XXX_CHARGER_ENA));
+
+ return ret;
+}
+
+int pm2xxx_charging_disable_mngt(struct pm2xxx_charger *pm2)
+{
+ int ret;
+
+ /* Disable charging */
+ ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG2,
+ (PM2XXX_CH_AUTO_RESUME_DIS | PM2XXX_CHARGER_DIS));
+
+ return ret;
+}
+
+int pm2xxx_charger_batt_therm_mngt(struct pm2xxx_charger *pm2, int val)
+{
+ queue_work(pm2->charger_wq, &pm2->check_main_thermal_prot_work);
+
+ return 0;
+}
+
+
+int pm2xxx_charger_die_therm_mngt(struct pm2xxx_charger *pm2, int val)
+{
+ queue_work(pm2->charger_wq, &pm2->check_main_thermal_prot_work);
+
+ return 0;
+}
+
+int pm2xxx_charger_ovv_mngt(struct pm2xxx_charger *pm2, int val)
+{
+ int ret = 0;
+
+ pm2->failure_input_ovv++;
+ if (pm2->failure_input_ovv < 4) {
+ ret = pm2xxx_charging_enable_mngt(pm2);
+ goto out;
+ } else {
+ pm2->failure_input_ovv = 0;
+ dev_err(pm2->dev, "Overvoltage detected\n");
+ pm2->flags.ovv = true;
+ power_supply_changed(&pm2->ac_chg.psy);
+ }
+
+out:
+ return ret;
+}
+
+int pm2xxx_charger_wd_exp_mngt(struct pm2xxx_charger *pm2, int val)
+{
+ dev_dbg(pm2->dev , "20 minutes watchdog occured\n");
+
+ pm2->ac.wd_expired = true;
+ power_supply_changed(&pm2->ac_chg.psy);
+
+ return 0;
+}
+
+int pm2xxx_charger_vbat_lsig_mngt(struct pm2xxx_charger *pm2, int val)
+{
+ switch (val) {
+ case PM2XXX_INT1_ITVBATLOWR:
+ dev_dbg(pm2->dev, "VBAT grows above VBAT_LOW level\n");
+ /* TODO: Handle this */
+ break;
+
+ case PM2XXX_INT1_ITVBATLOWF:
+ dev_dbg(pm2->dev, "VBAT drops below VBAT_LOW level\n");
+ /* TODO: Handle this */
+ break;
+
+ default:
+ dev_err(pm2->dev, "Unknown VBAT level\n");
+ }
+
+ return 0;
+}
+
+int pm2xxx_charger_bat_disc_mngt(struct pm2xxx_charger *pm2, int val)
+{
+ int ret;
+
+ dev_dbg(pm2->dev, "battery disconnected\n");
+
+ /* TODO: Update charging status */
+ ret = pm2xxx_charging_disable_mngt(pm2);
+
+ return ret;
+}
+
+static int pm2xxx_charger_detection(struct pm2xxx_charger *pm2)
+{
+ int ret = 0;
+
+ ret = pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT2);
+
+ if (ret < 0) {
+ dev_err(pm2->dev, "Charger detection failed\n");
+ return ret;
+ }
+
+ ret &= (PM2XXX_INT2_S_ITVPWR1PLUG | PM2XXX_INT2_S_ITVPWR2PLUG);
+
+ return ret;
+}
+
+static int pm2xxx_charger_itv_pwr_plug_mngt(struct pm2xxx_charger *pm2, int val)
+{
+
+ int ret;
+
+ /*
+ * Since we can't be sure that the events are received
+ * synchronously, we have the check if the main charger is
+ * connected by reading the interrupt source register.
+ */
+ ret = pm2xxx_charger_detection(pm2);
+
+ if (ret > 0) {
+ pm2->ac.charger_connected = 1;
+ pm2->ac_conn = true;
+
+ queue_work(pm2->charger_wq, &pm2->ac_work);
+ }
+
+
+ return ret;
+}
+
+static int pm2xxx_charger_itv_pwr_unplug_mngt(struct pm2xxx_charger *pm2,
+ int val)
+{
+ pm2->ac.charger_connected = 0;
+ queue_work(pm2->charger_wq, &pm2->ac_work);
+
+ return 0;
+}
+
+static int pm2_int_reg0(struct pm2xxx_charger *pm2)
+{
+ int ret;
+
+ if (pm2->pm2_int[0] &
+ (PM2XXX_INT1_ITVBATLOWR | PM2XXX_INT1_ITVBATLOWF)) {
+ ret = pm2xxx_charger_vbat_lsig_mngt(pm2, pm2->pm2_int[0] &
+ (PM2XXX_INT1_ITVBATLOWR | PM2XXX_INT1_ITVBATLOWF));
+ }
+
+ if (pm2->pm2_int[0] & PM2XXX_INT1_ITVBATDISCONNECT) {
+ ret = pm2xxx_charger_bat_disc_mngt(pm2,
+ PM2XXX_INT1_ITVBATDISCONNECT);
+ }
+
+ return ret;
+}
+
+static int pm2_int_reg1(struct pm2xxx_charger *pm2)
+{
+ int ret;
+
+ if (pm2->pm2_int[1] &
+ (PM2XXX_INT2_ITVPWR1PLUG | PM2XXX_INT2_ITVPWR2PLUG)) {
+ dev_dbg(pm2->dev , "Main charger plugged\n");
+ ret = pm2xxx_charger_itv_pwr_plug_mngt(pm2, pm2->pm2_int[1] &
+ (PM2XXX_INT2_ITVPWR1PLUG | PM2XXX_INT2_ITVPWR2PLUG));
+ }
+
+ if (pm2->pm2_int[1] &
+ (PM2XXX_INT2_ITVPWR1UNPLUG | PM2XXX_INT2_ITVPWR2UNPLUG)) {
+ dev_dbg(pm2->dev , "Main charger unplugged\n");
+ ret = pm2xxx_charger_itv_pwr_unplug_mngt(pm2, pm2->pm2_int[1] &
+ (PM2XXX_INT2_ITVPWR1UNPLUG |
+ PM2XXX_INT2_ITVPWR2UNPLUG));
+ }
+
+
+ return ret;
+}
+
+static int pm2_int_reg2(struct pm2xxx_charger *pm2)
+{
+ int ret;
+
+ if (pm2->pm2_int[2] & PM2XXX_INT3_ITAUTOTIMEOUTWD)
+ ret = pm2xxx_charger_wd_exp_mngt(pm2, pm2->pm2_int[2]);
+
+ if (pm2->pm2_int[2] & (PM2XXX_INT3_ITCHPRECHARGEWD |
+ PM2XXX_INT3_ITCHCCWD | PM2XXX_INT3_ITCHCVWD)) {
+ dev_dbg(pm2->dev,
+ "Watchdog occured for precharge, CC and CV charge\n");
+
+ /*TODO: handle watchdog expired */
+ }
+
+ return ret;
+}
+
+static int pm2_int_reg3(struct pm2xxx_charger *pm2)
+{
+ int ret;
+
+ if (pm2->pm2_int[3] & (PM2XXX_INT4_ITCHARGINGON)) {
+ dev_dbg(pm2->dev ,
+ "chargind operation has started\n");
+
+ /* TODO: Handle charging started */
+ }
+
+ if (pm2->pm2_int[3] & (PM2XXX_INT4_ITVRESUME)) {
+
+ dev_dbg(pm2->dev,
+ "battery discharged down to VResume threshold\n");
+
+ /* TODO: Handle recharging */
+ }
+
+ if (pm2->pm2_int[3] & (PM2XXX_INT4_ITBATTFULL)) {
+
+ dev_dbg(pm2->dev , "battery fully detected\n");
+
+ /* TODO: Handle battery full */
+ }
+
+ if (pm2->pm2_int[3] & (PM2XXX_INT4_ITCVPHASE)) {
+
+ dev_dbg(pm2->dev, "CV phase enter with 0.5C charging\n");
+
+ /* TODO: Handle CC to CV transition */
+ }
+
+ if (pm2->pm2_int[3] &
+ (PM2XXX_INT4_ITVPWR2OVV | PM2XXX_INT4_ITVPWR1OVV)) {
+ pm2->failure_case = VPWR_OVV;
+ ret = pm2xxx_charger_ovv_mngt(pm2, pm2->pm2_int[3] &
+ (PM2XXX_INT4_ITVPWR2OVV | PM2XXX_INT4_ITVPWR1OVV));
+ dev_dbg(pm2->dev, "VPWR/VSYSTEM overvoltage detected\n");
+ }
+
+ if (pm2->pm2_int[3] & (PM2XXX_INT4_S_ITBATTEMPCOLD |
+ PM2XXX_INT4_S_ITBATTEMPHOT)) {
+
+ pm2xxx_charger_batt_therm_mngt(pm2,
+ pm2->pm2_int[3] & (PM2XXX_INT4_S_ITBATTEMPCOLD |
+ PM2XXX_INT4_S_ITBATTEMPHOT));
+ dev_dbg(pm2->dev, "BTEMP is too Low/High\n");
+ }
+
+ return ret;
+}
+
+static int pm2_int_reg4(struct pm2xxx_charger *pm2)
+{
+ int ret;
+
+ if (pm2->pm2_int[4] & PM2XXX_INT5_ITVSYSTEMOVV) {
+ pm2->failure_case = VSYSTEM_OVV;
+ ret = pm2xxx_charger_ovv_mngt(pm2, pm2->pm2_int[4] &
+ PM2XXX_INT5_ITVSYSTEMOVV);
+ dev_dbg(pm2->dev, "VSYSTEM overvoltage detected\n");
+ }
+
+ if (pm2->pm2_int[4] & (PM2XXX_INT5_ITTHERMALWARNINGFALL |
+ PM2XXX_INT5_ITTHERMALWARNINGRISE |
+ PM2XXX_INT5_ITTHERMALSHUTDOWNFALL |
+ PM2XXX_INT5_ITTHERMALSHUTDOWNRISE)) {
+ dev_dbg(pm2->dev, "BTEMP die temperature is too Low/High\n");
+ ret = pm2xxx_charger_die_therm_mngt(pm2, pm2->pm2_int[4] &
+ (PM2XXX_INT5_ITTHERMALWARNINGFALL |
+ PM2XXX_INT5_ITTHERMALWARNINGRISE |
+ PM2XXX_INT5_ITTHERMALSHUTDOWNFALL |
+ PM2XXX_INT5_ITTHERMALSHUTDOWNRISE));
+ }
+
+ return ret;
+}
+
+static int pm2_int_reg5(struct pm2xxx_charger *pm2)
+{
+
+ if (pm2->pm2_int[5]
+ & (PM2XXX_INT6_ITVPWR2DROP | PM2XXX_INT6_ITVPWR1DROP)) {
+ dev_dbg(pm2->dev, "VMPWR drop to VBAT level\n");
+
+ /* TODO: handle this */
+ }
+
+ if (pm2->pm2_int[5] & (PM2XXX_INT6_ITVPWR2VALIDRISE |
+ PM2XXX_INT6_ITVPWR1VALIDRISE |
+ PM2XXX_INT6_ITVPWR2VALIDFALL |
+ PM2XXX_INT6_ITVPWR1VALIDFALL)) {
+ dev_dbg(pm2->dev, "Falling/Rising edge on WPWR1/2\n");
+
+ /* TODO: handle this */
+ }
+
+ return 0;
+}
+
+static irqreturn_t pm2xxx_irq_int(int irq, void *data)
+{
+ struct pm2xxx_charger *pm2 = data;
+ int ret, i;
+
+ for (i = 0; i < ARRAY_SIZE(pm2->pm2_int); i++) {
+ ret = pm2xxx_reg_read(pm2, pm2xxx_interrupt_registers[i]);
+ pm2->pm2_int[i] = ret;
+ }
+
+ pm2_int_reg0(pm2);
+ pm2_int_reg1(pm2);
+ pm2_int_reg2(pm2);
+ pm2_int_reg3(pm2);
+ pm2_int_reg4(pm2);
+ pm2_int_reg5(pm2);
+
+ return IRQ_HANDLED;
+}
+
+int pm2xxx_charger_led_en(struct pm2xxx_charger *pm2, int on)
+{
+ return 0;
+}
+
+static int pm2xxx_charger_get_ac_voltage(struct pm2xxx_charger *pm2)
+{
+ int vch = 0;
+
+ if (pm2->ac.charger_connected) {
+ vch = ab8500_gpadc_convert(pm2->gpadc, MAIN_CHARGER_V);
+ if (vch < 0)
+ dev_err(pm2->dev, "%s gpadc conv failed,\n", __func__);
+ }
+
+ return vch;
+}
+
+static int pm2xxx_charger_ac_cv(struct pm2xxx_charger *pm2)
+{
+ int ret = 0;
+
+ if (pm2->ac.charger_connected && pm2->ac.charger_online) {
+
+ ret = pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT4);
+ if (ret < 0) {
+ dev_err(pm2->dev, "%s pm2xxx read failed\n", __func__);
+ return 0;
+ }
+
+ if (ret & PM2XXX_INT4_S_ITCVPHASE)
+ ret = 1;
+ else
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int pm2xxx_charger_get_ac_current(struct pm2xxx_charger *pm2)
+{
+ int ich = 0;
+
+ if (pm2->ac.charger_online) {
+ ich = ab8500_gpadc_convert(pm2->gpadc, MAIN_CHARGER_C);
+ if (ich < 0)
+ dev_err(pm2->dev, "%s gpadc conv failed\n", __func__);
+ }
+
+ return ich;
+}
+
+static int pm2xxx_current_to_regval(int curr)
+{
+ int i;
+
+ if (curr < pm2xxx_charger_current_map[0])
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(pm2xxx_charger_current_map); i++) {
+ if (curr < pm2xxx_charger_current_map[i])
+ return i - 1;
+ }
+
+ i = ARRAY_SIZE(pm2xxx_charger_current_map) - 1;
+ if (curr == pm2xxx_charger_current_map[i])
+ return i;
+ else
+ return -1;
+}
+
+static int pm2xxx_voltage_to_regval(int curr)
+{
+ int i;
+
+ if (curr < pm2xxx_charger_voltage_map[0])
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(pm2xxx_charger_voltage_map); i++) {
+ if (curr < pm2xxx_charger_voltage_map[i])
+ return i - 1;
+ }
+
+ i = ARRAY_SIZE(pm2xxx_charger_voltage_map) - 1;
+ if (curr == pm2xxx_charger_voltage_map[i])
+ return i;
+ else
+ return -1;
+}
+
+static int pm2xxx_charger_update_charger_current(struct ux500_charger *charger,
+ int ich_out)
+{
+ int ret;
+ int curr_index;
+ struct pm2xxx_charger *pm2;
+
+ if (charger->psy.type == POWER_SUPPLY_TYPE_MAINS)
+ pm2 = to_pm2xxx_charger_ac_device_info(charger);
+ else
+ return -ENXIO;
+
+ curr_index = pm2xxx_current_to_regval(ich_out);
+ if (curr_index < 0) {
+ dev_err(pm2->dev,
+ "Charger current too high, "
+ "charging not started\n");
+ return -ENXIO;
+ }
+
+ ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG6);
+ if (ret >= 0) {
+ ret &= ~0xf;
+ ret |= curr_index;
+ ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG6, ret);
+ if (ret < 0) {
+ dev_err(pm2->dev,
+ "%s write failed\n", __func__);
+ }
+ }
+
+ return ret;
+}
+
+static int pm2xxx_charger_ac_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct pm2xxx_charger *pm2;
+
+ pm2 = to_pm2xxx_charger_ac_device_info(psy_to_ux500_charger(psy));
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_HEALTH:
+ if (pm2->flags.mainextchnotok)
+ val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+ else if (pm2->ac.wd_expired)
+ val->intval = POWER_SUPPLY_HEALTH_DEAD;
+ else if (pm2->flags.main_thermal_prot)
+ val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+ else
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = pm2->ac.charger_online;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = pm2->ac.charger_connected;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ pm2->ac.charger_voltage = pm2xxx_charger_get_ac_voltage(pm2);
+ val->intval = pm2->ac.charger_voltage * 1000;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+ pm2->ac.cv_active = pm2xxx_charger_ac_cv(pm2);
+ val->intval = pm2->ac.cv_active;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = pm2xxx_charger_get_ac_current(pm2) * 1000;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int pm2xxx_charging_init(struct pm2xxx_charger *pm2)
+{
+ int ret;
+
+ /* enable CC and CV watchdog */
+ ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG3,
+ (PM2XXX_CH_WD_CV_PHASE_60MIN | PM2XXX_CH_WD_CC_PHASE_60MIN));
+
+ /* enable precharge watchdog */
+ ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG4,
+ PM2XXX_CH_WD_PRECH_PHASE_60MIN);
+
+ return ret;
+}
+
+static int pm2xxx_charger_ac_en(struct ux500_charger *charger,
+ int enable, int vset, int iset)
+{
+ int ret;
+ int volt_index;
+ int curr_index;
+
+ struct pm2xxx_charger *pm2 = to_pm2xxx_charger_ac_device_info(charger);
+
+ if (enable) {
+ if (!pm2->ac.charger_connected) {
+ dev_dbg(pm2->dev, "AC charger not connected\n");
+ return -ENXIO;
+ }
+
+ dev_dbg(pm2->dev, "Enable AC: %dmV %dmA\n", vset, iset);
+ if (!pm2->vddadc_en_ac) {
+ regulator_enable(pm2->regu);
+ pm2->vddadc_en_ac = true;
+ }
+
+ pm2xxx_charging_init(pm2);
+
+ volt_index = pm2xxx_voltage_to_regval(vset);
+ curr_index = pm2xxx_current_to_regval(iset);
+
+ if (volt_index < 0 || curr_index < 0) {
+ dev_err(pm2->dev,
+ "Charger voltage or current too high, "
+ "charging not started\n");
+ return -ENXIO;
+ }
+
+ ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG8);
+ if (ret >= 0) {
+ ret &= ~0x3F;
+ ret |= volt_index;
+ ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG8, ret);
+
+ if (ret < 0) {
+ dev_err(pm2->dev,
+ "%s write failed\n", __func__);
+ goto error_occured;
+ }
+ }
+
+ ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG6);
+ if (ret >= 0) {
+ ret &= ~0xf;
+ ret |= curr_index;
+ ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG6, ret);
+ if (ret < 0) {
+ dev_err(pm2->dev,
+ "%s write failed\n", __func__);
+ goto error_occured;
+ }
+ }
+
+ if (!pm2->bat->enable_overshoot) {
+ ret = pm2xxx_reg_read(pm2, PM2XXX_LED_CTRL_REG);
+ if (ret >= 0) {
+ ret |= 0x01;
+ ret = pm2xxx_reg_write(pm2, PM2XXX_LED_CTRL_REG,
+ ret);
+ if (ret < 0)
+ goto error_occured;
+ }
+ }
+
+ ret = pm2xxx_charging_enable_mngt(pm2);
+ if (ret) {
+ dev_err(pm2->dev, "%s write failed\n", __func__);
+ return ret;
+ }
+
+ /*TODO: Add pm2301 Enable LED code */
+
+ pm2->ac.charger_online = 1;
+ } else {
+ /* TODO: add correct LED disable code */
+
+ pm2->ac.charger_online = 0;
+ pm2->ac.wd_expired = false;
+
+ /* Disable regulator if enabled */
+ if (pm2->vddadc_en_ac) {
+ regulator_disable(pm2->regu);
+ pm2->vddadc_en_ac = false;
+ }
+
+ ret = pm2xxx_charging_disable_mngt(pm2);
+ if (ret) {
+ dev_err(pm2->dev, "%s write failed\n", __func__);
+ return ret;
+ }
+
+ dev_dbg(pm2->dev, "PM2301: " "Disabled AC charging\n");
+ }
+ power_supply_changed(&pm2->ac_chg.psy);
+
+error_occured:
+ return ret;
+}
+
+static int pm2xxx_charger_watchdog_kick(struct ux500_charger *charger)
+{
+ int ret;
+ struct pm2xxx_charger *pm2;
+
+ if (charger->psy.type == POWER_SUPPLY_TYPE_MAINS)
+ pm2 = to_pm2xxx_charger_ac_device_info(charger);
+ else
+ return -ENXIO;
+
+ ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_WD_KICK, WD_TIMER);
+ if (ret)
+ dev_err(pm2->dev, "Failed to kick WD!\n");
+
+ return ret;
+}
+
+static void pm2xxx_charger_ac_work(struct work_struct *work)
+{
+ struct pm2xxx_charger *pm2 = container_of(work,
+ struct pm2xxx_charger, ac_work);
+
+
+ power_supply_changed(&pm2->ac_chg.psy);
+ sysfs_notify(&pm2->ac_chg.psy.dev->kobj, NULL, "present");
+};
+
+/* TODO: how thermal protection is handled in pm2301 */
+static void pm2xxx_charger_check_main_thermal_prot_work(
+ struct work_struct *work)
+{
+
+ /* TODO: check the thermal and die thermal protection */
+};
+
+static struct pm2xxx_irq pm2xxx_charger_irq[] = {
+ {"PM2XXX_IRQ_INT", pm2xxx_irq_int},
+};
+
+static int pm2xxx_wall_charger_resume(struct i2c_client *i2c_client)
+{
+ return 0;
+}
+
+static int pm2xxx_wall_charger_suspend(struct i2c_client *i2c_client,
+ pm_message_t state)
+{
+ return 0;
+}
+
+#ifdef CONFIG_PM2XXX_DEEP_DEBUG
+static int pm2xxx_ddbg_write(u32 addr, u32 val)
+{
+ int err;
+
+ err = pm2xxx_reg_write(pm2xxx_ddbg, addr, val);
+
+ if (err < 0)
+ return -EIO;
+
+ return 0;
+}
+
+static int pm2xxx_ddbg_read(u32 addr, u32 *val)
+{
+ *val = pm2xxx_reg_read(pm2xxx_ddbg, addr);
+
+ if (val < 0)
+ return -EIO;
+
+ return 0;
+}
+
+static struct ddbg_target ddbg_pm2xxx = {
+ .phyaddr = 0,
+ .reg = ddbg_pm2xxx_registers,
+ .read_reg = pm2xxx_ddbg_read,
+ .write_reg = pm2xxx_ddbg_write,
+};
+#endif
+
+static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
+ const struct i2c_device_id *id)
+{
+ struct pm2xxx_platform_data *pl_data = i2c_client->dev.platform_data;
+ struct pm2xxx_charger *pm2;
+ int ret = 0;
+
+ pm2 = kzalloc(sizeof(struct pm2xxx_charger), GFP_KERNEL);
+ if (!pm2)
+ return -ENOMEM;
+
+ /* get parent data */
+ pm2->dev = &i2c_client->dev;
+ pm2->gpadc = ab8500_gpadc_get();
+
+ /* get charger spcific platform data */
+ if (!pl_data->wall_charger) {
+ dev_err(pm2->dev, "no charger platform data supplied\n");
+ ret = -EINVAL;
+ goto free_device_info;
+ }
+
+ pm2->pdata = pl_data->wall_charger;
+
+ /* get battery specific platform data */
+ if (!pl_data->battery) {
+ dev_err(pm2->dev, "no battery platform data supplied\n");
+ ret = -EINVAL;
+ goto free_device_info;
+ }
+
+ pm2->bat = pl_data->battery;
+
+ if (!i2c_check_functionality(i2c_client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_READ_WORD_DATA)) {
+ ret = -ENODEV;
+ dev_info(pm2->dev, "pm2301 i2c_check_functionality failed\n");
+ goto free_device_info;
+ }
+
+ pm2->config.pm2xxx_i2c = i2c_client;
+ pm2->config.pm2xxx_id = (struct i2c_device_id *) id;
+ i2c_set_clientdata(i2c_client, pm2);
+
+ /* AC supply */
+ /* power_supply base class */
+ pm2->ac_chg.psy.name = pm2->pdata->label;
+ pm2->ac_chg.psy.type = POWER_SUPPLY_TYPE_MAINS;
+ pm2->ac_chg.psy.properties = pm2xxx_charger_ac_props;
+ pm2->ac_chg.psy.num_properties = ARRAY_SIZE(pm2xxx_charger_ac_props);
+ pm2->ac_chg.psy.get_property = pm2xxx_charger_ac_get_property;
+ pm2->ac_chg.psy.supplied_to = pm2->pdata->supplied_to;
+ pm2->ac_chg.psy.num_supplicants = pm2->pdata->num_supplicants;
+ /* pm2xxx_charger sub-class */
+ pm2->ac_chg.ops.enable = &pm2xxx_charger_ac_en;
+ pm2->ac_chg.ops.kick_wd = &pm2xxx_charger_watchdog_kick;
+ pm2->ac_chg.ops.update_curr = &pm2xxx_charger_update_charger_current;
+ pm2->ac_chg.max_out_volt = pm2xxx_charger_voltage_map[
+ ARRAY_SIZE(pm2xxx_charger_voltage_map) - 1];
+ pm2->ac_chg.max_out_curr = pm2xxx_charger_current_map[
+ ARRAY_SIZE(pm2xxx_charger_current_map) - 1];
+
+ /* Create a work queue for the charger */
+ pm2->charger_wq =
+ create_singlethread_workqueue("pm2xxx_charger_wq");
+ if (pm2->charger_wq == NULL) {
+ dev_err(pm2->dev, "failed to create work queue\n");
+ goto free_device_info;
+ }
+
+ /* Init work for charger detection */
+ INIT_WORK(&pm2->ac_work, pm2xxx_charger_ac_work);
+
+ /* Init work for checking HW status */
+ INIT_WORK(&pm2->check_main_thermal_prot_work,
+ pm2xxx_charger_check_main_thermal_prot_work);
+
+ /*
+ * VDD ADC supply needs to be enabled from this driver when there
+ * is a charger connected to avoid erroneous BTEMP_HIGH/LOW
+ * interrupts during charging
+ */
+ pm2->regu = regulator_get(pm2->dev, "vddadc");
+ if (IS_ERR(pm2->regu)) {
+ ret = PTR_ERR(pm2->regu);
+ dev_err(pm2->dev, "failed to get vddadc regulator\n");
+ goto free_charger_wq;
+ }
+
+ /* Register AC charger class */
+ ret = power_supply_register(pm2->dev, &pm2->ac_chg.psy);
+ if (ret) {
+ dev_err(pm2->dev, "failed to register AC charger\n");
+ goto free_regulator;
+ }
+
+ /* Register interrupts */
+ ret = request_threaded_irq(pm2->pdata->irq_number, NULL,
+ pm2xxx_charger_irq[0].isr,
+ pm2->pdata->irq_type,
+ pm2xxx_charger_irq[0].name, pm2);
+
+ if (ret != 0) {
+ dev_err(pm2->dev, "failed to request %s IRQ %d: %d\n"
+ , pm2xxx_charger_irq[0].name, pm2->pdata->irq_number, ret);
+ goto unregister_pm2xxx_charger;
+ }
+
+ /* TODO: I2C Read/Write will fail, if AC adaptor is not connected.
+ * fix the charger detection mechanism.
+ */
+ ret = pm2xxx_charger_detection(pm2);
+
+ if (ret > 0) {
+ pm2->ac.charger_connected = 1;
+ pm2->ac_conn = true;
+ power_supply_changed(&pm2->ac_chg.psy);
+ sysfs_notify(&pm2->ac_chg.psy.dev->kobj, NULL, "present");
+ }
+
+#ifdef CONFIG_PM2XXX_DEEP_DEBUG
+ pm2xxx_ddbg = pm2;
+ pm2->ddbg_dir_pm2xxx = debugfs_create_dir("pm2xxx", NULL);
+ if (pm2->ddbg_dir_pm2xxx == NULL) {
+ ret = -ENOMEM;
+ goto err_deep_debug;
+ }
+ ret = deep_debug_register(&ddbg_pm2xxx, pm2->ddbg_dir_pm2xxx);
+ if (ret)
+ goto err_deep_debug;
+#endif
+
+ return 0;
+
+#ifdef CONFIG_PM2XXX_DEEP_DEBUG
+err_deep_debug:
+ if (pm2->ddbg_dir_pm2xxx) {
+ debugfs_remove_recursive(pm2->ddbg_dir_pm2xxx);
+ pm2->ddbg_dir_pm2xxx = NULL;
+ }
+ dev_err(pm2->dev, "failed to create debugfs entries.\n");
+
+ /* disable interrupt */
+ free_irq(pm2->pdata->irq_number, pm2);
+#endif
+
+unregister_pm2xxx_charger:
+ /* unregister power supply */
+ power_supply_unregister(&pm2->ac_chg.psy);
+free_regulator:
+ /* disable the regulator */
+ regulator_put(pm2->regu);
+free_charger_wq:
+ destroy_workqueue(pm2->charger_wq);
+free_device_info:
+ kfree(pm2);
+ return ret;
+}
+
+static int __devexit pm2xxx_wall_charger_remove(struct i2c_client *i2c_client)
+{
+ struct pm2xxx_charger *pm2 = i2c_get_clientdata(i2c_client);
+
+ /* Disable AC charging */
+ pm2xxx_charger_ac_en(&pm2->ac_chg, false, 0, 0);
+
+ /* Disable interrupts */
+ free_irq(pm2->pdata->irq_number, pm2);
+
+ /* Delete the work queue */
+ destroy_workqueue(pm2->charger_wq);
+
+#ifdef CONFIG_PM2XXX_DEEP_DEBUG
+ deep_debug_unregister(&ddbg_pm2xxx);
+ if (pm2->ddbg_dir_pm2xxx)
+ debugfs_remove_recursive(pm2->ddbg_dir_pm2xxx);
+#endif
+
+ flush_scheduled_work();
+
+ /* disable the regulator */
+ regulator_put(pm2->regu);
+
+ power_supply_unregister(&pm2->ac_chg.psy);
+
+ kfree(pm2);
+
+ return 0;
+}
+
+static const struct i2c_device_id pm2xxx_id[] = {
+ { "pm2301", 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, pm2xxx_id);
+
+static struct i2c_driver pm2xxx_charger_driver = {
+ .probe = pm2xxx_wall_charger_probe,
+ .remove = __devexit_p(pm2xxx_wall_charger_remove),
+ .suspend = pm2xxx_wall_charger_suspend,
+ .resume = pm2xxx_wall_charger_resume,
+ .driver = {
+ .name = "pm2xxx-wall_charger",
+ .owner = THIS_MODULE,
+ },
+ .id_table = pm2xxx_id,
+};
+
+static int __init pm2xxx_charger_init(void)
+{
+ return i2c_add_driver(&pm2xxx_charger_driver);
+}
+
+static void __exit pm2xxx_charger_exit(void)
+{
+ i2c_del_driver(&pm2xxx_charger_driver);
+}
+
+subsys_initcall_sync(pm2xxx_charger_init);
+module_exit(pm2xxx_charger_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Rajkumar kasirajan, Olivier Launay");
+MODULE_ALIAS("platform:pm2xxx-charger");
+MODULE_DESCRIPTION("PM2xxx charger management driver");
+
diff --git a/include/linux/pm2301_charger.h b/include/linux/pm2301_charger.h
new file mode 100644
index 0000000..16bb1d3
--- /dev/null
+++ b/include/linux/pm2301_charger.h
@@ -0,0 +1,60 @@
+/*
+ * PM2301 charger driver.
+ *
+ * Copyright (C) 2012 ST Ericsson Corporation
+ *
+ * Contact: Olivier LAUNAY (olivier.launay at stericsson.com
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef __LINUX_PM2301_H
+#define __LINUX_PM2301_H
+
+/**
+ * struct pm2xxx_bm_charger_parameters - Charger specific parameters
+ * @ac_volt_max: maximum allowed AC charger voltage in mV
+ * @ac_curr_max: maximum allowed AC charger current in mA
+ */
+struct pm2xxx_bm_charger_parameters {
+ int ac_volt_max;
+ int ac_curr_max;
+};
+
+/**
+ * struct pm2xxx_bm_data - pm2xxx battery management data
+ * @enable_overshoot flag to enable VBAT overshoot control
+ * @chg_params charger parameters
+ */
+struct pm2xxx_bm_data {
+ bool enable_overshoot;
+ const struct pm2xxx_bm_charger_parameters *chg_params;
+};
+
+struct pm2xxx_charger_platform_data {
+ char **supplied_to;
+ size_t num_supplicants;
+ int i2c_bus;
+ const char *label;
+ int irq_number;
+ int irq_type;
+};
+
+struct pm2xxx_platform_data {
+ struct pm2xxx_charger_platform_data *wall_charger;
+ struct pm2xxx_bm_data *battery;
+};
+
+#endif /* __LINUX_PM2301_H */
--
1.7.9.5
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox