* [PATCH 1/4] ARM: cache-l2x0: Manage the errata at run time
From: srinidhi kasagar @ 2013-01-21 13:14 UTC (permalink / raw)
To: linux-arm-kernel
Make it possible to manage the errata by its own by using the
l2x0 ID register. This relieves the platforms from choosing the
Errata's at compile time
Signed-off-by: srinidhi kasagar <srinidhi.kasagar@stericsson.com>
---
arch/arm/include/asm/hardware/cache-l2x0.h | 2 +
arch/arm/mm/cache-l2x0.c | 77 +++++++++++++++-------------
2 files changed, 43 insertions(+), 36 deletions(-)
diff --git a/arch/arm/include/asm/hardware/cache-l2x0.h b/arch/arm/include/asm/hardware/cache-l2x0.h
index 3b2c40b..d5994ac 100644
--- a/arch/arm/include/asm/hardware/cache-l2x0.h
+++ b/arch/arm/include/asm/hardware/cache-l2x0.h
@@ -117,6 +117,8 @@ static inline int l2x0_of_init(u32 aux_val, u32 aux_mask)
}
#endif
+asmlinkage u32 l2x0_get_rtl_release(void);
+
struct l2x0_regs {
unsigned long phy_base;
unsigned long aux_ctrl;
diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c
index c2f3739..49058ac 100644
--- a/arch/arm/mm/cache-l2x0.c
+++ b/arch/arm/mm/cache-l2x0.c
@@ -49,6 +49,16 @@ struct l2x0_of_data {
static bool of_init = false;
+/*
+ * Identify ther RTL releases of l2x0 - This might help in applying
+ * the l2x0 errata's dynamically rather compile time options
+ */
+asmlinkage u32 l2x0_get_rtl_release(void)
+{
+ return readl_relaxed(l2x0_base + L2X0_CACHE_ID) &
+ L2X0_CACHE_ID_RTL_MASK;
+}
+
static inline void cache_wait_way(void __iomem *reg, unsigned long mask)
{
/* wait for cache operation by line or way to complete */
@@ -87,46 +97,41 @@ static inline void l2x0_inv_line(unsigned long addr)
writel_relaxed(addr, base + L2X0_INV_LINE_PA);
}
-#if defined(CONFIG_PL310_ERRATA_588369) || defined(CONFIG_PL310_ERRATA_727915)
-static inline void debug_writel(unsigned long val)
+static void debug_writel(unsigned long val)
{
- if (outer_cache.set_debug)
- outer_cache.set_debug(val);
+ u32 l2x0_revision = l2x0_get_rtl_release();
+
+ if (l2x0_revision == L2X0_CACHE_ID_RTL_R3P0 ||
+ l2x0_revision == L2X0_CACHE_ID_RTL_R2P0 ||
+ l2x0_revision == L2X0_CACHE_ID_RTL_R1P0 ||
+ l2x0_revision == L2X0_CACHE_ID_RTL_R0P0)
+ if (outer_cache.set_debug)
+ outer_cache.set_debug(val);
}
static void pl310_set_debug(unsigned long val)
{
writel_relaxed(val, l2x0_base + L2X0_DEBUG_CTRL);
}
-#else
-/* Optimised out for non-errata case */
-static inline void debug_writel(unsigned long val)
-{
-}
-
-#define pl310_set_debug NULL
-#endif
-
-#ifdef CONFIG_PL310_ERRATA_588369
-static inline void l2x0_flush_line(unsigned long addr)
-{
- void __iomem *base = l2x0_base;
-
- /* Clean by PA followed by Invalidate by PA */
- cache_wait(base + L2X0_CLEAN_LINE_PA, 1);
- writel_relaxed(addr, base + L2X0_CLEAN_LINE_PA);
- cache_wait(base + L2X0_INV_LINE_PA, 1);
- writel_relaxed(addr, base + L2X0_INV_LINE_PA);
-}
-#else
static inline void l2x0_flush_line(unsigned long addr)
{
void __iomem *base = l2x0_base;
- cache_wait(base + L2X0_CLEAN_INV_LINE_PA, 1);
- writel_relaxed(addr, base + L2X0_CLEAN_INV_LINE_PA);
+ u32 l2x0_revision = l2x0_get_rtl_release();
+
+ if (l2x0_revision == L2X0_CACHE_ID_RTL_R0P0 ||
+ l2x0_revision == L2X0_CACHE_ID_RTL_R1P0)
+ {
+ /* Clean by PA followed by Invalidate by PA */
+ cache_wait(base + L2X0_CLEAN_LINE_PA, 1);
+ writel_relaxed(addr, base + L2X0_CLEAN_LINE_PA);
+ cache_wait(base + L2X0_INV_LINE_PA, 1);
+ writel_relaxed(addr, base + L2X0_INV_LINE_PA);
+ } else {
+ cache_wait(base + L2X0_CLEAN_INV_LINE_PA, 1);
+ writel_relaxed(addr, base + L2X0_CLEAN_INV_LINE_PA);
+ }
}
-#endif
static void l2x0_cache_sync(void)
{
@@ -328,6 +333,7 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
int ways;
int way_size_shift = L2X0_WAY_SIZE_SHIFT;
const char *type;
+ u32 l2x0_revision = l2x0_get_rtl_release();
l2x0_base = base;
if (cache_id_part_number_from_dt)
@@ -348,10 +354,11 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
else
ways = 8;
type = "L310";
-#ifdef CONFIG_PL310_ERRATA_753970
- /* Unmapped register. */
- sync_reg_offset = L2X0_DUMMY_REG;
-#endif
+
+ if (l2x0_revision == L2X0_CACHE_ID_RTL_R3P0)
+ /* Unmapped register. */
+ sync_reg_offset = L2X0_DUMMY_REG;
+
if ((cache_id & L2X0_CACHE_ID_RTL_MASK) <= L2X0_CACHE_ID_RTL_R3P0)
outer_cache.set_debug = pl310_set_debug;
break;
@@ -594,8 +601,7 @@ static void __init pl310_of_setup(const struct device_node *np,
static void __init pl310_save(void)
{
- u32 l2x0_revision = readl_relaxed(l2x0_base + L2X0_CACHE_ID) &
- L2X0_CACHE_ID_RTL_MASK;
+ u32 l2x0_revision = l2x0_get_rtl_release();
l2x0_saved_regs.tag_latency = readl_relaxed(l2x0_base +
L2X0_TAG_LATENCY_CTRL);
@@ -657,8 +663,7 @@ static void pl310_resume(void)
writel_relaxed(l2x0_saved_regs.filter_start,
l2x0_base + L2X0_ADDR_FILTER_START);
- l2x0_revision = readl_relaxed(l2x0_base + L2X0_CACHE_ID) &
- L2X0_CACHE_ID_RTL_MASK;
+ l2x0_revision = l2x0_get_rtl_release();
if (l2x0_revision >= L2X0_CACHE_ID_RTL_R2P0) {
writel_relaxed(l2x0_saved_regs.prefetch_ctrl,
--
1.7.2.dirty
^ permalink raw reply related
* [PATCH 0/4] ARM: Manage the pl310 erratas in a dynamic way
From: srinidhi kasagar @ 2013-01-21 13:14 UTC (permalink / raw)
To: linux-arm-kernel
This series of patches attempts to manage the pl310 erratas
dynamically rather allowing platforms to choose them during
build time.
A bit of information which triggered the cause for this series is here:
http://lists.infradead.org/pipermail/linux-arm-kernel/2012-December/138066.html
Other alternate option perhaps may be device tree..
regards/srinidhi
srinidhi kasagar (4):
ARM: cache-l2x0: Manage the errata at run time
ARM: make the platforms not to select the l2x0 erratas
ARM: mach-omap2: apply the errata at run time rather
ARM: apply the l2x0 Errata 769419 at run time
arch/arm/include/asm/hardware/cache-l2x0.h | 2 +
arch/arm/kernel/process.c | 9 ++-
arch/arm/mach-omap2/Kconfig | 2 -
arch/arm/mach-omap2/sleep44xx.S | 25 ++++++++--
arch/arm/mach-tegra/Kconfig | 3 -
arch/arm/mach-ux500/Kconfig | 1 -
arch/arm/mach-vexpress/Kconfig | 1 -
arch/arm/mm/cache-l2x0.c | 77 +++++++++++++++-------------
8 files changed, 70 insertions(+), 50 deletions(-)
--
1.7.2.dirty
^ permalink raw reply
* [PATCH] ARM: shmobile: ipmmu: Add basic PMB support
From: Laurent Pinchart @ 2013-01-21 13:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358490910-30716-1-git-send-email-dhobsong@igel.co.jp>
Hi Damian,
Thank you for the patch.
On Friday 18 January 2013 15:35:10 Damian Hobson-Garcia wrote:
> The PMB can be used to remap 16, 64, 128 or 512 MiB pages from
> the 0x80000000-0xffffffff address range to anywhere in the
> 0x00000000-0x7fffffff range.
Isn't it 0x80000000 - 0xbfffffff to 0x00000000 - 0xffffffff ?
> It also has the ability to perform tiled-linear address translation,
> which can be used to access a memory buffer as a series of n x m tiles,
> useful for image encoding/decoding.
> Currently only the userspace API via character device is supported.
If I understand this correctly, you're allowing userspace to remap a virtual
address block to a physical address block without performing any sanity check.
Isn't that a major security issue ?
> All register access and hardware dependent functionality is
> provided by the IPMMU driver, which is shared with the IOMMU/TLB module.
>
> Signed-off-by: Damian Hobson-Garcia <dhobsong@igel.co.jp>
> ---
> This patch must be applied on top of the IPMMU patch series by
> Hideki EIRAKU. The code has been placed in the drivers/iommu directory for
> two reasons:
> 1) The PMB also performs hardware address translation
> 2) Since the PMB shares the same register address range as the
> shmobile IOMMU, the two functions are accessed through the same
> platform device, the driver for which is in drivers/iommu
>
> drivers/iommu/Kconfig | 14 ++
> drivers/iommu/Makefile | 1 +
> drivers/iommu/shmobile-ipmmu.c | 117 ++++++++++++-
> drivers/iommu/shmobile-ipmmu.h | 28 +++-
> drivers/iommu/shmobile-pmb.c | 362 +++++++++++++++++++++++++++++++++++++
> include/linux/ipmmu.h | 29 ++++
> 6 files changed, 544 insertions(+), 7 deletions(-)
> create mode 100644 drivers/iommu/shmobile-pmb.c
> create mode 100644 include/linux/ipmmu.h
>
> diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
> index d364494..44af0cb 100644
> --- a/drivers/iommu/Kconfig
> +++ b/drivers/iommu/Kconfig
> @@ -261,4 +261,18 @@ config SHMOBILE_IOMMU_L1SIZE
> default 256 if SHMOBILE_IOMMU_ADDRSIZE_64MB
> default 128 if SHMOBILE_IOMMU_ADDRSIZE_32MB
>
> +config SHMOBILE_PMB
> + bool "IPMMU PMB driver"
> + default n
> + select SHMOBILE_IPMMU
> + help
> + This enables the PMB interface of the IPMMU hardware module.
> + The PMB can be used to remap 16, 64, 128 or 512 MiB pages from
> + the 0x80000000-0xffffffff address range to anywhere in the
> + 0x00000000-0x7fffffff range.
> + PMB support can be used either with or without SHMOBILE IOMMU
> + support.
> +
> + If unsure, say N.
> +
> endif # IOMMU_SUPPORT
> diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
> index ef0e520..618238b 100644
> --- a/drivers/iommu/Makefile
> +++ b/drivers/iommu/Makefile
> @@ -15,3 +15,4 @@ obj-$(CONFIG_TEGRA_IOMMU_SMMU) += tegra-smmu.o
> obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iommu.o
> obj-$(CONFIG_SHMOBILE_IOMMU) += shmobile-iommu.o
> obj-$(CONFIG_SHMOBILE_IPMMU) += shmobile-ipmmu.o
> +obj-$(CONFIG_SHMOBILE_PMB) += shmobile-pmb.o
> diff --git a/drivers/iommu/shmobile-ipmmu.c b/drivers/iommu/shmobile-ipmmu.c
> index 8321f89..db2ae78 100644
> --- a/drivers/iommu/shmobile-ipmmu.c
> +++ b/drivers/iommu/shmobile-ipmmu.c
> @@ -14,15 +14,34 @@
> #include <linux/slab.h>
> #include <linux/platform_data/sh_ipmmu.h>
> #include "shmobile-ipmmu.h"
> +#include <linux/ipmmu.h>
>
> -#define IMCTR1 0x000
> -#define IMCTR2 0x004
> -#define IMASID 0x010
> -#define IMTTBR 0x014
> -#define IMTTBCR 0x018
> -
> +#define IMCTR1 0x00
> #define IMCTR1_TLBEN (1 << 0)
> #define IMCTR1_FLUSH (1 << 1)
> +#define IMCTR2 0x04
> +#define IMCTR2_PMBEN 0x01
> +#define IMASID 0x010
> +#define IMTTBR 0x014
> +#define IMTTBCR 0x018
> +#define IMPMBA_BASE 0x80
> +#define IMPBMA_V (1 << 8)
> +#define IMPMBD_BASE 0xC0
> +#define IMPBMD_V (1 << 8)
> +#define IMPBMD_SZ_16M 0x00
> +#define IMPBMD_SZ_64M 0x10
> +#define IMPBMD_SZ_128M 0x80
> +#define IMPBMD_SZ_512M 0x90
> +#define IMPBMD_BV (1 << 9)
> +#define IMPBMD_TBM_MASK (7 << 12)
> +#define IMPBMD_TBM_POS 12
> +#define IMPBMD_HBM_MASK (7 << 16)
> +#define IMPBMD_HBM_POS 16
> +#define IMPBMD_VBM_MASK (7 << 20)
> +#define IMPBMD_VBM_POS 20
> +
> +#define IMPBMA(x) (IMPMBA_BASE + 0x4*x)
> +#define IMPBMD(x) (IMPMBD_BASE + 0x4*x)
>
> static void ipmmu_reg_write(struct shmobile_ipmmu *ipmmu, unsigned long
> reg_off, unsigned long data)
> @@ -88,6 +107,91 @@ void ipmmu_tlb_set(struct shmobile_ipmmu *ipmmu,
> unsigned long phys, int size, mutex_unlock(&ipmmu->flush_lock);
> }
>
> +int ipmmu_pmb_enable(struct shmobile_ipmmu *ipmmu,
> + int enable)
> +{
> + ipmmu_reg_write(ipmmu, IMCTR2, enable ? IMCTR2_PMBEN : 0);
> + return 0;
This function could be void.
> +
> +}
> +int ipmmu_pmb_set_addr(struct shmobile_ipmmu *ipmmu,
> + int index,
The index could be an unsigned int.
> + unsigned long addr,
> + int enabled)
enabled could be a bool.
> +{
> +
> + if (!enabled) {
> + ipmmu_reg_write(ipmmu, IMPBMA(index), 0);
> + return 0;
> + }
> +
> + ipmmu_reg_write(ipmmu, IMPBMA(index), addr |
> + IMPBMD_V);
> + return 0;
This function could be void.
> +
Please avoid extra blank lines.
> +}
> +
> +int ipmmu_pmb_set_data(struct shmobile_ipmmu *ipmmu,
> + int index,
index could be an unsigned int.
> + struct ipmmu_pmb_info *info,
> + struct pmb_tile_info *tile)
info and tile are not modified, you can use const struct ...
> +{
> + int vbm, hbm, tbm;
> + int w, h;
> + unsigned long temp;
temp stores the value of a register, I think it would be better to use a type
with an explicit length (u32 in this case). I would also rename temp to val,
reg, regval or something similar.
> +
> + if (!info || !info->enabled) {
> + ipmmu_reg_write(ipmmu, IMPBMD(index), 0);
> + return 0;
> + }
> +
> + temp = info->paddr;
> +
> + switch (info->size_mb) {
> + case 16:
> + temp |= IMPBMD_SZ_16M;
> + break;
> + case 64:
> + temp |= IMPBMD_SZ_64M;
> + break;
> + case 128:
> + temp |= IMPBMD_SZ_128M;
> + break;
> + case 512:
> + temp |= IMPBMD_SZ_512M;
> + break;
> + default:
> + break;
Shouldn't you return an error here ?
> + }
> +
> + temp |= IMPBMD_V;
> +
> + if (!tile || !tile->enabled) {
> + ipmmu_reg_write(ipmmu, IMPBMD(index), temp);
> + return 0;
> + }
> +
> + w = tile->tile_width;
> + h = tile->tile_height;
> +
> + if (w & (w - 1) || h & (h - 1))
What about using the IS_ALIGNED macro here ?
if (!IS_ALIGNED(tile->tile_width) || !IS_ALIGNED(tile->tile_height))
You could then get rid of the w and h variables.
Shouldn't you also check that the width and height are inside the valid range
?
> + return -EINVAL;
> +
> + tbm = ilog2(tile->tile_width);
> + vbm = ilog2(tile->tile_height) - 1;
> + hbm = ilog2(tile->buffer_pitch) - tbm - 1;
> + tbm -= 4;
> +
> + temp |= (tbm << IMPBMD_TBM_POS) & IMPBMD_TBM_MASK;
> + temp |= (vbm << IMPBMD_VBM_POS) & IMPBMD_VBM_MASK;
> + temp |= (hbm << IMPBMD_HBM_POS) & IMPBMD_HBM_MASK;
> + temp |= IMPBMD_BV;
> + ipmmu_reg_write(ipmmu, IMPBMD(index),
> + temp);
No need for a line split here.
> + ipmmu_tlb_flush(ipmmu);
> + return 0;
> +}
> +
> static int ipmmu_probe(struct platform_device *pdev)
> {
> struct shmobile_ipmmu *ipmmu;
> @@ -118,6 +222,7 @@ static int ipmmu_probe(struct platform_device *pdev)
> ipmmu_reg_write(ipmmu, IMCTR1, 0x0); /* disable TLB */
> ipmmu_reg_write(ipmmu, IMCTR2, 0x0); /* disable PMB */
> ipmmu_iommu_init(ipmmu);
> + ipmmu->pmb_priv = ipmmu_pmb_init(ipmmu);
> return 0;
> }
>
> diff --git a/drivers/iommu/shmobile-ipmmu.h b/drivers/iommu/shmobile-ipmmu.h
> index 6270e7c..ecc4211 100644
> --- a/drivers/iommu/shmobile-ipmmu.h
> +++ b/drivers/iommu/shmobile-ipmmu.h
> @@ -11,6 +11,8 @@
> #define __SHMOBILE_IPMMU_H__
>
> struct dma_iommu_mapping;
> +struct pmb_tile_info;
> +struct ipmmu_pmb_info;
>
> struct shmobile_ipmmu {
> struct device *dev;
> @@ -20,10 +22,12 @@ struct shmobile_ipmmu {
> struct dma_iommu_mapping *iommu_mapping;
> const char * const *dev_names;
> unsigned int num_dev_names;
> + void *pmb_priv;
> };
>
> -#ifdef CONFIG_SHMOBILE_IPMMU_TLB
> void ipmmu_tlb_flush(struct shmobile_ipmmu *ipmmu);
> +
> +#ifdef CONFIG_SHMOBILE_IPMMU_TLB
> void ipmmu_tlb_set(struct shmobile_ipmmu *ipmmu, unsigned long phys, int
> size, int asid);
> int ipmmu_iommu_init(struct shmobile_ipmmu *ipmmu);
> @@ -34,4 +38,26 @@ static int ipmmu_iommu_init(struct shmobile_ipmmu *ipmmu)
> }
> #endif
>
> +#ifdef CONFIG_SHMOBILE_PMB
> +/* Access functions used by PMB device */
> +/*void handle_free(struct device *dev, unsigned long handle, int size_mb);
> +unsigned long handle_alloc(struct device *dev, int size_mb);*/
> +int ipmmu_pmb_set_addr(struct shmobile_ipmmu *ipmmu,
> + int index, unsigned long addr,
> + int enabled);
> +int ipmmu_pmb_set_data(struct shmobile_ipmmu *ipmmu, int index,
> + struct ipmmu_pmb_info *info,
> + struct pmb_tile_info *tile);
> +int ipmmu_pmb_enable(struct shmobile_ipmmu *ipmmu, int index);
> +/* PMB initialization */
> +void *ipmmu_pmb_init(struct shmobile_ipmmu *ipmmu);
> +void ipmmu_pmb_deinit(void *pmb_priv);
> +#else
> +static inline void *ipmmu_pmb_init(struct device *dev)
> +{
> + return NULL;
> +}
> +static inline void ipmmu_pmb_deinit(void *pmb_priv) { }
> +#endif
> +
> #endif /* __SHMOBILE_IPMMU_H__ */
> diff --git a/drivers/iommu/shmobile-pmb.c b/drivers/iommu/shmobile-pmb.c
> new file mode 100644
> index 0000000..464af0b
> --- /dev/null
> +++ b/drivers/iommu/shmobile-pmb.c
> @@ -0,0 +1,362 @@
> +/*
> + * IPMMU-PMB driver
> + * Copyright (C) 2012 Damian Hobson-Garcia
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 2 of the License.
> + *
> + * 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 + *
> + */
> +
> +#include <linux/platform_device.h>
> +#include <linux/io.h>
> +#include <linux/err.h>
> +#include <linux/export.h>
> +#include <linux/cdev.h>
> +#include <linux/fs.h>
> +#include <linux/slab.h>
> +#include <linux/ipmmu.h>
> +#include "shmobile-ipmmu.h"
> +#include <linux/uaccess.h>
Please sort header files alphabetically, and move the #include "" at the end.
> +
> +#define PMB_DEVICE_NAME "pmb"
> +
> +#define PMB_NR 16
> +/* the smallest size that can be reserverd in the pmb */
> +#define PMB_GRANULARITY (16 << 20)
> +#define PMB_START_ADDR 0x80000000
> +#define PMB_SIZE 0x40000000
> +#define NUM_BITS(x) ((x) / PMB_GRANULARITY)
> +#define NR_BITMAPS ((NUM_BITS(PMB_SIZE) + BITS_PER_LONG - 1) \
> + >> ilog2(BITS_PER_LONG))
Does ilog2(BITS_PER_LONG) resolve to a compile-time constant ?
> +
> +struct ipmmu_pmb_data {
> + struct ipmmu_pmb_priv *priv;
> + struct ipmmu_pmb_info info;
> + struct pmb_tile_info tile;
> + unsigned long handle;
> + int index;
> +};
> +
> +struct ipmmu_pmb_priv {
> + struct shmobile_ipmmu *ipmmu;
> + struct device *ipmmu_dev;
> + struct cdev cdev;
> + struct class *pmb_class;
> + dev_t pmb_dev;
> + unsigned long busy_pmbs;
> + struct mutex pmb_lock;
> + struct ipmmu_pmb_data pmbs[PMB_NR];
> + struct mutex alloc_lock;
> + unsigned long alloc_bitmaps[NR_BITMAPS];
> + int pmb_enabled;
bool could do here.
> +};
> +
> +static int valid_size(int size_mb)
> +{
> + switch (size_mb) {
> + case 16:
> + case 64:
> + case 128:
> + case 512:
> + return 1;
> + }
> + return 0;
> +
> +}
> +
> +static unsigned long alloc_handle(struct ipmmu_pmb_priv *priv,
> + int size_mb)
> +{
> + int i;
> + int idx;
> + unsigned long tmp_bitmap;
> + unsigned long alloc_mask;
> + unsigned long align_mask;
> + int alloc_bits;
> +
> + if (!valid_size(size_mb))
> + return -1;
> +
> + alloc_bits = NUM_BITS(size_mb << 20);
> + alloc_mask = alloc_bits < BITS_PER_LONG ?
> + (1 << alloc_bits) - 1 : -1;
> +
> +
> + align_mask = alloc_mask - 1;
> + for (i = BITS_PER_LONG >> 1; i >= alloc_bits; i = i >> 1)
> + align_mask = align_mask | (align_mask << i);
> +
> + mutex_lock(&priv->alloc_lock);
> + for (i = 0; i < NR_BITMAPS; i++) {
> + tmp_bitmap = priv->alloc_bitmaps[i];
> + tmp_bitmap |= align_mask;
> + idx = 0;
> + while (idx < BITS_PER_LONG) {
> + idx = find_next_zero_bit(&tmp_bitmap, BITS_PER_LONG,
> + idx);
> + if (!((alloc_mask << idx) & priv->alloc_bitmaps[i]) ||
> + (idx == BITS_PER_LONG))
> + break;
> + idx++;
> + }
> + if (idx < BITS_PER_LONG)
> + break;
> + }
> + if (i == NR_BITMAPS) {
> + mutex_unlock(&priv->alloc_lock);
> + return 0;
> + }
> +
> + priv->alloc_bitmaps[i] |= (alloc_mask << idx);
> + mutex_unlock(&priv->alloc_lock);
> +
> + return PMB_START_ADDR + (i * BITS_PER_LONG + idx) * PMB_GRANULARITY;
> +}
> +
> +void free_handle(struct ipmmu_pmb_priv *priv,
> + unsigned long handle,
> + int size_mb)
> +{
> + int idx;
> + unsigned long offset;
> + unsigned long alloc_bits;
> + unsigned long alloc_mask;
> +
> + alloc_bits = NUM_BITS(size_mb << 20);
> + alloc_mask = alloc_bits < BITS_PER_LONG ?
> + (1 << alloc_bits) - 1 : -1;
> + offset = handle - PMB_START_ADDR;
> + offset /= PMB_GRANULARITY;
> + idx = offset & (BITS_PER_LONG - 1);
> + offset = offset / BITS_PER_LONG;
> + mutex_lock(&priv->alloc_lock);
> + priv->alloc_bitmaps[offset] &= ~(alloc_mask << idx);
> + mutex_unlock(&priv->alloc_lock);
> +}
> +
> +static int set_pmb(struct ipmmu_pmb_data *data,
> + struct ipmmu_pmb_info *info)
> +{
> + struct ipmmu_pmb_priv *priv = data->priv;
> + unsigned long data_mask;
> + int err;
> +
> + if (!info->enabled) {
> + if (data->handle) {
> + free_handle(priv, data->handle,
> + data->info.size_mb);
> + data->handle = 0;
> + }
> + data->info = *info;
> + ipmmu_pmb_set_data(priv->ipmmu, data->index, NULL, NULL);
> + ipmmu_pmb_set_addr(priv->ipmmu, data->index, 0, 0);
> + ipmmu_tlb_flush(priv->ipmmu);
> + return 0;
> + }
> +
> + if (data->info.enabled) {
> + err = -EBUSY;
> + goto err_out;
> + }
> +
> + data_mask = ~((info->size_mb) - 1);
> +
> + if (info->paddr & ~(data_mask)) {
> + err = -EINVAL;
> + goto err_out;
> + }
> +
> + data->handle = alloc_handle(priv, info->size_mb);
> +
> + if (!data->handle) {
> + err = -ENOMEM;
> + goto err_out;
> + }
> +
> + data->info = *info;
> +
> + ipmmu_pmb_set_addr(priv->ipmmu, data->index, data->handle, 1);
> + ipmmu_pmb_set_data(priv->ipmmu, data->index, &data->info,
> + &data->tile);
> +
> + if (!data->priv->pmb_enabled) {
> + ipmmu_pmb_enable(priv->ipmmu, 1);
> + data->priv->pmb_enabled = 1;
> + }
> +
> + ipmmu_tlb_flush(priv->ipmmu);
> +
> + return 0;
> +
> +err_out:
> + info->enabled = 0;
> + return err;
> +}
> +
> +static int set_tile(struct ipmmu_pmb_data *data,
> + struct pmb_tile_info *tile)
> +{
> + struct ipmmu_pmb_priv *priv = data->priv;
> + data->tile = *tile;
> + return ipmmu_pmb_set_data(priv->ipmmu, data->index, &data->info,
> + &data->tile);
> +}
> +
> +static int ipmmu_pmb_open(struct inode *inode, struct file *filp)
> +{
> + struct ipmmu_pmb_priv *priv;
> + int idx;
> + priv = container_of(inode->i_cdev, struct ipmmu_pmb_priv,
> + cdev);
> +
> + mutex_lock(&priv->pmb_lock);
> + idx = find_first_zero_bit(&priv->busy_pmbs, PMB_NR);
> + if (idx == PMB_NR)
You should unlock the mutex here.
> + return -EBUSY;
> +
> + __set_bit(idx, &priv->busy_pmbs);
> + mutex_unlock(&priv->pmb_lock);
> + priv->pmbs[idx].index = idx;
> + priv->pmbs[idx].priv = priv;
You could set the index and priv fields for all PMBs at init time.
> + filp->private_data = &priv->pmbs[idx];
> + return 0;
> +}
> +
> +static int ipmmu_pmb_release(struct inode *inode, struct file *filp)
> +{
> + struct ipmmu_pmb_data *pmb;
> + pmb = filp->private_data;
> + if (pmb->info.enabled) {
> + pmb->info.enabled = 0;
> + set_pmb(pmb, &pmb->info);
> + }
> +
> + mutex_lock(&pmb->priv->pmb_lock);
> + __clear_bit(pmb->index, &pmb->priv->busy_pmbs);
> + mutex_unlock(&pmb->priv->pmb_lock);
> + return 0;
> +}
> +
> +static long ipmmu_pmb_ioctl(struct file *filp, unsigned int cmd_in,
> + unsigned long arg)
> +{
> + struct ipmmu_pmb_data *pmb;
> + struct ipmmu_pmb_info user_set;
> + struct pmb_tile_info user_tile;
> + long ret = -EINVAL;
> + pmb = filp->private_data;
> + switch (cmd_in) {
> + case IPMMU_GET_PMB:
> + ret = copy_to_user((char *)arg, &pmb->info, sizeof(pmb->info));
> + break;
> + case IPMMU_SET_PMB:
> + ret = copy_from_user(&user_set, (char *)arg, sizeof(user_set));
> + if (ret)
> + break;
> + ret = set_pmb(pmb, &user_set);
> + if (!ret)
> + pmb->info = user_set;
> + break;
> + case IPMMU_GET_PMB_HANDLE:
> + ret = copy_to_user((char *)arg, &pmb->handle,
> + sizeof(pmb->handle));
> + break;
> + case IPMMU_GET_PMB_TL:
> + ret = copy_to_user((char *)arg, &pmb->tile, sizeof(pmb->tile));
> + break;
> + case IPMMU_SET_PMB_TL:
> + ret = copy_from_user(&user_tile, (char *)arg,
> + sizeof(user_tile));
> + if (ret)
> + break;
> + ret = set_tile(pmb, &user_tile);
> + if (!ret)
> + pmb->tile = user_tile;
> + break;
> + }
> + return ret;
> +}
> +
> +static const struct file_operations ipmmu_pmb_fops = {
> + .owner = THIS_MODULE,
> + .open = ipmmu_pmb_open,
> + .release = ipmmu_pmb_release,
> + .unlocked_ioctl = ipmmu_pmb_ioctl,
> +};
> +
> +void ipmmu_pmb_deinit(void *arg)
> +{
> + struct ipmmu_pmb_priv *priv = arg;
> +
> + if (!priv || IS_ERR(priv))
> + return;
> +
> + cdev_del(&priv->cdev);
> + device_destroy(priv->pmb_class, priv->pmb_dev);
> + unregister_chrdev_region(priv->pmb_dev, 1);
> + class_destroy(priv->pmb_class);
> + kfree(priv);
> +}
> +
> +void *ipmmu_pmb_init(struct shmobile_ipmmu *ipmmu)
> +{
> + int err = -ENOENT;
> + struct ipmmu_pmb_priv *priv;
> +
> + priv = kzalloc(sizeof(struct ipmmu_pmb_priv), GFP_KERNEL);
Please use the devm_* allocation functions.
> + if (!priv) {
> + dev_err(ipmmu->dev, "cannot allocate device data\n");
> + return ERR_PTR(-ENOMEM);
> + }
> +
> + priv->ipmmu = ipmmu;
> + priv->ipmmu_dev = ipmmu->dev;
> +
> + mutex_init(&priv->pmb_lock);
> + mutex_init(&priv->alloc_lock);
> +
> + priv->pmb_class = class_create(THIS_MODULE, "ipmmu-pmb");
> + if (!priv->pmb_class)
> + goto free_priv;
> +
> + err = alloc_chrdev_region(&priv->pmb_dev, 0, 1, PMB_DEVICE_NAME);
> + if (err) {
> + dev_err(ipmmu->dev, "cannot allocate device num\n");
> + goto destroy_class;
> + }
> +
> + if (!device_create(priv->pmb_class, ipmmu->dev, priv->pmb_dev, priv,
> + "pmb"))
> + goto unregister_region;
> +
> + cdev_init(&priv->cdev, &ipmmu_pmb_fops);
> + priv->cdev.owner = THIS_MODULE;
> + priv->cdev.ops = &ipmmu_pmb_fops;
> + err = cdev_add(&priv->cdev, priv->pmb_dev, 1);
> + if (err) {
> + dev_err(ipmmu->dev, "cannot add ipmmu_pmb device\n");
> + goto destroy_device;
> + }
> +
> + return priv;
> +
> +destroy_device:
> + device_destroy(priv->pmb_class, priv->pmb_dev);
> +unregister_region:
> + unregister_chrdev_region(priv->pmb_dev, 1);
> +destroy_class:
> + class_destroy(priv->pmb_class);
> +free_priv:
> + kfree(priv);
> + return ERR_PTR(err);
> +}
> diff --git a/include/linux/ipmmu.h b/include/linux/ipmmu.h
> new file mode 100644
> index 0000000..4d31122
> --- /dev/null
> +++ b/include/linux/ipmmu.h
> @@ -0,0 +1,29 @@
> +#ifndef __LINUX_IPMMU_PMB_H__
> +#define __LINUX_IPMMU_PMB_H__
> +
> +struct ipmmu_pmb_info {
> + int enabled;
> + unsigned long paddr;
> + int size_mb;
> +};
> +
> +struct pmb_tile_info {
> + int tile_width;
> + int tile_height;
> + int buffer_pitch;
> + int enabled;
> +};
> +
> +/* IOCTL commands. */
> +
> +#define IPMMU_SET_PMB _IOW('S', 37, struct ipmmu_pmb_phys *)
> +#define IPMMU_GET_PMB _IOR('S', 38, struct ipmmu_pmb_phys *)
> +#define IPMMU_GET_PMB_HANDLE _IOR('S', 39, unsigned long *)
> +#define IPMMU_SET_PMB_TL _IOW('S', 41, struct ipmmu_pmb_tile_info *)
> +#define IPMMU_GET_PMB_TL _IOR('S', 42, struct ipmmu_pmb_tile_info *)
> +
> +#ifdef __kernel
> +
> +#endif /* __kernel */
> +
> +#endif /* __LINUX_IPMMU_PMB_H__ */
--
Regards,
Laurent Pinchart
^ permalink raw reply
* [PATCH 7/8] ARM: ux500: Use the GPIO regulator framework for SDI0's 'en' and 'vsel'
From: Lee Jones @ 2013-01-21 13:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAPDyKFo2VbEfFXe_HYTk9KyzbDgTa3Aj_kTC-2tYUJ4TMMxaJw@mail.gmail.com>
On Thu, 13 Dec 2012, Ulf Hansson wrote:
> On 13 December 2012 14:22, Lee Jones <lee.jones@linaro.org> wrote:
> > To prevent lots of unnecessary call-backs into platform code, we're
> > now using the GPIO regulator framework to control the 'enable' (en)
> > and 'voltage select' (vsel) GPIO pins which in turn control the
> > MMCI's secondary regulator settings. This already works with Device
> > Tree, but when booting with ATAGs we need to register it as a
> > platform device.
> >
> > Signed-off-by: Lee Jones <lee.jones@linaro.org>
What's the latest on these Ulf? Have they been taken in yet?
> > arch/arm/mach-ux500/board-mop500-regulators.c | 14 ++++++++
> > arch/arm/mach-ux500/board-mop500-regulators.h | 1 +
> > arch/arm/mach-ux500/board-mop500.c | 44 +++++++++++++++++++++++++
> > 3 files changed, 59 insertions(+)
> >
> > diff --git a/arch/arm/mach-ux500/board-mop500-regulators.c b/arch/arm/mach-ux500/board-mop500-regulators.c
> > index 2a17bc5..cb75405 100644
> > --- a/arch/arm/mach-ux500/board-mop500-regulators.c
> > +++ b/arch/arm/mach-ux500/board-mop500-regulators.c
> > @@ -28,6 +28,20 @@ struct regulator_init_data gpio_en_3v3_regulator = {
> > .consumer_supplies = gpio_en_3v3_consumers,
> > };
> >
> > +static struct regulator_consumer_supply sdi0_reg_consumers[] = {
> > + REGULATOR_SUPPLY("vqmmc", "sdi0"),
> > +};
> > +
> > +struct regulator_init_data sdi0_reg_init_data = {
> > + .constraints = {
> > + .min_uV = 1800000,
> > + .max_uV = 2900000,
> > + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE|REGULATOR_CHANGE_STATUS,
> > + },
> > + .num_consumer_supplies = ARRAY_SIZE(sdi0_reg_consumers),
> > + .consumer_supplies = sdi0_reg_consumers,
> > +};
> > +
> > /*
> > * TPS61052 regulator
> > */
> > diff --git a/arch/arm/mach-ux500/board-mop500-regulators.h b/arch/arm/mach-ux500/board-mop500-regulators.h
> > index 78a0642..0c79d90 100644
> > --- a/arch/arm/mach-ux500/board-mop500-regulators.h
> > +++ b/arch/arm/mach-ux500/board-mop500-regulators.h
> > @@ -19,5 +19,6 @@ ab8500_regulator_reg_init[AB8500_NUM_REGULATOR_REGISTERS];
> > extern struct regulator_init_data ab8500_regulators[AB8500_NUM_REGULATORS];
> > extern struct regulator_init_data tps61052_regulator;
> > extern struct regulator_init_data gpio_en_3v3_regulator;
> > +extern struct regulator_init_data sdi0_reg_init_data;
> >
> > #endif
> > diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c
> > index daa4237..e934176 100644
> > --- a/arch/arm/mach-ux500/board-mop500.c
> > +++ b/arch/arm/mach-ux500/board-mop500.c
> > @@ -24,6 +24,8 @@
> > #include <linux/mfd/abx500/ab8500.h>
> > #include <linux/regulator/ab8500.h>
> > #include <linux/regulator/fixed.h>
> > +#include <linux/regulator/driver.h>
> > +#include <linux/regulator/gpio-regulator.h>
> > #include <linux/mfd/tc3589x.h>
> > #include <linux/mfd/tps6105x.h>
> > #include <linux/mfd/abx500/ab8500-gpio.h>
> > @@ -91,6 +93,37 @@ static struct platform_device snowball_gpio_en_3v3_regulator_dev = {
> > },
> > };
> >
> > +/* Dynamically populated. */
> > +static struct gpio sdi0_reg_gpios[] = {
> > + { 0, GPIOF_OUT_INIT_LOW, "mmci_vsel" },
> > +};
> > +
> > +static struct gpio_regulator_state sdi0_reg_states[] = {
> > + { .value = 2900000, .gpios = (0 << 0) },
> > + { .value = 1800000, .gpios = (1 << 0) },
> > +};
> > +
> > +static struct gpio_regulator_config sdi0_reg_info = {
> > + .supply_name = "ext-mmc-level-shifter",
> > + .gpios = sdi0_reg_gpios,
> > + .nr_gpios = ARRAY_SIZE(sdi0_reg_gpios),
> > + .states = sdi0_reg_states,
> > + .nr_states = ARRAY_SIZE(sdi0_reg_states),
> > + .type = REGULATOR_VOLTAGE,
> > + .enable_high = 1,
> > + .enabled_at_boot = 0,
> > + .init_data = &sdi0_reg_init_data,
> > + .startup_delay = 100,
> > +};
> > +
> > +static struct platform_device sdi0_regulator = {
> > + .name = "gpio-regulator",
> > + .id = -1,
> > + .dev = {
> > + .platform_data = &sdi0_reg_info,
> > + },
> > +};
> > +
> > static struct ab8500_gpio_platform_data ab8500_gpio_pdata = {
> > .gpio_base = MOP500_AB8500_PIN_GPIO(1),
> > .irq_base = MOP500_AB8500_VIR_GPIO_IRQ_BASE,
> > @@ -440,6 +473,7 @@ static struct hash_platform_data u8500_hash1_platform_data = {
> > /* add any platform devices here - TODO */
> > static struct platform_device *mop500_platform_devs[] __initdata = {
> > &mop500_gpio_keys_device,
> > + &sdi0_regulator,
> > };
> >
> > #ifdef CONFIG_STE_DMA40
> > @@ -581,6 +615,7 @@ static struct platform_device *snowball_platform_devs[] __initdata = {
> > &snowball_key_dev,
> > &snowball_sbnet_dev,
> > &snowball_gpio_en_3v3_regulator_dev,
> > + &sdi0_regulator,
> > };
> >
> > static void __init mop500_init_machine(void)
> > @@ -591,6 +626,9 @@ static void __init mop500_init_machine(void)
> >
> > mop500_gpio_keys[0].gpio = GPIO_PROX_SENSOR;
> >
> > + sdi0_reg_info.enable_gpio = GPIO_SDMMC_EN;
> > + sdi0_reg_info.gpios[0].gpio = GPIO_SDMMC_1V8_3V_SEL;
> > +
> > mop500_pinmaps_init();
> > parent = u8500_init_devices(&ab8500_platdata);
> >
> > @@ -623,6 +661,9 @@ static void __init snowball_init_machine(void)
> > struct device *parent = NULL;
> > int i;
> >
> > + sdi0_reg_info.enable_gpio = SNOWBALL_SDMMC_EN_GPIO;
> > + sdi0_reg_info.gpios[0].gpio = SNOWBALL_SDMMC_1V8_3V_GPIO;
> > +
> > snowball_pinmaps_init();
> > parent = u8500_init_devices(&ab8500_platdata);
> >
> > @@ -655,6 +696,9 @@ static void __init hrefv60_init_machine(void)
> > */
> > mop500_gpio_keys[0].gpio = HREFV60_PROX_SENSE_GPIO;
> >
> > + sdi0_reg_info.enable_gpio = HREFV60_SDMMC_EN_GPIO;
> > + sdi0_reg_info.gpios[0].gpio = HREFV60_SDMMC_1V8_3V_GPIO;
> > +
> > hrefv60_pinmaps_init();
> > parent = u8500_init_devices(&ab8500_platdata);
> >
> >
>
>
> Acked-by: Ulf Hansson <ulf.hansson@linaro.org>
--
Lee Jones
Linaro ST-Ericsson Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply
* [PATCH 8/8] ARM: ux500: Remove traces of the ios_handler from platform code
From: Lee Jones @ 2013-01-21 13:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAPDyKFosXFxQCAeKNnmvM4rY5_otj=FfYdR1Vi_HBq_kk130eQ@mail.gmail.com>
On Fri, 14 Dec 2012, Ulf Hansson wrote:
> On 13 December 2012 14:22, Lee Jones <lee.jones@linaro.org> wrote:
> > Now MMCI on/off functionality is using the regulator framework
> > from the MMCI driver, there is no need to keep the ios_handler
> > laying around, duplicating functionality. So we're removing it.
> >
> > Acked-by: Linus Walleij <linus.walleij@linaro.org>
> > Signed-off-by: Lee Jones <lee.jones@linaro.org>
What's the latest on these Ulf? Have they been taken in yet?
> > ---
> > arch/arm/mach-ux500/board-mop500-sdi.c | 52 --------------------------------
> > 1 file changed, 52 deletions(-)
> >
> > diff --git a/arch/arm/mach-ux500/board-mop500-sdi.c b/arch/arm/mach-ux500/board-mop500-sdi.c
> > index 9c8e4a9..5a798d6 100644
> > --- a/arch/arm/mach-ux500/board-mop500-sdi.c
> > +++ b/arch/arm/mach-ux500/board-mop500-sdi.c
> > @@ -31,35 +31,6 @@
> > * SDI 0 (MicroSD slot)
> > */
> >
> > -/* GPIO pins used by the sdi0 level shifter */
> > -static int sdi0_en = -1;
> > -static int sdi0_vsel = -1;
> > -
> > -static int mop500_sdi0_ios_handler(struct device *dev, struct mmc_ios *ios)
> > -{
> > - switch (ios->power_mode) {
> > - case MMC_POWER_UP:
> > - case MMC_POWER_ON:
> > - /*
> > - * Level shifter voltage should depend on vdd to when deciding
> > - * on either 1.8V or 2.9V. Once the decision has been made the
> > - * level shifter must be disabled and re-enabled with a changed
> > - * select signal in order to switch the voltage. Since there is
> > - * no framework support yet for indicating 1.8V in vdd, use the
> > - * default 2.9V.
> > - */
> > - gpio_direction_output(sdi0_vsel, 0);
> > - gpio_direction_output(sdi0_en, 1);
> > - break;
> > - case MMC_POWER_OFF:
> > - gpio_direction_output(sdi0_vsel, 0);
> > - gpio_direction_output(sdi0_en, 0);
> > - break;
> > - }
> > -
> > - return 0;
> > -}
> > -
> > #ifdef CONFIG_STE_DMA40
> > struct stedma40_chan_cfg mop500_sdi0_dma_cfg_rx = {
> > .mode = STEDMA40_MODE_LOGICAL,
> > @@ -81,7 +52,6 @@ static struct stedma40_chan_cfg mop500_sdi0_dma_cfg_tx = {
> > #endif
> >
> > struct mmci_platform_data mop500_sdi0_data = {
> > - .ios_handler = mop500_sdi0_ios_handler,
> > .ocr_mask = MMC_VDD_29_30,
> > .f_max = 50000000,
> > .capabilities = MMC_CAP_4_BIT_DATA |
> > @@ -101,22 +71,6 @@ struct mmci_platform_data mop500_sdi0_data = {
> >
> > static void sdi0_configure(struct device *parent)
> > {
> > - int ret;
> > -
> > - ret = gpio_request(sdi0_en, "level shifter enable");
> > - if (!ret)
> > - ret = gpio_request(sdi0_vsel,
> > - "level shifter 1v8-3v select");
> > -
> > - if (ret) {
> > - pr_warning("unable to config sdi0 gpios for level shifter.\n");
> > - return;
> > - }
> > -
> > - /* Select the default 2.9V and enable level shifter */
> > - gpio_direction_output(sdi0_vsel, 0);
> > - gpio_direction_output(sdi0_en, 1);
> > -
> > /* Add the device, force v2 to subrevision 1 */
> > db8500_add_sdi0(parent, &mop500_sdi0_data, U8500_SDI_V2_PERIPHID);
> > }
> > @@ -124,8 +78,6 @@ static void sdi0_configure(struct device *parent)
> > void mop500_sdi_tc35892_init(struct device *parent)
> > {
> > mop500_sdi0_data.gpio_cd = GPIO_SDMMC_CD;
> > - sdi0_en = GPIO_SDMMC_EN;
> > - sdi0_vsel = GPIO_SDMMC_1V8_3V_SEL;
> > sdi0_configure(parent);
> > }
> >
> > @@ -264,8 +216,6 @@ void __init snowball_sdi_init(struct device *parent)
> > /* External Micro SD slot */
> > mop500_sdi0_data.gpio_cd = SNOWBALL_SDMMC_CD_GPIO;
> > mop500_sdi0_data.cd_invert = true;
> > - sdi0_en = SNOWBALL_SDMMC_EN_GPIO;
> > - sdi0_vsel = SNOWBALL_SDMMC_1V8_3V_GPIO;
> > sdi0_configure(parent);
> > }
> >
> > @@ -277,8 +227,6 @@ void __init hrefv60_sdi_init(struct device *parent)
> > db8500_add_sdi4(parent, &mop500_sdi4_data, U8500_SDI_V2_PERIPHID);
> > /* External Micro SD slot */
> > mop500_sdi0_data.gpio_cd = HREFV60_SDMMC_CD_GPIO;
> > - sdi0_en = HREFV60_SDMMC_EN_GPIO;
> > - sdi0_vsel = HREFV60_SDMMC_1V8_3V_GPIO;
> > sdi0_configure(parent);
> > /* WLAN SDIO channel */
> > db8500_add_sdi1(parent, &mop500_sdi1_data, U8500_SDI_V2_PERIPHID);
> >
>
> Acked-by: Ulf Hansson <ulf.hansson@linaro.org>
--
Lee Jones
Linaro ST-Ericsson Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply
* [PATCH 0/2] ARM: Exynos5 : Add Power domain device tree support for MFC and GSC
From: Prasanna Kumar @ 2013-01-21 13:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAPmaG0vQHJbp5tkHTPxEM3Oy1GASbN4asN-x+gtxb77jwpOK0g@mail.gmail.com>
Hello ,
Any comments for this patch set ?
> On Wed, Jan 9, 2013 at 5:45 PM, Prasanna Kumar <prasanna.ps@samsung.com> wrote:
>>
>> Hello all,
>>
>> This patch set adds device tree support for power domains (Gscaler and
>> MFC) of exynos5
>>
>> Prasanna Kumar (2):
>> arm: exynos5: Enable PM generic domain support in Kconfig
>> ARM: dts: exynos5: Set up power domain for MFC and G-scaler
>>
>> arch/arm/boot/dts/exynos5250.dtsi | 15 +++++++++++++++
>> arch/arm/mach-exynos/Kconfig | 1 +
>> 2 files changed, 16 insertions(+), 0 deletions(-)
>>
>> --
>> 1.7.5.4
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
>> the body of a message to majordomo at vger.kernel.org
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
>
>
>
> --
> Thanks
> Prasanna Kumar
--
Thanks
Prasanna Kumar
^ permalink raw reply
* [PATCH 4/4 v2] pinctrl/abx500: add AB8500 pinctrl driver
From: Linus Walleij @ 2013-01-21 13:01 UTC (permalink / raw)
To: linux-arm-kernel
From: Patrice Chotard <patrice.chotard@st.com>
This adds a subdriver for the AB8500 pinctrl portions.
Reviewed-by: Stephen Warren <swarren@nvidia.com>
Signed-off-by: Patrice Chotard <patrice.chotard@st.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
ChangeLog v1->v2:
- Move definition of init functions over to this patch from
the core patch so we don't have unimplemented functions defined
in the kernel.
- Drop a few "_ranges_" substrings wher unappropriate.
---
arch/arm/mach-ux500/Kconfig | 1 +
drivers/pinctrl/Kconfig | 4 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-ab8500.c | 485 +++++++++++++++++++++++++++++++++++++++
drivers/pinctrl/pinctrl-abx500.c | 3 +
drivers/pinctrl/pinctrl-abx500.h | 13 ++
6 files changed, 507 insertions(+)
create mode 100644 drivers/pinctrl/pinctrl-ab8500.c
diff --git a/arch/arm/mach-ux500/Kconfig b/arch/arm/mach-ux500/Kconfig
index 2c090d7..31f1b5a 100644
--- a/arch/arm/mach-ux500/Kconfig
+++ b/arch/arm/mach-ux500/Kconfig
@@ -19,6 +19,7 @@ config UX500_SOC_DB8500
select CPU_FREQ_TABLE if CPU_FREQ
select MFD_DB8500_PRCMU
select PINCTRL_DB8500
+ select PINCTRL_AB8500
select REGULATOR
select REGULATOR_DB8500_PRCMU
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index ba3038c..367556f 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -33,6 +33,10 @@ config PINCTRL_ABX500
help
Select this to enable the ABx500 family IC GPIO driver
+config PINCTRL_AB8500
+ bool "AB8500 pin controller driver"
+ depends on PINCTRL_ABX500 && ARCH_U8500
+
config PINCTRL_AT91
bool "AT91 pinctrl driver"
depends on OF
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index ead4fa7..aad5c93 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_PINCTRL) += devicetree.o
endif
obj-$(CONFIG_GENERIC_PINCONF) += pinconf-generic.o
obj-$(CONFIG_PINCTRL_ABX500) += pinctrl-abx500.o
+obj-$(CONFIG_PINCTRL_AB8500) += pinctrl-ab8500.o
obj-$(CONFIG_PINCTRL_AT91) += pinctrl-at91.o
obj-$(CONFIG_PINCTRL_BCM2835) += pinctrl-bcm2835.o
obj-$(CONFIG_PINCTRL_IMX) += pinctrl-imx.o
diff --git a/drivers/pinctrl/pinctrl-ab8500.c b/drivers/pinctrl/pinctrl-ab8500.c
new file mode 100644
index 0000000..c4a40a8
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-ab8500.c
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * Author: Patrice Chotard <patrice.chotard@stericsson.com> for 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/kernel.h>
+#include <linux/gpio.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include "pinctrl-abx500.h"
+
+/* All the pins that can be used for GPIO and some other functions */
+#define ABX500_GPIO(offset) (offset)
+
+#define AB8500_PIN_T10 ABX500_GPIO(1)
+#define AB8500_PIN_T9 ABX500_GPIO(2)
+#define AB8500_PIN_U9 ABX500_GPIO(3)
+#define AB8500_PIN_W2 ABX500_GPIO(4)
+/* hole */
+#define AB8500_PIN_Y18 ABX500_GPIO(6)
+#define AB8500_PIN_AA20 ABX500_GPIO(7)
+#define AB8500_PIN_W18 ABX500_GPIO(8)
+#define AB8500_PIN_AA19 ABX500_GPIO(9)
+#define AB8500_PIN_U17 ABX500_GPIO(10)
+#define AB8500_PIN_AA18 ABX500_GPIO(11)
+#define AB8500_PIN_U16 ABX500_GPIO(12)
+#define AB8500_PIN_W17 ABX500_GPIO(13)
+#define AB8500_PIN_F14 ABX500_GPIO(14)
+#define AB8500_PIN_B17 ABX500_GPIO(15)
+#define AB8500_PIN_F15 ABX500_GPIO(16)
+#define AB8500_PIN_P5 ABX500_GPIO(17)
+#define AB8500_PIN_R5 ABX500_GPIO(18)
+#define AB8500_PIN_U5 ABX500_GPIO(19)
+#define AB8500_PIN_T5 ABX500_GPIO(20)
+#define AB8500_PIN_H19 ABX500_GPIO(21)
+#define AB8500_PIN_G20 ABX500_GPIO(22)
+#define AB8500_PIN_G19 ABX500_GPIO(23)
+#define AB8500_PIN_T14 ABX500_GPIO(24)
+#define AB8500_PIN_R16 ABX500_GPIO(25)
+#define AB8500_PIN_M16 ABX500_GPIO(26)
+#define AB8500_PIN_J6 ABX500_GPIO(27)
+#define AB8500_PIN_K6 ABX500_GPIO(28)
+#define AB8500_PIN_G6 ABX500_GPIO(29)
+#define AB8500_PIN_H6 ABX500_GPIO(30)
+#define AB8500_PIN_F5 ABX500_GPIO(31)
+#define AB8500_PIN_G5 ABX500_GPIO(32)
+/* hole */
+#define AB8500_PIN_R17 ABX500_GPIO(34)
+#define AB8500_PIN_W15 ABX500_GPIO(35)
+#define AB8500_PIN_A17 ABX500_GPIO(36)
+#define AB8500_PIN_E15 ABX500_GPIO(37)
+#define AB8500_PIN_C17 ABX500_GPIO(38)
+#define AB8500_PIN_E16 ABX500_GPIO(39)
+#define AB8500_PIN_T19 ABX500_GPIO(40)
+#define AB8500_PIN_U19 ABX500_GPIO(41)
+#define AB8500_PIN_U2 ABX500_GPIO(42)
+
+/* indicates the highest GPIO number */
+#define AB8500_GPIO_MAX_NUMBER 42
+
+/*
+ * The names of the pins are denoted by GPIO number and ball name, even
+ * though they can be used for other things than GPIO, this is the first
+ * column in the table of the data sheet and often used on schematics and
+ * such.
+ */
+static const struct pinctrl_pin_desc ab8500_ranges_pins[] = {
+ PINCTRL_PIN(AB8500_PIN_T10, "GPIO1_T10"),
+ PINCTRL_PIN(AB8500_PIN_T9, "GPIO2_T9"),
+ PINCTRL_PIN(AB8500_PIN_U9, "GPIO3_U9"),
+ PINCTRL_PIN(AB8500_PIN_W2, "GPIO4_W2"),
+ /* hole */
+ PINCTRL_PIN(AB8500_PIN_Y18, "GPIO6_Y18"),
+ PINCTRL_PIN(AB8500_PIN_AA20, "GPIO7_AA20"),
+ PINCTRL_PIN(AB8500_PIN_W18, "GPIO8_W18"),
+ PINCTRL_PIN(AB8500_PIN_AA19, "GPIO9_AA19"),
+ PINCTRL_PIN(AB8500_PIN_U17, "GPIO10_U17"),
+ PINCTRL_PIN(AB8500_PIN_AA18, "GPIO11_AA18"),
+ PINCTRL_PIN(AB8500_PIN_U16, "GPIO12_U16"),
+ PINCTRL_PIN(AB8500_PIN_W17, "GPIO13_W17"),
+ PINCTRL_PIN(AB8500_PIN_F14, "GPIO14_F14"),
+ PINCTRL_PIN(AB8500_PIN_B17, "GPIO15_B17"),
+ PINCTRL_PIN(AB8500_PIN_F15, "GPIO16_F15"),
+ PINCTRL_PIN(AB8500_PIN_P5, "GPIO17_P5"),
+ PINCTRL_PIN(AB8500_PIN_R5, "GPIO18_R5"),
+ PINCTRL_PIN(AB8500_PIN_U5, "GPIO19_U5"),
+ PINCTRL_PIN(AB8500_PIN_T5, "GPIO20_T5"),
+ PINCTRL_PIN(AB8500_PIN_H19, "GPIO21_H19"),
+ PINCTRL_PIN(AB8500_PIN_G20, "GPIO22_G20"),
+ PINCTRL_PIN(AB8500_PIN_G19, "GPIO23_G19"),
+ PINCTRL_PIN(AB8500_PIN_T14, "GPIO24_T14"),
+ PINCTRL_PIN(AB8500_PIN_R16, "GPIO25_R16"),
+ PINCTRL_PIN(AB8500_PIN_M16, "GPIO26_M16"),
+ PINCTRL_PIN(AB8500_PIN_J6, "GPIO27_J6"),
+ PINCTRL_PIN(AB8500_PIN_K6, "GPIO28_K6"),
+ PINCTRL_PIN(AB8500_PIN_G6, "GPIO29_G6"),
+ PINCTRL_PIN(AB8500_PIN_H6, "GPIO30_H6"),
+ PINCTRL_PIN(AB8500_PIN_F5, "GPIO31_F5"),
+ PINCTRL_PIN(AB8500_PIN_G5, "GPIO32_G5"),
+ /* hole */
+ PINCTRL_PIN(AB8500_PIN_R17, "GPIO34_R17"),
+ PINCTRL_PIN(AB8500_PIN_W15, "GPIO35_W15"),
+ PINCTRL_PIN(AB8500_PIN_A17, "GPIO36_A17"),
+ PINCTRL_PIN(AB8500_PIN_E15, "GPIO37_E15"),
+ PINCTRL_PIN(AB8500_PIN_C17, "GPIO38_C17"),
+ PINCTRL_PIN(AB8500_PIN_E16, "GPIO39_E16"),
+ PINCTRL_PIN(AB8500_PIN_T19, "GPIO40_T19"),
+ PINCTRL_PIN(AB8500_PIN_U19, "GPIO41_U19"),
+ PINCTRL_PIN(AB8500_PIN_U2, "GPIO42_U2"),
+};
+
+/*
+ * Maps local GPIO offsets to local pin numbers
+ */
+static const struct abx500_pinrange ab8500_pinranges[] = {
+ ABX500_PINRANGE(1, 4, ABX500_ALT_A),
+ ABX500_PINRANGE(6, 4, ABX500_ALT_A),
+ ABX500_PINRANGE(10, 4, ABX500_DEFAULT),
+ ABX500_PINRANGE(14, 12, ABX500_ALT_A),
+ ABX500_PINRANGE(26, 1, ABX500_DEFAULT),
+ ABX500_PINRANGE(27, 6, ABX500_ALT_A),
+ ABX500_PINRANGE(34, 1, ABX500_ALT_A),
+ ABX500_PINRANGE(35, 1, ABX500_DEFAULT),
+ ABX500_PINRANGE(36, 7, ABX500_ALT_A),
+};
+
+/*
+ * Read the pin group names like this:
+ * sysclkreq2_d_1 = first groups of pins for sysclkreq2 on default function
+ *
+ * The groups are arranged as sets per altfunction column, so we can
+ * mux in one group at a time by selecting the same altfunction for them
+ * all. When functions require pins on different altfunctions, you need
+ * to combine several groups.
+ */
+
+/* default column */
+static const unsigned sysclkreq2_d_1_pins[] = { AB8500_PIN_T10 };
+static const unsigned sysclkreq3_d_1_pins[] = { AB8500_PIN_T9 };
+static const unsigned sysclkreq4_d_1_pins[] = { AB8500_PIN_U9 };
+static const unsigned sysclkreq6_d_1_pins[] = { AB8500_PIN_W2 };
+static const unsigned ycbcr0123_d_1_pins[] = { AB8500_PIN_Y18, AB8500_PIN_AA20,
+ AB8500_PIN_W18, AB8500_PIN_AA19};
+static const unsigned gpio10_d_1_pins[] = { AB8500_PIN_U17 };
+static const unsigned gpio11_d_1_pins[] = { AB8500_PIN_AA18 };
+static const unsigned gpio12_d_1_pins[] = { AB8500_PIN_U16 };
+static const unsigned gpio13_d_1_pins[] = { AB8500_PIN_W17 };
+static const unsigned pwmout1_d_1_pins[] = { AB8500_PIN_F14 };
+static const unsigned pwmout2_d_1_pins[] = { AB8500_PIN_B17 };
+static const unsigned pwmout3_d_1_pins[] = { AB8500_PIN_F15 };
+
+/* audio data interface 1*/
+static const unsigned adi1_d_1_pins[] = { AB8500_PIN_P5, AB8500_PIN_R5,
+ AB8500_PIN_U5, AB8500_PIN_T5 };
+/* USBUICC */
+static const unsigned usbuicc_d_1_pins[] = { AB8500_PIN_H19, AB8500_PIN_G20,
+ AB8500_PIN_G19 };
+static const unsigned sysclkreq7_d_1_pins[] = { AB8500_PIN_T14 };
+static const unsigned sysclkreq8_d_1_pins[] = { AB8500_PIN_R16 };
+static const unsigned gpio26_d_1_pins[] = { AB8500_PIN_M16 };
+/* Digital microphone 1 and 2 */
+static const unsigned dmic12_d_1_pins[] = { AB8500_PIN_J6, AB8500_PIN_K6 };
+/* Digital microphone 3 and 4 */
+static const unsigned dmic34_d_1_pins[] = { AB8500_PIN_G6, AB8500_PIN_H6 };
+/* Digital microphone 5 and 6 */
+static const unsigned dmic56_d_1_pins[] = { AB8500_PIN_F5, AB8500_PIN_G5 };
+static const unsigned extcpena_d_1_pins[] = { AB8500_PIN_R17 };
+static const unsigned gpio35_d_1_pins[] = { AB8500_PIN_W15 };
+/* APE SPI */
+static const unsigned apespi_d_1_pins[] = { AB8500_PIN_A17, AB8500_PIN_E15,
+ AB8500_PIN_C17, AB8500_PIN_E16};
+/* modem SDA/SCL */
+static const unsigned modsclsda_d_1_pins[] = { AB8500_PIN_T19, AB8500_PIN_U19 };
+static const unsigned sysclkreq5_d_1_pins[] = { AB8500_PIN_U2 };
+
+/* Altfunction A column */
+static const unsigned gpio1_a_1_pins[] = { AB8500_PIN_T10 };
+static const unsigned gpio2_a_1_pins[] = { AB8500_PIN_T9 };
+static const unsigned gpio3_a_1_pins[] = { AB8500_PIN_U9 };
+static const unsigned gpio4_a_1_pins[] = { AB8500_PIN_W2 };
+static const unsigned gpio6_a_1_pins[] = { AB8500_PIN_Y18 };
+static const unsigned gpio7_a_1_pins[] = { AB8500_PIN_AA20 };
+static const unsigned gpio8_a_1_pins[] = { AB8500_PIN_W18 };
+static const unsigned gpio9_a_1_pins[] = { AB8500_PIN_AA19 };
+/* YCbCr4 YCbCr5 YCbCr6 YCbCr7*/
+static const unsigned ycbcr4567_a_1_pins[] = { AB8500_PIN_U17, AB8500_PIN_AA18,
+ AB8500_PIN_U16, AB8500_PIN_W17};
+static const unsigned gpio14_a_1_pins[] = { AB8500_PIN_F14 };
+static const unsigned gpio15_a_1_pins[] = { AB8500_PIN_B17 };
+static const unsigned gpio16_a_1_pins[] = { AB8500_PIN_F15 };
+static const unsigned gpio17_a_1_pins[] = { AB8500_PIN_P5 };
+static const unsigned gpio18_a_1_pins[] = { AB8500_PIN_R5 };
+static const unsigned gpio19_a_1_pins[] = { AB8500_PIN_U5 };
+static const unsigned gpio20_a_1_pins[] = { AB8500_PIN_T5 };
+static const unsigned gpio21_a_1_pins[] = { AB8500_PIN_H19 };
+static const unsigned gpio22_a_1_pins[] = { AB8500_PIN_G20 };
+static const unsigned gpio23_a_1_pins[] = { AB8500_PIN_G19 };
+static const unsigned gpio24_a_1_pins[] = { AB8500_PIN_T14 };
+static const unsigned gpio25_a_1_pins[] = { AB8500_PIN_R16 };
+static const unsigned gpio27_a_1_pins[] = { AB8500_PIN_J6 };
+static const unsigned gpio28_a_1_pins[] = { AB8500_PIN_K6 };
+static const unsigned gpio29_a_1_pins[] = { AB8500_PIN_G6 };
+static const unsigned gpio30_a_1_pins[] = { AB8500_PIN_H6 };
+static const unsigned gpio31_a_1_pins[] = { AB8500_PIN_F5 };
+static const unsigned gpio32_a_1_pins[] = { AB8500_PIN_G5 };
+static const unsigned gpio34_a_1_pins[] = { AB8500_PIN_R17 };
+static const unsigned gpio36_a_1_pins[] = { AB8500_PIN_A17 };
+static const unsigned gpio37_a_1_pins[] = { AB8500_PIN_E15 };
+static const unsigned gpio38_a_1_pins[] = { AB8500_PIN_C17 };
+static const unsigned gpio39_a_1_pins[] = { AB8500_PIN_E16 };
+static const unsigned gpio40_a_1_pins[] = { AB8500_PIN_T19 };
+static const unsigned gpio41_a_1_pins[] = { AB8500_PIN_U19 };
+static const unsigned gpio42_a_1_pins[] = { AB8500_PIN_U2 };
+
+/* Altfunction B colum */
+static const unsigned hiqclkena_b_1_pins[] = { AB8500_PIN_U17 };
+static const unsigned usbuiccpd_b_1_pins[] = { AB8500_PIN_AA18 };
+static const unsigned i2ctrig1_b_1_pins[] = { AB8500_PIN_U16 };
+static const unsigned i2ctrig2_b_1_pins[] = { AB8500_PIN_W17 };
+
+/* Altfunction C column */
+static const unsigned usbvdat_c_1_pins[] = { AB8500_PIN_W17 };
+
+
+#define AB8500_PIN_GROUP(a, b) { .name = #a, .pins = a##_pins, \
+ .npins = ARRAY_SIZE(a##_pins), .altsetting = b }
+
+static const struct abx500_pingroup ab8500_ranges_groups[] = {
+ /* default column */
+ AB8500_PIN_GROUP(sysclkreq2_d_1, ABX500_DEFAULT),
+ AB8500_PIN_GROUP(sysclkreq3_d_1, ABX500_DEFAULT),
+ AB8500_PIN_GROUP(sysclkreq4_d_1, ABX500_DEFAULT),
+ AB8500_PIN_GROUP(sysclkreq6_d_1, ABX500_DEFAULT),
+ AB8500_PIN_GROUP(ycbcr0123_d_1, ABX500_DEFAULT),
+ AB8500_PIN_GROUP(gpio10_d_1, ABX500_DEFAULT),
+ AB8500_PIN_GROUP(gpio11_d_1, ABX500_DEFAULT),
+ AB8500_PIN_GROUP(gpio12_d_1, ABX500_DEFAULT),
+ AB8500_PIN_GROUP(gpio13_d_1, ABX500_DEFAULT),
+ AB8500_PIN_GROUP(pwmout1_d_1, ABX500_DEFAULT),
+ AB8500_PIN_GROUP(pwmout2_d_1, ABX500_DEFAULT),
+ AB8500_PIN_GROUP(pwmout3_d_1, ABX500_DEFAULT),
+ AB8500_PIN_GROUP(adi1_d_1, ABX500_DEFAULT),
+ AB8500_PIN_GROUP(usbuicc_d_1, ABX500_DEFAULT),
+ AB8500_PIN_GROUP(sysclkreq7_d_1, ABX500_DEFAULT),
+ AB8500_PIN_GROUP(sysclkreq8_d_1, ABX500_DEFAULT),
+ AB8500_PIN_GROUP(gpio26_d_1, ABX500_DEFAULT),
+ AB8500_PIN_GROUP(dmic12_d_1, ABX500_DEFAULT),
+ AB8500_PIN_GROUP(dmic34_d_1, ABX500_DEFAULT),
+ AB8500_PIN_GROUP(dmic56_d_1, ABX500_DEFAULT),
+ AB8500_PIN_GROUP(extcpena_d_1, ABX500_DEFAULT),
+ AB8500_PIN_GROUP(gpio35_d_1, ABX500_DEFAULT),
+ AB8500_PIN_GROUP(apespi_d_1, ABX500_DEFAULT),
+ AB8500_PIN_GROUP(modsclsda_d_1, ABX500_DEFAULT),
+ AB8500_PIN_GROUP(sysclkreq5_d_1, ABX500_DEFAULT),
+ /* Altfunction A column */
+ AB8500_PIN_GROUP(gpio1_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio2_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio3_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio4_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio6_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio7_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio8_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio9_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(ycbcr4567_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio14_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio15_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio16_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio17_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio18_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio19_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio20_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio21_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio22_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio23_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio24_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio25_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio27_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio28_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio29_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio30_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio31_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio32_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio34_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio36_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio37_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio38_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio39_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio40_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio41_a_1, ABX500_ALT_A),
+ AB8500_PIN_GROUP(gpio42_a_1, ABX500_ALT_A),
+ /* Altfunction B column */
+ AB8500_PIN_GROUP(hiqclkena_b_1, ABX500_ALT_B),
+ AB8500_PIN_GROUP(usbuiccpd_b_1, ABX500_ALT_B),
+ AB8500_PIN_GROUP(i2ctrig1_b_1, ABX500_ALT_B),
+ AB8500_PIN_GROUP(i2ctrig2_b_1, ABX500_ALT_B),
+ /* Altfunction C column */
+ AB8500_PIN_GROUP(usbvdat_c_1, ABX500_ALT_C),
+};
+
+/* We use this macro to define the groups applicable to a function */
+#define AB8500_FUNC_GROUPS(a, b...) \
+static const char * const a##_groups[] = { b };
+
+AB8500_FUNC_GROUPS(sysclkreq, "sysclkreq2_d_1", "sysclkreq3_d_1",
+ "sysclkreq4_d_1", "sysclkreq5_d_1", "sysclkreq6_d_1",
+ "sysclkreq7_d_1", "sysclkreq8_d_1");
+AB8500_FUNC_GROUPS(ycbcr, "ycbcr0123_d_1", "ycbcr4567_a_1");
+AB8500_FUNC_GROUPS(gpio, "gpio1_a_1", "gpio2_a_1", "gpio3_a_1", "gpio4_a_1",
+ "gpio6_a_1", "gpio7_a_1", "gpio8_a_1", "gpio9_a_1",
+ "gpio10_d_1", "gpio11_d_1", "gpio12_d_1", "gpio13_d_1",
+ "gpio14_a_1", "gpio15_a_1", "gpio16_a_1", "gpio17_a_1",
+ "gpio18_a_1", "gpio19_a_1", "gpio20_a_1", "gpio21_a_1",
+ "gpio22_a_1", "gpio23_a_1", "gpio24_a_1", "gpio25_a_1",
+ "gpio26_d_1", "gpio27_a_1", "gpio28_a_1", "gpio29_a_1",
+ "gpio30_a_1", "gpio31_a_1", "gpio32_a_1", "gpio34_a_1",
+ "gpio35_d_1", "gpio36_a_1", "gpio37_a_1", "gpio38_a_1",
+ "gpio39_a_1", "gpio40_a_1", "gpio41_a_1", "gpio42_a_1");
+AB8500_FUNC_GROUPS(pwmout, "pwmout1_d_1", "pwmout2_d_1", "pwmout3_d_1");
+AB8500_FUNC_GROUPS(adi1, "adi1_d_1");
+AB8500_FUNC_GROUPS(usbuicc, "usbuicc_d_1", "usbuiccpd_b_1");
+AB8500_FUNC_GROUPS(dmic, "dmic12_d_1", "dmic34_d_1", "dmic56_d_1");
+AB8500_FUNC_GROUPS(extcpena, "extcpena_d_1");
+AB8500_FUNC_GROUPS(apespi, "apespi_d_1");
+AB8500_FUNC_GROUPS(modsclsda, "modsclsda_d_1");
+AB8500_FUNC_GROUPS(hiqclkena, "hiqclkena_b_1");
+AB8500_FUNC_GROUPS(i2ctrig, "i2ctrig1_b_1", "i2ctrig2_b_1");
+AB8500_FUNC_GROUPS(usbvdat, "usbvdat_c_1");
+
+#define FUNCTION(fname) \
+ { \
+ .name = #fname, \
+ .groups = fname##_groups, \
+ .ngroups = ARRAY_SIZE(fname##_groups), \
+ }
+
+static const struct abx500_function ab8500_ranges_functions[] = {
+ FUNCTION(sysclkreq),
+ FUNCTION(ycbcr),
+ FUNCTION(gpio),
+ FUNCTION(pwmout),
+ FUNCTION(adi1),
+ FUNCTION(usbuicc),
+ FUNCTION(dmic),
+ FUNCTION(extcpena),
+ FUNCTION(apespi),
+ FUNCTION(modsclsda),
+ FUNCTION(hiqclkena),
+ FUNCTION(i2ctrig),
+ FUNCTION(usbvdat),
+};
+
+/*
+ * this table translates what's is in the AB8500 specification regarding the
+ * balls alternate functions (as for DB, default, ALT_A, ALT_B and ALT_C).
+ * ALTERNATE_FUNCTIONS(GPIO_NUMBER, GPIOSEL bit, ALTERNATFUNC bit1,
+ * ALTERNATEFUNC bit2, ALTA val, ALTB val, ALTC val),
+ *
+ * example :
+ *
+ * ALTERNATE_FUNCTIONS(13, 4, 3, 4, 0, 1 ,2),
+ * means that pin AB8500_PIN_W17 (pin 13) supports 4 mux (default/ALT_A,
+ * ALT_B and ALT_C), so GPIOSEL and ALTERNATFUNC registers are used to
+ * select the mux. ALTA, ALTB and ALTC val indicates values to write in
+ * ALTERNATFUNC register. We need to specifies these values as SOC
+ * designers didn't apply the same logic on how to select mux in the
+ * ABx500 family.
+ *
+ * As this pins supports at least ALT_B mux, default mux is
+ * selected by writing 1 in GPIOSEL bit :
+ *
+ * | GPIOSEL bit=4 | alternatfunc bit2=4 | alternatfunc bit1=3
+ * default | 1 | 0 | 0
+ * alt_A | 0 | 0 | 0
+ * alt_B | 0 | 0 | 1
+ * alt_C | 0 | 1 | 0
+ *
+ * ALTERNATE_FUNCTIONS(8, 7, UNUSED, UNUSED),
+ * means that pin AB8500_PIN_W18 (pin 8) supports 2 mux, so only GPIOSEL
+ * register is used to select the mux. As this pins doesn't support at
+ * least ALT_B mux, default mux is by writing 0 in GPIOSEL bit :
+ *
+ * | GPIOSEL bit=7 | alternatfunc bit2= | alternatfunc bit1=
+ * default | 0 | 0 | 0
+ * alt_A | 1 | 0 | 0
+ */
+
+struct alternate_functions ab8500_alternate_functions[AB8500_GPIO_MAX_NUMBER + 1] = {
+ ALTERNATE_FUNCTIONS(0, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO0 */
+ ALTERNATE_FUNCTIONS(1, 0, UNUSED, UNUSED, 0, 0, 0), /* GPIO1, altA controlled by bit 0 */
+ ALTERNATE_FUNCTIONS(2, 1, UNUSED, UNUSED, 0, 0, 0), /* GPIO2, altA controlled by bit 1 */
+ ALTERNATE_FUNCTIONS(3, 2, UNUSED, UNUSED, 0, 0, 0), /* GPIO3, altA controlled by bit 2*/
+ ALTERNATE_FUNCTIONS(4, 3, UNUSED, UNUSED, 0, 0, 0), /* GPIO4, altA controlled by bit 3*/
+ /* bit 4 reserved */
+ ALTERNATE_FUNCTIONS(5, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO5 */
+ ALTERNATE_FUNCTIONS(6, 5, UNUSED, UNUSED, 0, 0, 0), /* GPIO6, altA controlled by bit 5*/
+ ALTERNATE_FUNCTIONS(7, 6, UNUSED, UNUSED, 0, 0, 0), /* GPIO7, altA controlled by bit 6*/
+ ALTERNATE_FUNCTIONS(8, 7, UNUSED, UNUSED, 0, 0, 0), /* GPIO8, altA controlled by bit 7*/
+
+ ALTERNATE_FUNCTIONS(9, 0, UNUSED, UNUSED, 0, 0, 0), /* GPIO9, altA controlled by bit 0*/
+ ALTERNATE_FUNCTIONS(10, 1, 0, UNUSED, 0, 1, 0), /* GPIO10, altA and altB controlled by bit 0 */
+ ALTERNATE_FUNCTIONS(11, 2, 1, UNUSED, 0, 1, 0), /* GPIO11, altA and altB controlled by bit 1 */
+ ALTERNATE_FUNCTIONS(12, 3, 2, UNUSED, 0, 1, 0), /* GPIO12, altA and altB controlled by bit 2 */
+ ALTERNATE_FUNCTIONS(13, 4, 3, 4, 0, 1, 2), /* GPIO13, altA altB and altC controlled by bit 3 and 4 */
+ ALTERNATE_FUNCTIONS(14, 5, UNUSED, UNUSED, 0, 0, 0), /* GPIO14, altA controlled by bit 5 */
+ ALTERNATE_FUNCTIONS(15, 6, UNUSED, UNUSED, 0, 0, 0), /* GPIO15, altA controlled by bit 6 */
+ ALTERNATE_FUNCTIONS(16, 7, UNUSED, UNUSED, 0, 0, 0), /* GPIO16, altA controlled by bit 7 */
+ /*
+ * pins 17 to 20 are special case, only bit 0 is used to select
+ * alternate function for these 4 pins.
+ * bits 1 to 3 are reserved
+ */
+ ALTERNATE_FUNCTIONS(17, 0, UNUSED, UNUSED, 0, 0, 0), /* GPIO17, altA controlled by bit 0 */
+ ALTERNATE_FUNCTIONS(18, 0, UNUSED, UNUSED, 0, 0, 0), /* GPIO18, altA controlled by bit 0 */
+ ALTERNATE_FUNCTIONS(19, 0, UNUSED, UNUSED, 0, 0, 0), /* GPIO19, altA controlled by bit 0 */
+ ALTERNATE_FUNCTIONS(20, 0, UNUSED, UNUSED, 0, 0, 0), /* GPIO20, altA controlled by bit 0 */
+ ALTERNATE_FUNCTIONS(21, 4, UNUSED, UNUSED, 0, 0, 0), /* GPIO21, altA controlled by bit 4 */
+ ALTERNATE_FUNCTIONS(22, 5, UNUSED, UNUSED, 0, 0, 0), /* GPIO22, altA controlled by bit 5 */
+ ALTERNATE_FUNCTIONS(23, 6, UNUSED, UNUSED, 0, 0, 0), /* GPIO23, altA controlled by bit 6 */
+ ALTERNATE_FUNCTIONS(24, 7, UNUSED, UNUSED, 0, 0, 0), /* GPIO24, altA controlled by bit 7 */
+
+ ALTERNATE_FUNCTIONS(25, 0, UNUSED, UNUSED, 0, 0, 0), /* GPIO25, altA controlled by bit 0 */
+ /* pin 26 special case, no alternate function, bit 1 reserved */
+ ALTERNATE_FUNCTIONS(26, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* GPIO26 */
+ ALTERNATE_FUNCTIONS(27, 2, UNUSED, UNUSED, 0, 0, 0), /* GPIO27, altA controlled by bit 2 */
+ ALTERNATE_FUNCTIONS(28, 3, UNUSED, UNUSED, 0, 0, 0), /* GPIO28, altA controlled by bit 3 */
+ ALTERNATE_FUNCTIONS(29, 4, UNUSED, UNUSED, 0, 0, 0), /* GPIO29, altA controlled by bit 4 */
+ ALTERNATE_FUNCTIONS(30, 5, UNUSED, UNUSED, 0, 0, 0), /* GPIO30, altA controlled by bit 5 */
+ ALTERNATE_FUNCTIONS(31, 6, UNUSED, UNUSED, 0, 0, 0), /* GPIO31, altA controlled by bit 6 */
+ ALTERNATE_FUNCTIONS(32, 7, UNUSED, UNUSED, 0, 0, 0), /* GPIO32, altA controlled by bit 7 */
+
+ ALTERNATE_FUNCTIONS(33, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* no GPIO33 */
+ ALTERNATE_FUNCTIONS(34, 1, UNUSED, UNUSED, 0, 0, 0), /* GPIO34, altA controlled by bit 1 */
+ /* pin 35 special case, no alternate function, bit 2 reserved */
+ ALTERNATE_FUNCTIONS(35, UNUSED, UNUSED, UNUSED, 0, 0, 0), /* GPIO35 */
+ ALTERNATE_FUNCTIONS(36, 3, UNUSED, UNUSED, 0, 0, 0), /* GPIO36, altA controlled by bit 3 */
+ ALTERNATE_FUNCTIONS(37, 4, UNUSED, UNUSED, 0, 0, 0), /* GPIO37, altA controlled by bit 4 */
+ ALTERNATE_FUNCTIONS(38, 5, UNUSED, UNUSED, 0, 0, 0), /* GPIO38, altA controlled by bit 5 */
+ ALTERNATE_FUNCTIONS(39, 6, UNUSED, UNUSED, 0, 0, 0), /* GPIO39, altA controlled by bit 6 */
+ ALTERNATE_FUNCTIONS(40, 7, UNUSED, UNUSED, 0, 0, 0), /* GPIO40, altA controlled by bit 7 */
+
+ ALTERNATE_FUNCTIONS(41, 0, UNUSED, UNUSED, 0, 0, 0), /* GPIO41, altA controlled by bit 0 */
+ ALTERNATE_FUNCTIONS(42, 1, UNUSED, UNUSED, 0, 0, 0), /* GPIO42, altA controlled by bit 1 */
+};
+
+/*
+ * Only some GPIOs are interrupt capable, and they are
+ * organized in discontiguous clusters:
+ *
+ * GPIO6 to GPIO13
+ * GPIO24 and GPIO25
+ * GPIO36 to GPIO41
+ */
+struct abx500_gpio_irq_cluster ab8500_gpio_irq_cluster[] = {
+ GPIO_IRQ_CLUSTER(5, 12, 0), /* GPIO numbers start from 1 */
+ GPIO_IRQ_CLUSTER(23, 24, 0),
+ GPIO_IRQ_CLUSTER(35, 40, 0),
+};
+
+static struct abx500_pinctrl_soc_data ab8500_ranges_soc = {
+ .gpio_ranges = ab8500_pinranges,
+ .gpio_num_ranges = ARRAY_SIZE(ab8500_pinranges),
+ .pins = ab8500_ranges_pins,
+ .npins = ARRAY_SIZE(ab8500_ranges_pins),
+ .functions = ab8500_ranges_functions,
+ .nfunctions = ARRAY_SIZE(ab8500_ranges_functions),
+ .groups = ab8500_ranges_groups,
+ .ngroups = ARRAY_SIZE(ab8500_ranges_groups),
+ .alternate_functions = ab8500_alternate_functions,
+ .gpio_irq_cluster = ab8500_gpio_irq_cluster,
+ .ngpio_irq_cluster = ARRAY_SIZE(ab8500_gpio_irq_cluster),
+ .irq_gpio_rising_offset = AB8500_INT_GPIO6R,
+ .irq_gpio_falling_offset = AB8500_INT_GPIO6F,
+ .irq_gpio_factor = 1,
+};
+
+void __devinit
+abx500_pinctrl_ab8500_init(struct abx500_pinctrl_soc_data **soc)
+{
+ *soc = &ab8500_ranges_soc;
+}
diff --git a/drivers/pinctrl/pinctrl-abx500.c b/drivers/pinctrl/pinctrl-abx500.c
index 26abfd8..13ae68f 100644
--- a/drivers/pinctrl/pinctrl-abx500.c
+++ b/drivers/pinctrl/pinctrl-abx500.c
@@ -1116,6 +1116,9 @@ static int __devinit abx500_gpio_probe(struct platform_device *pdev)
/* Poke in other ASIC variants here */
switch (platid->driver_data) {
+ case PINCTRL_AB8500:
+ abx500_pinctrl_ab8500_init(&pct->soc);
+ break;
default:
dev_err(&pdev->dev, "Unsupported pinctrl sub driver (%d)\n",
(int) platid->driver_data);
diff --git a/drivers/pinctrl/pinctrl-abx500.h b/drivers/pinctrl/pinctrl-abx500.h
index 436ace3..a6c9332 100644
--- a/drivers/pinctrl/pinctrl-abx500.h
+++ b/drivers/pinctrl/pinctrl-abx500.h
@@ -177,4 +177,17 @@ struct abx500_pinctrl_soc_data {
int irq_gpio_factor;
};
+#ifdef CONFIG_PINCTRL_AB8500
+
+void abx500_pinctrl_ab8500_init(struct abx500_pinctrl_soc_data **soc);
+
+#else
+
+static inline void
+abx500_pinctrl_ab8500_init(struct abx500_pinctrl_soc_data **soc)
+{
+}
+
+#endif
+
#endif /* PINCTRL_PINCTRL_ABx500_H */
--
1.7.11.3
^ permalink raw reply related
* [PATCH 3/4 v2] pinctrl: add abx500 pinctrl driver core
From: Linus Walleij @ 2013-01-21 13:01 UTC (permalink / raw)
To: linux-arm-kernel
From: Patrice Chotard <patrice.chotard@st.com>
This adds the AB8500 core driver, which will be utilized by
the follow-on drivers for different ABx500 variants.
Sselect the driver from the DBX500_SOC, as this chip is
powering and clocking that SoC.
Cc: Samuel Ortiz <sameo@linux.intel.com>
Signed-off-by: Patrice Chotard <patrice.chotard@st.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
ChangeLog v1->v2:
- Make abx500_gpio_get() call abx500_gpio_get_bit()
- Make abx500_gpio_request_enable() use abx500_set_mode()
and as part of this define the way that different GPIO
pins are set into GPIO mode in the soc structure.
- Add clarifying comments for the "pullud" thing that is
just supported in some chip variants.
- Add setup functions as part of each driver instead of
open-coding them up front in the first patch.
- Put FIXME into one comment
- Skip setting drvdata to NULL.
- Cut kfree() on devm_* allocated object.
- Still request ACK from Samuel Ortiz
---
arch/arm/mach-ux500/Kconfig | 1 +
arch/arm/mach-ux500/board-mop500.c | 19 +-
drivers/pinctrl/Kconfig | 7 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-abx500.c | 1233 ++++++++++++++++++++++++++++++++
drivers/pinctrl/pinctrl-abx500.h | 180 +++++
include/linux/mfd/abx500/ab8500-gpio.h | 15 +-
include/linux/mfd/abx500/ab8500.h | 2 +-
8 files changed, 1437 insertions(+), 21 deletions(-)
create mode 100644 drivers/pinctrl/pinctrl-abx500.c
create mode 100644 drivers/pinctrl/pinctrl-abx500.h
diff --git a/arch/arm/mach-ux500/Kconfig b/arch/arm/mach-ux500/Kconfig
index 5dea906..2c090d7 100644
--- a/arch/arm/mach-ux500/Kconfig
+++ b/arch/arm/mach-ux500/Kconfig
@@ -11,6 +11,7 @@ config UX500_SOC_COMMON
select COMMON_CLK
select PINCTRL
select PINCTRL_NOMADIK
+ select PINCTRL_ABX500
select PL310_ERRATA_753970 if CACHE_PL310
config UX500_SOC_DB8500
diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c
index d453522..b6f14ee 100644
--- a/arch/arm/mach-ux500/board-mop500.c
+++ b/arch/arm/mach-ux500/board-mop500.c
@@ -90,26 +90,9 @@ static struct platform_device snowball_gpio_en_3v3_regulator_dev = {
},
};
-static struct ab8500_gpio_platform_data ab8500_gpio_pdata = {
+static struct abx500_gpio_platform_data ab8500_gpio_pdata = {
.gpio_base = MOP500_AB8500_PIN_GPIO(1),
.irq_base = MOP500_AB8500_VIR_GPIO_IRQ_BASE,
- /* config_reg is the initial configuration of ab8500 pins.
- * The pins can be configured as GPIO or alt functions based
- * on value present in GpioSel1 to GpioSel6 and AlternatFunction
- * register. This is the array of 7 configuration settings.
- * One has to compile time decide these settings. Below is the
- * explanation of these setting
- * GpioSel1 = 0x00 => Pins GPIO1 to GPIO8 are not used as GPIO
- * GpioSel2 = 0x1E => Pins GPIO10 to GPIO13 are configured as GPIO
- * GpioSel3 = 0x80 => Pin GPIO24 is configured as GPIO
- * GpioSel4 = 0x01 => Pin GPIo25 is configured as GPIO
- * GpioSel5 = 0x7A => Pins GPIO34, GPIO36 to GPIO39 are conf as GPIO
- * GpioSel6 = 0x00 => Pins GPIO41 & GPIo42 are not configured as GPIO
- * AlternaFunction = 0x00 => If Pins GPIO10 to 13 are not configured
- * as GPIO then this register selectes the alternate fucntions
- */
- .config_reg = {0x00, 0x1E, 0x80, 0x01,
- 0x7A, 0x00, 0x00},
};
/* ab8500-codec */
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 881ddcd..ba3038c 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -26,6 +26,13 @@ config DEBUG_PINCTRL
help
Say Y here to add some extra checks and diagnostics to PINCTRL calls.
+config PINCTRL_ABX500
+ bool "ST-Ericsson ABx500 family Mixed Signal Circuit gpio functions"
+ depends on AB8500_CORE
+ select GENERIC_PINCONF
+ help
+ Select this to enable the ABx500 family IC GPIO driver
+
config PINCTRL_AT91
bool "AT91 pinctrl driver"
depends on OF
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index e9ad8c3..ead4fa7 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -9,6 +9,7 @@ ifeq ($(CONFIG_OF),y)
obj-$(CONFIG_PINCTRL) += devicetree.o
endif
obj-$(CONFIG_GENERIC_PINCONF) += pinconf-generic.o
+obj-$(CONFIG_PINCTRL_ABX500) += pinctrl-abx500.o
obj-$(CONFIG_PINCTRL_AT91) += pinctrl-at91.o
obj-$(CONFIG_PINCTRL_BCM2835) += pinctrl-bcm2835.o
obj-$(CONFIG_PINCTRL_IMX) += pinctrl-imx.o
diff --git a/drivers/pinctrl/pinctrl-abx500.c b/drivers/pinctrl/pinctrl-abx500.c
new file mode 100644
index 0000000..26abfd8
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-abx500.c
@@ -0,0 +1,1233 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2013
+ *
+ * Author: Patrice Chotard <patrice.chotard@st.com>
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ * 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/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/abx500/ab8500-gpio.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+
+#include "pinctrl-abx500.h"
+
+/*
+ * The AB9540 and AB8540 GPIO support are extended versions
+ * of the AB8500 GPIO support.
+ * The AB9540 supports an additional (7th) register so that
+ * more GPIO may be configured and used.
+ * The AB8540 supports 4 new gpios (GPIOx_VBAT) that have
+ * internal pull-up and pull-down capabilities.
+ */
+
+/*
+ * GPIO registers offset
+ * Bank: 0x10
+ */
+#define AB8500_GPIO_SEL1_REG 0x00
+#define AB8500_GPIO_SEL2_REG 0x01
+#define AB8500_GPIO_SEL3_REG 0x02
+#define AB8500_GPIO_SEL4_REG 0x03
+#define AB8500_GPIO_SEL5_REG 0x04
+#define AB8500_GPIO_SEL6_REG 0x05
+#define AB9540_GPIO_SEL7_REG 0x06
+
+#define AB8500_GPIO_DIR1_REG 0x10
+#define AB8500_GPIO_DIR2_REG 0x11
+#define AB8500_GPIO_DIR3_REG 0x12
+#define AB8500_GPIO_DIR4_REG 0x13
+#define AB8500_GPIO_DIR5_REG 0x14
+#define AB8500_GPIO_DIR6_REG 0x15
+#define AB9540_GPIO_DIR7_REG 0x16
+
+#define AB8500_GPIO_OUT1_REG 0x20
+#define AB8500_GPIO_OUT2_REG 0x21
+#define AB8500_GPIO_OUT3_REG 0x22
+#define AB8500_GPIO_OUT4_REG 0x23
+#define AB8500_GPIO_OUT5_REG 0x24
+#define AB8500_GPIO_OUT6_REG 0x25
+#define AB9540_GPIO_OUT7_REG 0x26
+
+#define AB8500_GPIO_PUD1_REG 0x30
+#define AB8500_GPIO_PUD2_REG 0x31
+#define AB8500_GPIO_PUD3_REG 0x32
+#define AB8500_GPIO_PUD4_REG 0x33
+#define AB8500_GPIO_PUD5_REG 0x34
+#define AB8500_GPIO_PUD6_REG 0x35
+#define AB9540_GPIO_PUD7_REG 0x36
+
+#define AB8500_GPIO_IN1_REG 0x40
+#define AB8500_GPIO_IN2_REG 0x41
+#define AB8500_GPIO_IN3_REG 0x42
+#define AB8500_GPIO_IN4_REG 0x43
+#define AB8500_GPIO_IN5_REG 0x44
+#define AB8500_GPIO_IN6_REG 0x45
+#define AB9540_GPIO_IN7_REG 0x46
+#define AB8540_GPIO_VINSEL_REG 0x47
+#define AB8540_GPIO_PULL_UPDOWN_REG 0x48
+#define AB8500_GPIO_ALTFUN_REG 0x50
+#define AB8500_NUM_VIR_GPIO_IRQ 16
+#define AB8540_GPIO_PULL_UPDOWN_MASK 0x03
+#define AB8540_GPIO_VINSEL_MASK 0x03
+#define AB8540_GPIOX_VBAT_START 51
+#define AB8540_GPIOX_VBAT_END 54
+
+enum abx500_gpio_action {
+ NONE,
+ STARTUP,
+ SHUTDOWN,
+ MASK,
+ UNMASK
+};
+
+struct abx500_pinctrl {
+ struct device *dev;
+ struct pinctrl_dev *pctldev;
+ struct abx500_pinctrl_soc_data *soc;
+ struct gpio_chip chip;
+ struct ab8500 *parent;
+ struct mutex lock;
+ u32 irq_base;
+ enum abx500_gpio_action irq_action;
+ u16 rising;
+ u16 falling;
+ struct abx500_gpio_irq_cluster *irq_cluster;
+ int irq_cluster_size;
+ int irq_gpio_rising_offset;
+ int irq_gpio_falling_offset;
+ int irq_gpio_factor;
+};
+
+/**
+ * to_abx500_pinctrl() - get the pointer to abx500_pinctrl
+ * @chip: Member of the structure abx500_pinctrl
+ */
+static inline struct abx500_pinctrl *to_abx500_pinctrl(struct gpio_chip *chip)
+{
+ return container_of(chip, struct abx500_pinctrl, chip);
+}
+
+static int abx500_gpio_get_bit(struct gpio_chip *chip, u8 reg,
+ unsigned offset, bool *bit)
+{
+ struct abx500_pinctrl *pct = to_abx500_pinctrl(chip);
+ u8 pos = offset % 8;
+ u8 val;
+ int ret;
+
+ reg += offset / 8;
+ ret = abx500_get_register_interruptible(pct->dev,
+ AB8500_MISC, reg, &val);
+
+ *bit = !!(val & BIT(pos));
+
+ if (ret < 0)
+ dev_err(pct->dev,
+ "%s read reg =%x, offset=%x failed\n",
+ __func__, reg, offset);
+
+ return ret;
+}
+
+static int abx500_gpio_set_bits(struct gpio_chip *chip, u8 reg,
+ unsigned offset, int val)
+{
+ struct abx500_pinctrl *pct = to_abx500_pinctrl(chip);
+ u8 pos = offset % 8;
+ int ret;
+
+ reg += offset / 8;
+ ret = abx500_mask_and_set_register_interruptible(pct->dev,
+ AB8500_MISC, reg, 1 << pos, val << pos);
+ if (ret < 0)
+ dev_err(pct->dev, "%s write failed\n", __func__);
+ return ret;
+}
+/**
+ * abx500_gpio_get() - Get the particular GPIO value
+ * @chip: Gpio device
+ * @offset: GPIO number to read
+ */
+static int abx500_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct abx500_pinctrl *pct = to_abx500_pinctrl(chip);
+ bool bit;
+ int ret;
+
+ ret = abx500_gpio_get_bit(chip, AB8500_GPIO_IN1_REG,
+ offset, &bit);
+ if (ret < 0) {
+ dev_err(pct->dev, "%s failed\n", __func__);
+ return ret;
+ }
+ return bit;
+}
+
+static void abx500_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
+{
+ struct abx500_pinctrl *pct = to_abx500_pinctrl(chip);
+ int ret;
+
+ ret = abx500_gpio_set_bits(chip, AB8500_GPIO_OUT1_REG, offset, val);
+ if (ret < 0)
+ dev_err(pct->dev, "%s write failed\n", __func__);
+}
+
+static int abx500_config_pull_updown(struct abx500_pinctrl *pct,
+ int offset, enum abx500_gpio_pull_updown val)
+{
+ u8 pos;
+ int ret;
+ struct pullud *pullud;
+
+ if (!pct->soc->pullud) {
+ dev_err(pct->dev, "%s AB chip doesn't support pull up/down feature",
+ __func__);
+ ret = -EPERM;
+ goto out;
+ }
+
+ pullud = pct->soc->pullud;
+
+ if ((offset < pullud->first_pin)
+ || (offset > pullud->last_pin)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ pos = offset << 1;
+
+ ret = abx500_mask_and_set_register_interruptible(pct->dev,
+ AB8500_MISC, AB8540_GPIO_PULL_UPDOWN_REG,
+ AB8540_GPIO_PULL_UPDOWN_MASK << pos, val << pos);
+
+out:
+ if (ret < 0)
+ dev_err(pct->dev, "%s failed (%d)\n", __func__, ret);
+ return ret;
+}
+
+static int abx500_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset,
+ int val)
+{
+ struct abx500_pinctrl *pct = to_abx500_pinctrl(chip);
+ struct pullud *pullud = pct->soc->pullud;
+ unsigned gpio;
+ int ret;
+ /* set direction as output */
+ ret = abx500_gpio_set_bits(chip, AB8500_GPIO_DIR1_REG, offset, 1);
+ if (ret < 0)
+ return ret;
+
+ /* disable pull down */
+ ret = abx500_gpio_set_bits(chip, AB8500_GPIO_PUD1_REG, offset, 1);
+ if (ret < 0)
+ return ret;
+
+ /* if supported, disable both pull down and pull up */
+ gpio = offset + 1;
+ if (pullud && gpio >= pullud->first_pin && gpio <= pullud->last_pin) {
+ ret = abx500_config_pull_updown(pct,
+ offset,
+ ABX500_GPIO_PULL_NONE);
+ if (ret < 0)
+ return ret;
+ }
+ /* set the output as 1 or 0 */
+ return abx500_gpio_set_bits(chip, AB8500_GPIO_OUT1_REG, offset, val);
+}
+
+static int abx500_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ /* set the register as input */
+ return abx500_gpio_set_bits(chip, AB8500_GPIO_DIR1_REG, offset, 0);
+}
+
+static int abx500_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct abx500_pinctrl *pct = to_abx500_pinctrl(chip);
+ int base = pct->irq_base;
+ int i;
+
+ for (i = 0; i < pct->irq_cluster_size; i++) {
+ struct abx500_gpio_irq_cluster *cluster =
+ &pct->irq_cluster[i];
+
+ if (offset >= cluster->start && offset <= cluster->end)
+ return base + offset - cluster->start;
+
+ /* Advance by the number of gpios in this cluster */
+ base += cluster->end + cluster->offset - cluster->start + 1;
+ }
+
+ return -EINVAL;
+}
+
+static int abx500_set_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip,
+ unsigned gpio, int alt_setting)
+{
+ struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev);
+ struct alternate_functions af = pct->soc->alternate_functions[gpio];
+ int ret;
+ int val;
+ unsigned offset;
+ const char *modes[] = {
+ [ABX500_DEFAULT] = "default",
+ [ABX500_ALT_A] = "altA",
+ [ABX500_ALT_B] = "altB",
+ [ABX500_ALT_C] = "altC",
+ };
+
+ /* sanity check */
+ if (((alt_setting == ABX500_ALT_A) && (af.gpiosel_bit == UNUSED)) ||
+ ((alt_setting == ABX500_ALT_B) && (af.alt_bit1 == UNUSED)) ||
+ ((alt_setting == ABX500_ALT_C) && (af.alt_bit2 == UNUSED))) {
+ dev_dbg(pct->dev, "pin %d doesn't support %s mode\n", gpio,
+ modes[alt_setting]);
+ return -EINVAL;
+ }
+
+ /* on ABx5xx, there is no GPIO0, so adjust the offset */
+ offset = gpio - 1;
+ switch (alt_setting) {
+ case ABX500_DEFAULT:
+ /*
+ * for ABx5xx family, default mode is always selected by
+ * writing 0 to GPIOSELx register, except for pins which
+ * support at least ALT_B mode, default mode is selected
+ * by writing 1 to GPIOSELx register
+ */
+ val = 0;
+ if (af.alt_bit1 != UNUSED)
+ val++;
+
+ ret = abx500_gpio_set_bits(chip, AB8500_GPIO_SEL1_REG,
+ offset, val);
+ break;
+ case ABX500_ALT_A:
+ /*
+ * for ABx5xx family, alt_a mode is always selected by
+ * writing 1 to GPIOSELx register, except for pins which
+ * support at least ALT_B mode, alt_a mode is selected
+ * by writing 0 to GPIOSELx register and 0 in ALTFUNC
+ * register
+ */
+ if (af.alt_bit1 != UNUSED) {
+ ret = abx500_gpio_set_bits(chip, AB8500_GPIO_SEL1_REG,
+ offset, 0);
+ ret = abx500_gpio_set_bits(chip,
+ AB8500_GPIO_ALTFUN_REG,
+ af.alt_bit1,
+ !!(af.alta_val && BIT(0)));
+ if (af.alt_bit2 != UNUSED)
+ ret = abx500_gpio_set_bits(chip,
+ AB8500_GPIO_ALTFUN_REG,
+ af.alt_bit2,
+ !!(af.alta_val && BIT(1)));
+ } else
+ ret = abx500_gpio_set_bits(chip, AB8500_GPIO_SEL1_REG,
+ offset, 1);
+ break;
+ case ABX500_ALT_B:
+ ret = abx500_gpio_set_bits(chip, AB8500_GPIO_SEL1_REG,
+ offset, 0);
+ ret = abx500_gpio_set_bits(chip, AB8500_GPIO_ALTFUN_REG,
+ af.alt_bit1, !!(af.altb_val && BIT(0)));
+ if (af.alt_bit2 != UNUSED)
+ ret = abx500_gpio_set_bits(chip,
+ AB8500_GPIO_ALTFUN_REG,
+ af.alt_bit2,
+ !!(af.altb_val && BIT(1)));
+ break;
+ case ABX500_ALT_C:
+ ret = abx500_gpio_set_bits(chip, AB8500_GPIO_SEL1_REG,
+ offset, 0);
+ ret = abx500_gpio_set_bits(chip, AB8500_GPIO_ALTFUN_REG,
+ af.alt_bit2, !!(af.altc_val && BIT(0)));
+ ret = abx500_gpio_set_bits(chip, AB8500_GPIO_ALTFUN_REG,
+ af.alt_bit2, !!(af.altc_val && BIT(1)));
+ break;
+
+ default:
+ dev_dbg(pct->dev, "unknow alt_setting %d\n", alt_setting);
+ return -EINVAL;
+ }
+ return ret;
+}
+
+static u8 abx500_get_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip,
+ unsigned gpio)
+{
+ u8 mode;
+ bool bit_mode;
+ bool alt_bit1;
+ bool alt_bit2;
+ struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev);
+ struct alternate_functions af = pct->soc->alternate_functions[gpio];
+
+ /*
+ * if gpiosel_bit is set to unused,
+ * it means no GPIO or special case
+ */
+ if (af.gpiosel_bit == UNUSED)
+ return ABX500_DEFAULT;
+
+ /* read GpioSelx register */
+ abx500_gpio_get_bit(chip, AB8500_GPIO_SEL1_REG + (gpio / 8),
+ af.gpiosel_bit, &bit_mode);
+ mode = bit_mode;
+
+ /* sanity check */
+ if ((af.alt_bit1 < UNUSED) || (af.alt_bit1 > 7) ||
+ (af.alt_bit2 < UNUSED) || (af.alt_bit2 > 7)) {
+ dev_err(pct->dev,
+ "alt_bitX value not in correct range (-1 to 7)\n");
+ return -EINVAL;
+ }
+ /* if alt_bit2 is used, alt_bit1 must be used too */
+ if ((af.alt_bit2 != UNUSED) && (af.alt_bit1 == UNUSED)) {
+ dev_err(pct->dev,
+ "if alt_bit2 is used, alt_bit1 can't be unused\n");
+ return -EINVAL;
+ }
+
+ /* check if pin use AlternateFunction register */
+ if ((af.alt_bit1 == UNUSED) && (af.alt_bit1 == UNUSED))
+ return mode;
+ /*
+ * if pin GPIOSEL bit is set and pin supports alternate function,
+ * it means DEFAULT mode
+ */
+ if (mode)
+ return ABX500_DEFAULT;
+ /*
+ * pin use the AlternatFunction register
+ * read alt_bit1 value
+ */
+ abx500_gpio_get_bit(chip, AB8500_GPIO_ALTFUN_REG,
+ af.alt_bit1, &alt_bit1);
+
+ if (af.alt_bit2 != UNUSED)
+ /* read alt_bit2 value */
+ abx500_gpio_get_bit(chip, AB8500_GPIO_ALTFUN_REG, af.alt_bit2,
+ &alt_bit2);
+ else
+ alt_bit2 = 0;
+
+ mode = (alt_bit2 << 1) + alt_bit1;
+ if (mode == af.alta_val)
+ return ABX500_ALT_A;
+ else if (mode == af.altb_val)
+ return ABX500_ALT_B;
+ else
+ return ABX500_ALT_C;
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+#include <linux/seq_file.h>
+
+static void abx500_gpio_dbg_show_one(struct seq_file *s,
+ struct pinctrl_dev *pctldev, struct gpio_chip *chip,
+ unsigned offset, unsigned gpio)
+{
+ struct abx500_pinctrl *pct = to_abx500_pinctrl(chip);
+ const char *label = gpiochip_is_requested(chip, offset - 1);
+ u8 gpio_offset = offset - 1;
+ int mode = -1;
+ bool is_out;
+ bool pull;
+ const char *modes[] = {
+ [ABX500_DEFAULT] = "default",
+ [ABX500_ALT_A] = "altA",
+ [ABX500_ALT_B] = "altB",
+ [ABX500_ALT_C] = "altC",
+ };
+
+ abx500_gpio_get_bit(chip, AB8500_GPIO_DIR1_REG, gpio_offset, &is_out);
+ abx500_gpio_get_bit(chip, AB8500_GPIO_PUD1_REG, gpio_offset, &pull);
+
+ if (pctldev)
+ mode = abx500_get_mode(pctldev, chip, offset);
+
+ seq_printf(s, " gpio-%-3d (%-20.20s) %-3s %-9s %s",
+ gpio, label ?: "(none)",
+ is_out ? "out" : "in ",
+ is_out ?
+ (chip->get
+ ? (chip->get(chip, offset) ? "hi" : "lo")
+ : "? ")
+ : (pull ? "pull up" : "pull down"),
+ (mode < 0) ? "unknown" : modes[mode]);
+
+ if (label && !is_out) {
+ int irq = gpio_to_irq(gpio);
+ struct irq_desc *desc = irq_to_desc(irq);
+
+ if (irq >= 0 && desc->action) {
+ char *trigger;
+ int irq_offset = irq - pct->irq_base;
+
+ if (pct->rising & BIT(irq_offset))
+ trigger = "edge-rising";
+ else if (pct->falling & BIT(irq_offset))
+ trigger = "edge-falling";
+ else
+ trigger = "edge-undefined";
+
+ seq_printf(s, " irq-%d %s", irq, trigger);
+ }
+ }
+}
+
+static void abx500_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+ unsigned i;
+ unsigned gpio = chip->base;
+ struct abx500_pinctrl *pct = to_abx500_pinctrl(chip);
+ struct pinctrl_dev *pctldev = pct->pctldev;
+
+ for (i = 0; i < chip->ngpio; i++, gpio++) {
+ /* On AB8500, there is no GPIO0, the first is the GPIO 1 */
+ abx500_gpio_dbg_show_one(s, pctldev, chip, i + 1, gpio);
+ seq_printf(s, "\n");
+ }
+}
+
+#else
+static inline void abx500_gpio_dbg_show_one(struct seq_file *s,
+ struct pinctrl_dev *pctldev,
+ struct gpio_chip *chip,
+ unsigned offset, unsigned gpio)
+{
+}
+#define abx500_gpio_dbg_show NULL
+#endif
+
+int abx500_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ int gpio = chip->base + offset;
+
+ return pinctrl_request_gpio(gpio);
+}
+
+void abx500_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ int gpio = chip->base + offset;
+
+ pinctrl_free_gpio(gpio);
+}
+
+static struct gpio_chip abx500gpio_chip = {
+ .label = "abx500-gpio",
+ .owner = THIS_MODULE,
+ .request = abx500_gpio_request,
+ .free = abx500_gpio_free,
+ .direction_input = abx500_gpio_direction_input,
+ .get = abx500_gpio_get,
+ .direction_output = abx500_gpio_direction_output,
+ .set = abx500_gpio_set,
+ .to_irq = abx500_gpio_to_irq,
+ .dbg_show = abx500_gpio_dbg_show,
+};
+
+static unsigned int irq_to_rising(unsigned int irq)
+{
+ struct abx500_pinctrl *pct = irq_get_chip_data(irq);
+ int offset = irq - pct->irq_base;
+ int new_irq;
+
+ new_irq = offset * pct->irq_gpio_factor
+ + pct->irq_gpio_rising_offset
+ + pct->parent->irq_base;
+
+ return new_irq;
+}
+
+static unsigned int irq_to_falling(unsigned int irq)
+{
+ struct abx500_pinctrl *pct = irq_get_chip_data(irq);
+ int offset = irq - pct->irq_base;
+ int new_irq;
+
+ new_irq = offset * pct->irq_gpio_factor
+ + pct->irq_gpio_falling_offset
+ + pct->parent->irq_base;
+ return new_irq;
+
+}
+
+static unsigned int rising_to_irq(unsigned int irq, void *dev)
+{
+ struct abx500_pinctrl *pct = dev;
+ int offset, new_irq;
+
+ offset = irq - pct->irq_gpio_rising_offset
+ - pct->parent->irq_base;
+ new_irq = (offset / pct->irq_gpio_factor)
+ + pct->irq_base;
+
+ return new_irq;
+}
+
+static unsigned int falling_to_irq(unsigned int irq, void *dev)
+{
+ struct abx500_pinctrl *pct = dev;
+ int offset, new_irq;
+
+ offset = irq - pct->irq_gpio_falling_offset
+ - pct->parent->irq_base;
+ new_irq = (offset / pct->irq_gpio_factor)
+ + pct->irq_base;
+
+ return new_irq;
+}
+
+/*
+ * IRQ handler
+ */
+
+static irqreturn_t handle_rising(int irq, void *dev)
+{
+
+ handle_nested_irq(rising_to_irq(irq , dev));
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t handle_falling(int irq, void *dev)
+{
+
+ handle_nested_irq(falling_to_irq(irq, dev));
+ return IRQ_HANDLED;
+}
+
+static void abx500_gpio_irq_lock(struct irq_data *data)
+{
+ struct abx500_pinctrl *pct = irq_data_get_irq_chip_data(data);
+ mutex_lock(&pct->lock);
+}
+
+static void abx500_gpio_irq_sync_unlock(struct irq_data *data)
+{
+ struct abx500_pinctrl *pct = irq_data_get_irq_chip_data(data);
+ unsigned int irq = data->irq;
+ int offset = irq - pct->irq_base;
+ bool rising = pct->rising & BIT(offset);
+ bool falling = pct->falling & BIT(offset);
+ int ret;
+
+ switch (pct->irq_action) {
+ case STARTUP:
+ if (rising)
+ ret = request_threaded_irq(irq_to_rising(irq),
+ NULL, handle_rising,
+ IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND,
+ "abx500-gpio-r", pct);
+ if (falling)
+ ret = request_threaded_irq(irq_to_falling(irq),
+ NULL, handle_falling,
+ IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND,
+ "abx500-gpio-f", pct);
+ break;
+ case SHUTDOWN:
+ if (rising)
+ free_irq(irq_to_rising(irq), pct);
+ if (falling)
+ free_irq(irq_to_falling(irq), pct);
+ break;
+ case MASK:
+ if (rising)
+ disable_irq(irq_to_rising(irq));
+ if (falling)
+ disable_irq(irq_to_falling(irq));
+ break;
+ case UNMASK:
+ if (rising)
+ enable_irq(irq_to_rising(irq));
+ if (falling)
+ enable_irq(irq_to_falling(irq));
+ break;
+ case NONE:
+ break;
+ }
+ pct->irq_action = NONE;
+ pct->rising &= ~(BIT(offset));
+ pct->falling &= ~(BIT(offset));
+ mutex_unlock(&pct->lock);
+}
+
+
+static void abx500_gpio_irq_mask(struct irq_data *data)
+{
+ struct abx500_pinctrl *pct = irq_data_get_irq_chip_data(data);
+ pct->irq_action = MASK;
+}
+
+static void abx500_gpio_irq_unmask(struct irq_data *data)
+{
+ struct abx500_pinctrl *pct = irq_data_get_irq_chip_data(data);
+ pct->irq_action = UNMASK;
+}
+
+static int abx500_gpio_irq_set_type(struct irq_data *data, unsigned int type)
+{
+ struct abx500_pinctrl *pct = irq_data_get_irq_chip_data(data);
+ unsigned int irq = data->irq;
+ int offset = irq - pct->irq_base;
+
+ if (type == IRQ_TYPE_EDGE_BOTH) {
+ pct->rising = BIT(offset);
+ pct->falling = BIT(offset);
+ } else if (type == IRQ_TYPE_EDGE_RISING) {
+ pct->rising = BIT(offset);
+ } else {
+ pct->falling = BIT(offset);
+ }
+ return 0;
+}
+
+static unsigned int abx500_gpio_irq_startup(struct irq_data *data)
+{
+ struct abx500_pinctrl *pct = irq_data_get_irq_chip_data(data);
+ pct->irq_action = STARTUP;
+ return 0;
+}
+
+static void abx500_gpio_irq_shutdown(struct irq_data *data)
+{
+ struct abx500_pinctrl *pct = irq_data_get_irq_chip_data(data);
+ pct->irq_action = SHUTDOWN;
+}
+
+static struct irq_chip abx500_gpio_irq_chip = {
+ .name = "abx500-gpio",
+ .irq_startup = abx500_gpio_irq_startup,
+ .irq_shutdown = abx500_gpio_irq_shutdown,
+ .irq_bus_lock = abx500_gpio_irq_lock,
+ .irq_bus_sync_unlock = abx500_gpio_irq_sync_unlock,
+ .irq_mask = abx500_gpio_irq_mask,
+ .irq_unmask = abx500_gpio_irq_unmask,
+ .irq_set_type = abx500_gpio_irq_set_type,
+};
+
+static int abx500_gpio_irq_init(struct abx500_pinctrl *pct)
+{
+ u32 base = pct->irq_base;
+ int irq;
+
+ for (irq = base; irq < base + AB8500_NUM_VIR_GPIO_IRQ ; irq++) {
+ irq_set_chip_data(irq, pct);
+ irq_set_chip_and_handler(irq, &abx500_gpio_irq_chip,
+ handle_simple_irq);
+ irq_set_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ irq_set_noprobe(irq);
+#endif
+ }
+
+ return 0;
+}
+
+static void abx500_gpio_irq_remove(struct abx500_pinctrl *pct)
+{
+ int base = pct->irq_base;
+ int irq;
+
+ for (irq = base; irq < base + AB8500_NUM_VIR_GPIO_IRQ; irq++) {
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, 0);
+#endif
+ irq_set_chip_and_handler(irq, NULL, NULL);
+ irq_set_chip_data(irq, NULL);
+ }
+}
+
+static int abx500_pmx_get_funcs_cnt(struct pinctrl_dev *pctldev)
+{
+ struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev);
+
+ return pct->soc->nfunctions;
+}
+
+static const char *abx500_pmx_get_func_name(struct pinctrl_dev *pctldev,
+ unsigned function)
+{
+ struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev);
+
+ return pct->soc->functions[function].name;
+}
+
+static int abx500_pmx_get_func_groups(struct pinctrl_dev *pctldev,
+ unsigned function,
+ const char * const **groups,
+ unsigned * const num_groups)
+{
+ struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev);
+
+ *groups = pct->soc->functions[function].groups;
+ *num_groups = pct->soc->functions[function].ngroups;
+
+ return 0;
+}
+
+static void abx500_disable_lazy_irq(struct gpio_chip *chip, unsigned gpio)
+{
+ struct abx500_pinctrl *pct = to_abx500_pinctrl(chip);
+ int irq;
+ int offset;
+ bool rising;
+ bool falling;
+
+ /*
+ * check if gpio has interrupt capability and convert
+ * gpio number to irq
+ * On ABx5xx, there is no GPIO0, GPIO1 is the
+ * first one, so adjust gpio number
+ */
+ gpio--;
+ irq = gpio_to_irq(gpio + chip->base);
+ if (irq < 0)
+ return;
+
+ offset = irq - pct->irq_base;
+ rising = pct->rising & BIT(offset);
+ falling = pct->falling & BIT(offset);
+
+ /* nothing to do ?*/
+ if (!rising && !falling)
+ return;
+
+ if (rising) {
+ disable_irq(irq_to_rising(irq));
+ free_irq(irq_to_rising(irq), pct);
+ }
+ if (falling) {
+ disable_irq(irq_to_falling(irq));
+ free_irq(irq_to_falling(irq), pct);
+ }
+}
+
+static int abx500_pmx_enable(struct pinctrl_dev *pctldev, unsigned function,
+ unsigned group)
+{
+ struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev);
+ struct gpio_chip *chip = &pct->chip;
+ const struct abx500_pingroup *g;
+ int i;
+ int ret = 0;
+
+ g = &pct->soc->groups[group];
+ if (g->altsetting < 0)
+ return -EINVAL;
+
+ dev_dbg(pct->dev, "enable group %s, %u pins\n", g->name, g->npins);
+
+ for (i = 0; i < g->npins; i++) {
+ dev_dbg(pct->dev, "setting pin %d to altsetting %d\n",
+ g->pins[i], g->altsetting);
+
+ abx500_disable_lazy_irq(chip, g->pins[i]);
+ ret = abx500_set_mode(pctldev, chip, g->pins[i], g->altsetting);
+ }
+ return ret;
+}
+
+static void abx500_pmx_disable(struct pinctrl_dev *pctldev,
+ unsigned function, unsigned group)
+{
+ struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev);
+ const struct abx500_pingroup *g;
+
+ g = &pct->soc->groups[group];
+ if (g->altsetting < 0)
+ return;
+
+ /* FIXME: poke out the mux, set the pin to some default state? */
+ dev_dbg(pct->dev, "disable group %s, %u pins\n", g->name, g->npins);
+}
+
+int abx500_gpio_request_enable(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned offset)
+{
+ struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev);
+ const struct abx500_pinrange *p;
+ int ret;
+ int i;
+
+ /*
+ * Different ranges have different ways to enable GPIO function on a
+ * pin, so refer back to our local range type, where we handily define
+ * what altfunc enables GPIO for a certain pin.
+ */
+ for (i = 0; i < pct->soc->gpio_num_ranges; i++) {
+ p = &pct->soc->gpio_ranges[i];
+ if ((offset >= p->offset) &&
+ (offset < (p->offset + p->npins)))
+ break;
+ }
+
+ if (i == pct->soc->gpio_num_ranges) {
+ dev_err(pct->dev, "%s failed to locate range\n", __func__);
+ return -ENODEV;
+ }
+
+ dev_dbg(pct->dev, "enable GPIO by altfunc %d at gpio %d\n",
+ p->altfunc, offset);
+
+ ret = abx500_set_mode(pct->pctldev, &pct->chip,
+ offset, p->altfunc);
+ if (ret < 0) {
+ dev_err(pct->dev, "%s setting altfunc failed\n", __func__);
+ return ret;
+ }
+
+ return ret;
+}
+
+static void abx500_gpio_disable_free(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned offset)
+{
+}
+
+static struct pinmux_ops abx500_pinmux_ops = {
+ .get_functions_count = abx500_pmx_get_funcs_cnt,
+ .get_function_name = abx500_pmx_get_func_name,
+ .get_function_groups = abx500_pmx_get_func_groups,
+ .enable = abx500_pmx_enable,
+ .disable = abx500_pmx_disable,
+ .gpio_request_enable = abx500_gpio_request_enable,
+ .gpio_disable_free = abx500_gpio_disable_free,
+};
+
+static int abx500_get_groups_cnt(struct pinctrl_dev *pctldev)
+{
+ struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev);
+
+ return pct->soc->ngroups;
+}
+
+static const char *abx500_get_group_name(struct pinctrl_dev *pctldev,
+ unsigned selector)
+{
+ struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev);
+
+ return pct->soc->groups[selector].name;
+}
+
+static int abx500_get_group_pins(struct pinctrl_dev *pctldev,
+ unsigned selector,
+ const unsigned **pins,
+ unsigned *num_pins)
+{
+ struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev);
+
+ *pins = pct->soc->groups[selector].pins;
+ *num_pins = pct->soc->groups[selector].npins;
+ return 0;
+}
+
+static void abx500_pin_dbg_show(struct pinctrl_dev *pctldev,
+ struct seq_file *s, unsigned offset)
+{
+ struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev);
+ struct gpio_chip *chip = &pct->chip;
+
+ abx500_gpio_dbg_show_one(s, pctldev, chip, offset,
+ chip->base + offset - 1);
+}
+
+static struct pinctrl_ops abx500_pinctrl_ops = {
+ .get_groups_count = abx500_get_groups_cnt,
+ .get_group_name = abx500_get_group_name,
+ .get_group_pins = abx500_get_group_pins,
+ .pin_dbg_show = abx500_pin_dbg_show,
+};
+
+int abx500_pin_config_get(struct pinctrl_dev *pctldev,
+ unsigned pin,
+ unsigned long *config)
+{
+ /* Not implemented */
+ return -EINVAL;
+}
+
+int abx500_pin_config_set(struct pinctrl_dev *pctldev,
+ unsigned pin,
+ unsigned long config)
+{
+ struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev);
+ struct pullud *pullud = pct->soc->pullud;
+ struct gpio_chip *chip = &pct->chip;
+ unsigned offset;
+ int ret;
+ enum pin_config_param param = pinconf_to_config_param(config);
+ enum pin_config_param argument = pinconf_to_config_argument(config);
+
+ dev_dbg(chip->dev, "pin %d [%#lx]: %s %s\n",
+ pin, config, (param == PIN_CONFIG_OUTPUT) ? "output " : "input",
+ (param == PIN_CONFIG_OUTPUT) ? (argument ? "high" : "low") :
+ (argument ? "pull up" : "pull down"));
+ /* on ABx500, there is no GPIO0, so adjust the offset */
+ offset = pin - 1;
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ /*
+ * if argument = 1 set the pull down
+ * else clear the pull down
+ */
+ ret = abx500_gpio_direction_input(chip, offset);
+ /*
+ * Some chips only support pull down, while some actually
+ * support both pull up and pull down. Such chips have
+ * a "pullud" range specified for the pins that support
+ * both features. If the pin is not within that range, we
+ * fall back to the old bit set that only support pull down.
+ */
+ if (pullud &&
+ pin >= pullud->first_pin &&
+ pin <= pullud->last_pin)
+ ret = abx500_config_pull_updown(pct,
+ offset,
+ argument ? ABX500_GPIO_PULL_DOWN : ABX500_GPIO_PULL_NONE);
+ else
+ /* Chip only supports pull down */
+ ret = abx500_gpio_set_bits(chip, AB8500_GPIO_PUD1_REG,
+ offset, argument ? 0 : 1);
+ break;
+ case PIN_CONFIG_OUTPUT:
+ ret = abx500_gpio_direction_output(chip, offset, argument);
+ break;
+ default:
+ dev_err(chip->dev, "illegal configuration requested\n");
+ return -EINVAL;
+ }
+ return ret;
+}
+
+static struct pinconf_ops abx500_pinconf_ops = {
+ .pin_config_get = abx500_pin_config_get,
+ .pin_config_set = abx500_pin_config_set,
+};
+
+static struct pinctrl_desc abx500_pinctrl_desc = {
+ .name = "pinctrl-abx500",
+ .pctlops = &abx500_pinctrl_ops,
+ .pmxops = &abx500_pinmux_ops,
+ .confops = &abx500_pinconf_ops,
+ .owner = THIS_MODULE,
+};
+
+static int abx500_get_gpio_num(struct abx500_pinctrl_soc_data *soc)
+{
+ unsigned int lowest = 0;
+ unsigned int highest = 0;
+ unsigned int npins = 0;
+ int i;
+
+ /*
+ * Compute number of GPIOs from the last SoC gpio range descriptors
+ * These ranges may include "holes" but the GPIO number space shall
+ * still be homogeneous, so we need to detect and account for any
+ * such holes so that these are included in the number of GPIO pins.
+ */
+ for (i = 0; i < soc->gpio_num_ranges; i++) {
+ unsigned gstart;
+ unsigned gend;
+ const struct abx500_pinrange *p;
+
+ p = &soc->gpio_ranges[i];
+ gstart = p->offset;
+ gend = p->offset + p->npins - 1;
+
+ if (i == 0) {
+ /* First iteration, set start values */
+ lowest = gstart;
+ highest = gend;
+ } else {
+ if (gstart < lowest)
+ lowest = gstart;
+ if (gend > highest)
+ highest = gend;
+ }
+ }
+ /* this gives the absolute number of pins */
+ npins = highest - lowest + 1;
+ return npins;
+}
+
+static int __devinit abx500_gpio_probe(struct platform_device *pdev)
+{
+ struct ab8500_platform_data *abx500_pdata =
+ dev_get_platdata(pdev->dev.parent);
+ struct abx500_gpio_platform_data *pdata;
+ struct abx500_pinctrl *pct;
+ const struct platform_device_id *platid = platform_get_device_id(pdev);
+ int ret;
+ int i;
+
+ pdata = abx500_pdata->gpio;
+ if (!pdata) {
+ dev_err(&pdev->dev, "gpio platform data missing\n");
+ return -ENODEV;
+ }
+
+ pct = devm_kzalloc(&pdev->dev, sizeof(struct abx500_pinctrl),
+ GFP_KERNEL);
+ if (pct == NULL) {
+ dev_err(&pdev->dev,
+ "failed to allocate memory for pct\n");
+ return -ENOMEM;
+ }
+
+ pct->dev = &pdev->dev;
+ pct->parent = dev_get_drvdata(pdev->dev.parent);
+ pct->chip = abx500gpio_chip;
+ pct->chip.dev = &pdev->dev;
+ pct->chip.base = pdata->gpio_base;
+ pct->irq_base = pdata->irq_base;
+
+ /* initialize the lock */
+ mutex_init(&pct->lock);
+
+ /* Poke in other ASIC variants here */
+ switch (platid->driver_data) {
+ default:
+ dev_err(&pdev->dev, "Unsupported pinctrl sub driver (%d)\n",
+ (int) platid->driver_data);
+ return -EINVAL;
+ }
+
+ if (!pct->soc) {
+ dev_err(&pdev->dev, "Invalid SOC data\n");
+ return -EINVAL;
+ }
+
+ pct->chip.ngpio = abx500_get_gpio_num(pct->soc);
+ pct->irq_cluster = pct->soc->gpio_irq_cluster;
+ pct->irq_cluster_size = pct->soc->ngpio_irq_cluster;
+ pct->irq_gpio_rising_offset = pct->soc->irq_gpio_rising_offset;
+ pct->irq_gpio_falling_offset = pct->soc->irq_gpio_falling_offset;
+ pct->irq_gpio_factor = pct->soc->irq_gpio_factor;
+
+ ret = abx500_gpio_irq_init(pct);
+ if (ret)
+ goto out_free;
+ ret = gpiochip_add(&pct->chip);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to add gpiochip: %d\n",
+ ret);
+ goto out_rem_irq;
+ }
+ dev_info(&pdev->dev, "added gpiochip\n");
+
+ abx500_pinctrl_desc.pins = pct->soc->pins;
+ abx500_pinctrl_desc.npins = pct->soc->npins;
+ pct->pctldev = pinctrl_register(&abx500_pinctrl_desc, &pdev->dev, pct);
+ if (!pct->pctldev) {
+ dev_err(&pdev->dev,
+ "could not register abx500 pinctrl driver\n");
+ goto out_rem_chip;
+ }
+ dev_info(&pdev->dev, "registered pin controller\n");
+
+ /* We will handle a range of GPIO pins */
+ for (i = 0; i < pct->soc->gpio_num_ranges; i++) {
+ const struct abx500_pinrange *p = &pct->soc->gpio_ranges[i];
+
+ ret = gpiochip_add_pin_range(&pct->chip,
+ dev_name(&pdev->dev),
+ p->offset - 1, p->offset, p->npins);
+ if (ret < 0)
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, pct);
+ dev_info(&pdev->dev, "initialized abx500 pinctrl driver\n");
+
+ return 0;
+
+out_rem_chip:
+ ret = gpiochip_remove(&pct->chip);
+ if (ret)
+ dev_info(&pdev->dev, "failed to remove gpiochip\n");
+out_rem_irq:
+ abx500_gpio_irq_remove(pct);
+out_free:
+ mutex_destroy(&pct->lock);
+ return ret;
+}
+
+/*
+ * abx500_gpio_remove() - remove Ab8500-gpio driver
+ * @pdev : Platform device registered
+ */
+static int __devexit abx500_gpio_remove(struct platform_device *pdev)
+{
+ struct abx500_pinctrl *pct = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = gpiochip_remove(&pct->chip);
+ if (ret < 0) {
+ dev_err(pct->dev, "unable to remove gpiochip: %d\n",
+ ret);
+ return ret;
+ }
+
+ mutex_destroy(&pct->lock);
+
+ return 0;
+}
+
+static const struct platform_device_id abx500_pinctrl_id[] = {
+ { "pinctrl-ab8500", PINCTRL_AB8500 },
+ { "pinctrl-ab8540", PINCTRL_AB8540 },
+ { "pinctrl-ab9540", PINCTRL_AB9540 },
+ { "pinctrl-ab8505", PINCTRL_AB8505 },
+ { },
+};
+
+static struct platform_driver abx500_gpio_driver = {
+ .driver = {
+ .name = "abx500-gpio",
+ .owner = THIS_MODULE,
+ },
+ .probe = abx500_gpio_probe,
+ .remove = __devexit_p(abx500_gpio_remove),
+ .id_table = abx500_pinctrl_id,
+};
+
+static int __init abx500_gpio_init(void)
+{
+ return platform_driver_register(&abx500_gpio_driver);
+}
+core_initcall(abx500_gpio_init);
+
+MODULE_AUTHOR("Patrice Chotard <patrice.chotard@st.com>");
+MODULE_DESCRIPTION("Driver allows to use AxB5xx unused pins to be used as GPIO");
+MODULE_ALIAS("platform:abx500-gpio");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pinctrl/pinctrl-abx500.h b/drivers/pinctrl/pinctrl-abx500.h
new file mode 100644
index 0000000..436ace3
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-abx500.h
@@ -0,0 +1,180 @@
+#ifndef PINCTRL_PINCTRL_ABx5O0_H
+#define PINCTRL_PINCTRL_ABx500_H
+
+/* Package definitions */
+#define PINCTRL_AB8500 0
+#define PINCTRL_AB8540 1
+#define PINCTRL_AB9540 2
+#define PINCTRL_AB8505 3
+
+/* pins alternate function */
+enum abx500_pin_func {
+ ABX500_DEFAULT,
+ ABX500_ALT_A,
+ ABX500_ALT_B,
+ ABX500_ALT_C,
+};
+
+/**
+ * struct abx500_function - ABx500 pinctrl mux function
+ * @name: The name of the function, exported to pinctrl core.
+ * @groups: An array of pin groups that may select this function.
+ * @ngroups: The number of entries in @groups.
+ */
+struct abx500_function {
+ const char *name;
+ const char * const *groups;
+ unsigned ngroups;
+};
+
+/**
+ * struct abx500_pingroup - describes a ABx500 pin group
+ * @name: the name of this specific pin group
+ * @pins: an array of discrete physical pins used in this group, taken
+ * from the driver-local pin enumeration space
+ * @num_pins: the number of pins in this group array, i.e. the number of
+ * elements in .pins so we can iterate over that array
+ * @altsetting: the altsetting to apply to all pins in this group to
+ * configure them to be used by a function
+ */
+struct abx500_pingroup {
+ const char *name;
+ const unsigned int *pins;
+ const unsigned npins;
+ int altsetting;
+};
+
+#define ALTERNATE_FUNCTIONS(pin, sel_bit, alt1, alt2, alta, altb, altc) \
+{ \
+ .pin_number = pin, \
+ .gpiosel_bit = sel_bit, \
+ .alt_bit1 = alt1, \
+ .alt_bit2 = alt2, \
+ .alta_val = alta, \
+ .altb_val = altb, \
+ .altc_val = altc, \
+}
+
+#define UNUSED -1
+/**
+ * struct alternate_functions
+ * @pin_number: The pin number
+ * @gpiosel_bit: Control bit in GPIOSEL register,
+ * @alt_bit1: First AlternateFunction bit used to select the
+ * alternate function
+ * @alt_bit2: Second AlternateFunction bit used to select the
+ * alternate function
+ *
+ * these 3 following fields are necessary due to none
+ * coherency on how to select the altA, altB and altC
+ * function between the ABx500 SOC family when using
+ * alternatfunc register.
+ * @alta_val: value to write in alternatfunc to select altA function
+ * @altb_val: value to write in alternatfunc to select altB function
+ * @altc_val: value to write in alternatfunc to select altC function
+ */
+struct alternate_functions {
+ unsigned pin_number;
+ s8 gpiosel_bit;
+ s8 alt_bit1;
+ s8 alt_bit2;
+ u8 alta_val;
+ u8 altb_val;
+ u8 altc_val;
+};
+
+/**
+ * struct pullud - specific pull up/down feature
+ * @first_pin: The pin number of the first pins which support
+ * specific pull up/down
+ * @last_pin: The pin number of the last pins
+ */
+struct pullud {
+ unsigned first_pin;
+ unsigned last_pin;
+};
+
+#define GPIO_IRQ_CLUSTER(a, b, c) \
+{ \
+ .start = a, \
+ .end = b, \
+ .offset = c, \
+}
+
+/**
+ * struct abx500_gpio_irq_cluster - indicates GPIOs which are interrupt
+ * capable
+ * @start: The pin number of the first pin interrupt capable
+ * @end: The pin number of the last pin interrupt capable
+ * @offset: offset used to compute specific setting strategy of
+ * the interrupt line
+ */
+
+struct abx500_gpio_irq_cluster {
+ int start;
+ int end;
+ int offset;
+};
+
+/**
+ * struct abx500_pinrange - map pin numbers to GPIO offsets
+ * @offset: offset into the GPIO local numberspace, incidentally
+ * identical to the offset into the local pin numberspace
+ * @npins: number of pins to map from both offsets
+ * @altfunc: altfunc setting to be used to enable GPIO on a pin in
+ * this range (may vary)
+ */
+struct abx500_pinrange {
+ unsigned int offset;
+ unsigned int npins;
+ int altfunc;
+};
+
+#define ABX500_PINRANGE(a, b, c) { .offset = a, .npins = b, .altfunc = c }
+
+/**
+ * struct abx500_pinctrl_soc_data - ABx500 pin controller per-SoC configuration
+ * @gpio_ranges: An array of GPIO ranges for this SoC
+ * @gpio_num_ranges: The number of GPIO ranges for this SoC
+ * @pins: An array describing all pins the pin controller affects.
+ * All pins which are also GPIOs must be listed first within the
+ * array, and be numbered identically to the GPIO controller's
+ * numbering.
+ * @npins: The number of entries in @pins.
+ * @functions: The functions supported on this SoC.
+ * @nfunction: The number of entries in @functions.
+ * @groups: An array describing all pin groups the pin SoC supports.
+ * @ngroups: The number of entries in @groups.
+ * @alternate_functions: array describing pins which supports alternate and
+ * how to set it.
+ * @pullud: array describing pins which supports pull up/down
+ * specific registers.
+ * @gpio_irq_cluster: An array of GPIO interrupt capable for this SoC
+ * @ngpio_irq_cluster: The number of GPIO inetrrupt capable for this SoC
+ * @irq_gpio_rising_offset: Interrupt offset used as base to compute specific
+ * setting strategy of the rising interrupt line
+ * @irq_gpio_falling_offset: Interrupt offset used as base to compute specific
+ * setting strategy of the falling interrupt line
+ * @irq_gpio_factor: Factor used to compute specific setting strategy of
+ * the interrupt line
+ */
+
+struct abx500_pinctrl_soc_data {
+ const struct abx500_pinrange *gpio_ranges;
+ unsigned gpio_num_ranges;
+ const struct pinctrl_pin_desc *pins;
+ unsigned npins;
+ const struct abx500_function *functions;
+ unsigned nfunctions;
+ const struct abx500_pingroup *groups;
+ unsigned ngroups;
+ struct alternate_functions *alternate_functions;
+ struct pullud *pullud;
+ struct abx500_gpio_irq_cluster *gpio_irq_cluster;
+ unsigned ngpio_irq_cluster;
+ int irq_gpio_rising_offset;
+ int irq_gpio_falling_offset;
+ int irq_gpio_factor;
+};
+
+#endif /* PINCTRL_PINCTRL_ABx500_H */
diff --git a/include/linux/mfd/abx500/ab8500-gpio.h b/include/linux/mfd/abx500/ab8500-gpio.h
index 2387c20..e8c8281 100644
--- a/include/linux/mfd/abx500/ab8500-gpio.h
+++ b/include/linux/mfd/abx500/ab8500-gpio.h
@@ -14,10 +14,21 @@
* registers.
*/
-struct ab8500_gpio_platform_data {
+struct abx500_gpio_platform_data {
int gpio_base;
u32 irq_base;
- u8 config_reg[8];
+};
+
+enum abx500_gpio_pull_updown {
+ ABX500_GPIO_PULL_DOWN = 0x0,
+ ABX500_GPIO_PULL_NONE = 0x1,
+ ABX500_GPIO_PULL_UP = 0x3,
+};
+
+enum abx500_gpio_vinsel {
+ ABX500_GPIO_VINSEL_VBAT = 0x0,
+ ABX500_GPIO_VINSEL_VIN_1V8 = 0x1,
+ ABX500_GPIO_VINSEL_VDD_BIF = 0x2,
};
#endif /* _AB8500_GPIO_H */
diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h
index e640ea0..fc05344 100644
--- a/include/linux/mfd/abx500/ab8500.h
+++ b/include/linux/mfd/abx500/ab8500.h
@@ -385,7 +385,7 @@ struct ab8500_platform_data {
struct ab8500_regulator_reg_init *regulator_reg_init;
int num_regulator;
struct regulator_init_data *regulator;
- struct ab8500_gpio_platform_data *gpio;
+ struct abx500_gpio_platform_data *gpio;
struct ab8500_codec_platform_data *codec;
};
--
1.7.11.3
^ permalink raw reply related
* [v3 3/9] ARM: tegra: Use DT /cpu node to detect number of CPU core
From: Lorenzo Pieralisi @ 2013-01-21 12:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358237598-32413-4-git-send-email-hdoyu@nvidia.com>
On Tue, Jan 15, 2013 at 08:13:12AM +0000, Hiroshi Doyu wrote:
> SCU based detection only works with Cortex-A9 MP and it doesn't
> support ones with multiple clusters. The only way to detect number of
> CPU core correctly is with DT /cpu node.
>
> Tegra SoCs decided to use DT detection as the only way and to not use
> SCU based detection at all. Even if DT /cpu node based detection
> fails, it continues with a single core
Please add missing punctuation, reword the commit log to make it clearer.
>
> Signed-off-by: Hiroshi Doyu <hdoyu@nvidia.com>
> ---
> Based on the discussion:
> http://lists.infradead.org/pipermail/linux-arm-kernel/2013-January/140608.html
>
> Signed-off-by: Hiroshi Doyu <hdoyu@nvidia.com>
> ---
> arch/arm/mach-tegra/platsmp.c | 15 ---------------
> 1 file changed, 15 deletions(-)
>
> diff --git a/arch/arm/mach-tegra/platsmp.c b/arch/arm/mach-tegra/platsmp.c
> index 6867030..689ee4b 100644
> --- a/arch/arm/mach-tegra/platsmp.c
> +++ b/arch/arm/mach-tegra/platsmp.c
> @@ -177,23 +177,8 @@ done:
> return status;
> }
>
> -/*
> - * Initialise the CPU possible map early - this describes the CPUs
> - * which may be present or become present in the system.
> - */
> static void __init tegra_smp_init_cpus(void)
> {
> - unsigned int i, ncores = scu_get_core_count(scu_base);
> -
> - if (ncores > nr_cpu_ids) {
> - pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
> - ncores, nr_cpu_ids);
> - ncores = nr_cpu_ids;
> - }
> -
> - for (i = 0; i < ncores; i++)
> - set_cpu_possible(i, true);
> -
> set_smp_cross_call(gic_raise_softirq);
> }
>
As discussed,
Reviewed-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
^ permalink raw reply
* [PATCH V5 3/3] ARM: davinci: da850: add NAND driver DT entries
From: Sekhar Nori @ 2013-01-21 12:32 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358327261-9679-4-git-send-email-anilkumar.v@ti.com>
Hi Anil,
On 1/16/2013 2:37 PM, Kumar, Anil wrote:
> Add NAND driver DT node and related pinctrl DT data to export NAND
> functionality on da850 evm.
>
> Signed-off-by: Kumar, Anil <anilkumar.v@ti.com>
I applied all three patches in this series.
> + nand_cs3 at 62000000 {
> + compatible = "ti,davinci-nand";
> + reg = <0x62000000 0x807ff
> + 0x68000000 0x8000>;
While applying I changed the indentation of this line so the two
addresses are aligned.
Thanks,
Sekhar
^ permalink raw reply
* [PATCH v2 3/3] arm: omap2: gpmc: add DT bindings for OneNAND
From: Mark Rutland @ 2013-01-21 12:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358634477-25868-3-git-send-email-ezequiel.garcia@free-electrons.com>
[...]
> diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c
> index 01ce462..f7de9eb 100644
> --- a/arch/arm/mach-omap2/gpmc.c
> +++ b/arch/arm/mach-omap2/gpmc.c
> @@ -39,6 +39,7 @@
> #include "omap_device.h"
> #include "gpmc.h"
> #include "gpmc-nand.h"
> +#include "gpmc-onenand.h"
>
> #define DEVICE_NAME "omap-gpmc"
>
> @@ -1259,6 +1260,43 @@ static int gpmc_probe_nand_child(struct platform_device *pdev,
> }
> #endif
>
> +#ifdef CONFIG_MTD_ONENAND
> +static int gpmc_probe_onenand_child(struct platform_device *pdev,
> + struct device_node *child)
> +{
> + u32 val;
> + struct omap_onenand_platform_data *gpmc_onenand_data;
> +
> + if (of_property_read_u32(child, "reg", &val) < 0) {
> + dev_err(&pdev->dev, "%s has no 'reg' property\n",
> + child->full_name);
> + return -ENODEV;
> + }
> +
> + gpmc_onenand_data = devm_kzalloc(&pdev->dev, sizeof(*gpmc_onenand_data),
> + GFP_KERNEL);
> + if (!gpmc_onenand_data)
> + return -ENOMEM;
> +
> + gpmc_onenand_data->cs = val;
> + gpmc_onenand_data->of_node = child;
> + gpmc_onenand_data->dma_channel = -1;
> +
> + if (!of_property_read_u32(child, "dma-channel", &val))
> + gpmc_onenand_data->dma_channel = val;
> +
> + gpmc_onenand_init(gpmc_onenand_data);
> +
> + return 0;
> +}
> +#else
> +static int gpmc_probe_onenand_child(struct platform_device *pdev,
> + struct device_node *child)
> +{
> + return 0;
> +}
> +#endif
> +
> static int gpmc_probe_dt(struct platform_device *pdev)
> {
> int ret;
> @@ -1276,6 +1314,12 @@ static int gpmc_probe_dt(struct platform_device *pdev)
> return ret;
> }
>
This doesn't look right to me:
> + for_each_node_by_name(child, "onenand") {
> + ret = gpmc_probe_onenand_child(pdev, child);
> + of_node_put(child);
> + if (ret < 0)
> + return ret;
> + }
for_each_node_by_name automatically calls of_node_put on each node once passed,
and as far as I can tell, gpmc_probe_onenand_child doesn't do anything that'd
increment a node's refcount.
As far as I can see, you only need the of_node_put in the error case:
for_each_node_by_name(child, "onenand") {
ret = gpmc_probe_onenand_child(pdev, child);
if (ret < 0) {
of_node_put(child);
return ret;
}
}
Have I missed something here?
> return 0;
> }
> #else
> --
> 1.7.8.6
>
> _______________________________________________
> devicetree-discuss mailing list
> devicetree-discuss at lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/devicetree-discuss
>
Thanks,
Mark.
^ permalink raw reply
* [PATCH 3/4] pinctrl: add abx500 pinctrl driver core
From: Linus Walleij @ 2013-01-21 12:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <50F5A1B7.20504@wwwdotorg.org>
[Patrice, jump in if I'm saying something wrong.]
On Tue, Jan 15, 2013 at 7:36 PM, Stephen Warren <swarren@wwwdotorg.org> wrote:
> On 01/15/2013 02:43 AM, Linus Walleij wrote:
>> From: Patrice Chotard <patrice.chotard@st.com>
>>
>> This adds the AB8500 core driver, which will be utilized by
>> the follow-on drivers for different ABx500 variants.
>> Sselect the driver from the DBX500_SOC, as this chip is
>> powering and clocking that SoC.
>
>> diff --git a/drivers/pinctrl/pinctrl-abx500.c b/drivers/pinctrl/pinctrl-abx500.c
>
>> +static int abx500_gpio_get(struct gpio_chip *chip, unsigned offset)
>
> Shouldn't this call abx500_gpio_get_bit(), just like abx500_gpio_set()
> calls abx500_gpio_set_bit()?
OK done.
>> +static int abx500_gpio_direction_output(struct gpio_chip *chip,
>> + unsigned offset,
>> + int val)
> ...
>> + /* disable pull down */
> ...
>> + /* if supported, disable both pull down and pull up */
>
> Why the need to override those options?
Because of electronics, if you're going to use something as output
any pulling will just leak current through the resistor. These drivers
are push/pull and can't do fancy stuff like open collector/drain
so pulling something that is an output is strictly illegal.
Of course the pin config should never be set up like that in the
first place, but to be sure, or the case where said pin is not set
up in the pinmap, this adds additional security.
>> +static u8 abx500_get_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip,
>> + unsigned gpio)
>
>> + if (af.gpiosel_bit == UNUSED)
>> + return ABX500_DEFAULT;
>
> That's odd; abx500_set_mode() seems to allow setting the mode to
> something other than default even if (af.gpiosel_bit == UNUSED). Are
> set_mode/get_mode actually correct inverses of each-other?
Well this clause is correct, because pins that do not use the GPIOSEL
register have only one possible function, which is also hardwired to
the pin, so you cannot select anything else.
But when you *set* the default mode, it is done in one of two
different ways, either by writing zero to the GPIOSEL register or
by writing 1 to the GPIOSEL register, depending on which
GPIO it is. (I didn't invent this hardware.)
The set() function does not disallow setting default mode on
pins that only have default mode though, so that's why that
code path is slightly longer.
But the sentinel in the beginning of the set() function still
explicitly disallows impossible combinations so it should be
safe.
I hope this make thinks clearer :-/
>> +static int abx500_gpio_irq_init(struct abx500_pinctrl *pct)
> ...
>> +#ifdef CONFIG_ARM
>> + set_irq_flags(irq, IRQF_VALID);
>> +#else
>> + irq_set_noprobe(irq);
>> +#endif
>
> I assume that ifdef is always set one particular way?
>
>> +static void abx500_gpio_irq_remove(struct abx500_pinctrl *pct)
> ...
>> +#ifdef CONFIG_ARM
>> + set_irq_flags(irq, 0);
>> +#endif
>
> Same there.
It's not always set in a particular way unless we e.g. depend on
it in Kconfig.
Some time back the people doing x86_64 allyesconfig builds
told me to not unnecessarily disallow the compilation of my
drivers, so this gets some test compilation coverage also on
the standard builds like this, and I think that's one of the major
resons to why you find this in a few GPIO drivers & such.
I'm split about the thing, it's a small price for some nice compile-
coverage.
>> +static void abx500_pmx_disable(struct pinctrl_dev *pctldev,
>> + unsigned function, unsigned group)
>> +{
>> + struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev);
>> + const struct abx500_pingroup *g;
>> +
>> + g = &pct->soc->groups[group];
>> + if (g->altsetting < 0)
>> + return;
>> +
>> + /* Poke out the mux, set the pin to some default state? */
>> + dev_dbg(pct->dev, "disable group %s, %u pins\n", g->name, g->npins);
>> +}
>
> That looks basically unimplemented, and the comment seems like a FIXME?
Yes well, I've put in a FIXME.
>> +int abx500_gpio_request_enable(struct pinctrl_dev *pctldev,
>> + struct pinctrl_gpio_range *range,
>> + unsigned offset)
> ...
>> + /*
>> + * by default, for ABx5xx family, GPIO mode is selected by
>> + * writing 1 in GPIOSELx registers
>> + */
>> + ret = abx500_mask_and_set_register_interruptible(pct->dev,
>> + AB8500_MISC, reg, 1 << pos, 1 << pos);
>
> It sounds like this should be implemented using abx500_set_mode()?
OK done.
>> +int abx500_pin_config_set(struct pinctrl_dev *pctldev,
>> + unsigned pin,
>> + unsigned long config)
>
>> + switch (param) {
>> + case PIN_CONFIG_BIAS_PULL_DOWN:
>> + /*
>> + * if argument = 1 set the pull down
>> + * else clear the pull down
>> + */
>> + ret = abx500_gpio_direction_input(chip, offset);
>
> That looks odd; why force the pin to be a GPIO just to enable a pull down?
This is to avoid the situation where you drive current (when set as
output) through the pull-down resistor.
I.e. a way to avoid doing illegal things. Safety measure.
(As per above, no open drain/collector here, this is push/pull)
>> + /* check if pin supports pull updown feature */
>> + if (pullud && pin >= pullud->first_pin && pin <= pullud->last_pin)
>> + ret = abx500_config_pull_updown(pct,
>> + offset,
>> + argument ? ABX500_GPIO_PULL_DOWN : ABX500_GPIO_PULL_NONE);
>> + else
>> + ret = abx500_gpio_set_bits(chip, AB8500_GPIO_PUD1_REG,
>> + offset, argument ? 0 : 1);
>
> Hmm. Wouldn't it be better to remove the if statement, and just store
> ABX500_GPIO_PULL_DOWN or 0, and ABX500_GPIO_PULL_NONE or 1, in the soc_data?
So on some chips, some ranges of pins support both pull up and pull
down, and those are handled in the first branch of the clause.
Others just support pull down, and those are handled in the second
clause.
The "pullud" range is supposed to be reused when we implement
setting pull up as well, then it comes in handy.
But I added some comments so it's clear why we do this.
>> +static int __devinit abx500_gpio_probe(struct platform_device *pdev)
>
>> + /* Poke in other ASIC variants here */
>> + switch (platid->driver_data) {
>> + case PINCTRL_AB8500:
>> + abx500_pinctrl_ab8500_init(&pct->soc);
>> + break;
>> + case PINCTRL_AB8540:
>> + abx500_pinctrl_ab8540_init(&pct->soc);
>> + break;
>> + case PINCTRL_AB9540:
>> + abx500_pinctrl_ab9540_init(&pct->soc);
>> + break;
>> + case PINCTRL_AB8505:
>> + abx500_pinctrl_ab8505_init(&pct->soc);
>> + break;
>> + default:
>> + dev_err(&pdev->dev, "Unsupported pinctrl sub driver (%d)\n",
>> + (int) platid->driver_data);
>> + return -EINVAL;
>> + }
>
> Most of those functions don't exist yet.
Sorry about this, I'll strip the chips out and add them with
respective chip patch.
I want to get this core driver and AB8500 vanilla in first,
then proceed with the other variants.
> In the past, Arnd requested that each variant had a separate top-level
> driver object that called into a utility probe() function, rather than
> having a probe() function that knew about all the SoC variants, and
> dispatched out to a variant-specific function.
Yeah, as usual I'm happy with either approach as long as it's
straight-forward.
Is it a big issue?
>> +static int __devexit abx500_gpio_remove(struct platform_device *pdev)
>
>> + platform_set_drvdata(pdev, NULL);
>
> There's no point doing that; nothing should touch the drvdata while the
> device doesn't exist (or isn't probed rather).
OK.
>> + mutex_destroy(&pct->lock);
>> + kfree(pct);
>
> That was allocated using devm_kzalloc(). There's no point freeing it
> here, and if there were, devm_kfree() should be used, or a double-free
> will occur.
OK.
Thanks a *lot* Stephen, I'll post v2 soon.
Yours,
Linus Walleij
^ permalink raw reply
* [PATCH 5/8] ARM: shmobile: add a GPIO controller DT node for sh7372
From: Laurent Pinchart @ 2013-01-21 12:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358356097-26180-6-git-send-email-g.liakhovetski@gmx.de>
Hi Guennadi,
Thanks for the patch.
On Wednesday 16 January 2013 18:08:14 Guennadi Liakhovetski wrote:
> Add a missing GPIO controller node to sh7372.
>
> Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> ---
> arch/arm/boot/dts/sh7372.dtsi | 8 ++++++++
> 1 files changed, 8 insertions(+), 0 deletions(-)
>
> diff --git a/arch/arm/boot/dts/sh7372.dtsi b/arch/arm/boot/dts/sh7372.dtsi
> index 582fdec..7ca9322 100644
> --- a/arch/arm/boot/dts/sh7372.dtsi
> +++ b/arch/arm/boot/dts/sh7372.dtsi
> @@ -19,6 +19,14 @@
> };
> };
>
> + gpio: pfc at e6050000 {
> + compatible = "renesas,pfc-sh7372";
> + reg = <0xe6050000 0x8000>,
> + <0xe605800c 0x20>;
Shouldn't the second one be
<0xe605800c 0x1c>;
> + gpio-controller;
> + #gpio-cells = <2>;
> + };
> +
I haven't checked the board files, have you made sure that the PFC device
won't be instantiated twice, once from DT and once from board code ?
> soc {
> compatible = "simple-bus";
> #address-cells = <1>;
--
Regards,
Laurent Pinchart
^ permalink raw reply
* [PATCH 1/2] clocksource: dbx500-prcmu: use relaxed readl variant
From: Fabio Baltieri @ 2013-01-21 12:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358770172-30125-1-git-send-email-fabio.baltieri@linaro.org>
On Mon, Jan 21, 2013 at 01:09:31PM +0100, Fabio Baltieri wrote:
> From: Rabin Vincent <rabin.vincent@stericsson.com>
>
> Modify clksrc_dbx500_prcmu_read to replace readl() with readl_relaxed().
> This speeds up calls to the function by about 40%.
Hello John,
these two patches may go in the existing ux500 clksrc branch
together with the nomadik-mtu's patches queued up some time ago.
Can I have your Acked-by for that?
Thanks,
Fabio
--
Fabio Baltieri
^ permalink raw reply
* [PATCH 2/8] pinctrl: add SDHI and MMCIF pin groups to sh7372
From: Laurent Pinchart @ 2013-01-21 12:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358356097-26180-3-git-send-email-g.liakhovetski@gmx.de>
Hi Guennadi,
Thanks for the patch.
On Wednesday 16 January 2013 18:08:11 Guennadi Liakhovetski wrote:
> Add pin groups for all three SDHI interfaces and two alternative pin
> groups for the MMCIF interface on the sh7372 SoC.
>
> Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> ---
> drivers/pinctrl/sh-pfc/pfc-sh7372.c | 205 ++++++++++++++++++++++++++++++++
> 1 files changed, 205 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/pinctrl/sh-pfc/pfc-sh7372.c
> b/drivers/pinctrl/sh-pfc/pfc-sh7372.c index 847e0cd..78444a0 100644
> --- a/drivers/pinctrl/sh-pfc/pfc-sh7372.c
> +++ b/drivers/pinctrl/sh-pfc/pfc-sh7372.c
[snip]
> @@ -1644,6 +1844,11 @@ struct sh_pfc_soc_info sh7372_pinmux_info = {
>
> .pins = pinmux_pins,
> .nr_pins = ARRAY_SIZE(pinmux_pins),
> + .groups = pinmux_groups,
> + .nr_groups = ARRAY_SIZE(pinmux_groups),
> + .functions = pinmux_functions,
> + .nr_functions = ARRAY_SIZE(pinmux_functions),
> +
Just a little bit of nitpicking here. I don't have a strong preference on
whether the = signs should be aligned. Both options were used in the PFC code,
so I've tried to keep whatever was there. It might be better to do so here as
well.
> .func_gpios = pinmux_func_gpios,
> .nr_func_gpios = ARRAY_SIZE(pinmux_func_gpios),
--
Regards,
Laurent Pinchart
^ permalink raw reply
* [PATCH] ARM: architected timers: allow dt based discovery using clocksource_of_init
From: Mark Rutland @ 2013-01-21 12:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358731326-2979-1-git-send-email-thomas.abraham@linaro.org>
Hi Thomas,
This looks really useful, and it's going to conflict with my arch_timer /
arm_generic driver unification series [1].
I'm happy to take this as part of my series if that's ok with you?
On Mon, Jan 21, 2013 at 01:22:06AM +0000, Thomas Abraham wrote:
> Add an entry in __clksrc_of_table so that ARMv7 architected timer is
> discoverable using call to clocksource_of_init.
>
> Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
> ---
> arch/arm/kernel/arch_timer.c | 3 +++
> 1 files changed, 3 insertions(+), 0 deletions(-)
>
> diff --git a/arch/arm/kernel/arch_timer.c b/arch/arm/kernel/arch_timer.c
> index c8ef207..d21aada 100644
> --- a/arch/arm/kernel/arch_timer.c
> +++ b/arch/arm/kernel/arch_timer.c
> @@ -504,6 +504,9 @@ int __init arch_timer_of_register(void)
>
> return arch_timer_register();
> }
> +#ifdef CONFIG_CLKSRC_OF
> +CLOCKSOURCE_OF_DECLARE(armv7_timer, "arm,armv7-timer", arch_timer_of_register)
> +#endif
>
> int __init arch_timer_sched_clock_init(void)
> {
> --
> 1.7.5.4
>
> _______________________________________________
> devicetree-discuss mailing list
> devicetree-discuss at lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/devicetree-discuss
>
Thanks,
Mark.
[1] http://lists.infradead.org/pipermail/linux-arm-kernel/2013-January/142070.html
^ permalink raw reply
* [PATCH v2 1/8] pinctrl: add SDHI and MMCIF pin groups to r8a7740
From: Laurent Pinchart @ 2013-01-21 12:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <Pine.LNX.4.64.1301171103020.30897@axis700.grange>
Hi Guennadi,
Thanks for the patch.
On Thursday 17 January 2013 11:04:26 Guennadi Liakhovetski wrote:
> Add pin groups for all three SDHI interfaces and two alternative pin
> groups for the MMCIF interface on the r8a7740 SoC.
>
> Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> ---
>
> v2: add SDHI2 pins
>
> drivers/pinctrl/sh-pfc/pfc-r8a7740.c | 248 +++++++++++++++++++++++++++++++
> 1 files changed, 248 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7740.c
> b/drivers/pinctrl/sh-pfc/pfc-r8a7740.c index d0b7165..57d9e25 100644
> --- a/drivers/pinctrl/sh-pfc/pfc-r8a7740.c
> +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7740.c
> @@ -1870,6 +1870,188 @@ static const unsigned int lcd1_sys_mux[] = {
[snip]
> +static const unsigned int mmc0_ctrl_0_pins[] = {
> + /* CMD, CLK */
> + 66, 67,
Shouldn't this be 67, 66 ?
> +};
> +static const unsigned int mmc0_ctrl_0_mux[] = {
> + MMC0_CMD_PORT67_MARK, MMC0_CLK_PORT66_MARK,
> +};
--
Regards,
Laurent Pinchart
^ permalink raw reply
* [PATCH 2/2] clocksource: dbx500-prcmu: comment cleanup
From: Fabio Baltieri @ 2013-01-21 12:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358770172-30125-1-git-send-email-fabio.baltieri@linaro.org>
Remove references to the deprecated DB5500 platform.
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Fabio Baltieri <fabio.baltieri@linaro.org>
---
drivers/clocksource/clksrc-dbx500-prcmu.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/clocksource/clksrc-dbx500-prcmu.c b/drivers/clocksource/clksrc-dbx500-prcmu.c
index 159f54a..6879a74 100644
--- a/drivers/clocksource/clksrc-dbx500-prcmu.c
+++ b/drivers/clocksource/clksrc-dbx500-prcmu.c
@@ -10,7 +10,7 @@
* DBx500-PRCMU Timer
* The PRCMU has 5 timers which are available in a always-on
* power domain. We use the Timer 4 for our always-on clock
- * source on DB8500 and Timer 3 on DB5500.
+ * source on DB8500.
*/
#include <linux/clockchips.h>
#include <linux/clksrc-dbx500-prcmu.h>
--
1.7.12.1
^ permalink raw reply related
* [PATCH 1/2] clocksource: dbx500-prcmu: use relaxed readl variant
From: Fabio Baltieri @ 2013-01-21 12:09 UTC (permalink / raw)
To: linux-arm-kernel
From: Rabin Vincent <rabin.vincent@stericsson.com>
Modify clksrc_dbx500_prcmu_read to replace readl() with readl_relaxed().
This speeds up calls to the function by about 40%.
Signed-off-by: Rabin Vincent <rabin.vincent@stericsson.com>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Fabio Baltieri <fabio.baltieri@linaro.org>
---
drivers/clocksource/clksrc-dbx500-prcmu.c | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/drivers/clocksource/clksrc-dbx500-prcmu.c b/drivers/clocksource/clksrc-dbx500-prcmu.c
index c26c369..159f54a 100644
--- a/drivers/clocksource/clksrc-dbx500-prcmu.c
+++ b/drivers/clocksource/clksrc-dbx500-prcmu.c
@@ -33,15 +33,14 @@
static void __iomem *clksrc_dbx500_timer_base;
-static cycle_t clksrc_dbx500_prcmu_read(struct clocksource *cs)
+static cycle_t notrace clksrc_dbx500_prcmu_read(struct clocksource *cs)
{
+ void __iomem *base = clksrc_dbx500_timer_base;
u32 count, count2;
do {
- count = readl(clksrc_dbx500_timer_base +
- PRCMU_TIMER_DOWNCOUNT);
- count2 = readl(clksrc_dbx500_timer_base +
- PRCMU_TIMER_DOWNCOUNT);
+ count = readl_relaxed(base + PRCMU_TIMER_DOWNCOUNT);
+ count2 = readl_relaxed(base + PRCMU_TIMER_DOWNCOUNT);
} while (count2 != count);
/* Negate because the timer is a decrementing counter */
--
1.7.12.1
^ permalink raw reply related
* [PATCH] net: fec: Add support for multiple phys on mdiobus
From: Sascha Hauer @ 2013-01-21 12:06 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <50FD2172.2020608@grandegger.com>
On Mon, Jan 21, 2013 at 12:07:30PM +0100, Wolfgang Grandegger wrote:
> On 01/21/2013 11:07 AM, Sascha Hauer wrote:
> > On Mon, Jan 21, 2013 at 09:56:24AM +0100, Wolfgang Grandegger wrote:
> >> On 01/21/2013 09:37 AM, Sascha Hauer wrote:
> >>> There may be multiple phys on an mdio bus. This series adds support
> >>> for this to the fec driver. I recently had a board which has a switch
> >>> connected to the fec's mdio bus, so I had to pick the correct phy.
> >>
> >> Pick one PHY from a switch port? Well, does a PHY-less (or fixed-link)
> >> configuration for a switch not make more sense?
> >
> > Yes, you're probably right.
> >
> >> Various ARM Ethernet
> >> contoller drivers do not support it. I recently needed a hack for an
> >> AT91 board.
> >
> > I wonder how we want to proceed. Should there be a devicetree property
> > 'fixed-link' like done for fs_enet (and not recommended for new code,
> > stated in the comment above of_phy_connect_fixed_link)?
>
> Also the gianfar and ucc_geth drivers use this interface (via fixed
> link phy). I tried to use it for the AT91 macb driver but stopped
> quickly because the usage was not straight forward (too much code)...
> even if the idea of using a fake fixed-link phy is not bad.
>
> > Currently I have a property 'phy' in the fec binding which has a phandle
> > to a phy provided by the fec's mdio bus, but this could equally well
>
> But than the cable must be connected to the associated switch port.
>
> > point to a fixed dummy phy:
> >
> > phy = &fixed-phy;
>
> The link speed, full/half duplex and maybe some mroe parameter should
> be configurable via device tree.
Well this could be done when the fixed phy driver could be registered
with the devicetree, maybe like this:
fixed-phy: mdiophy {
compatible = "mdio-fixed-phy";
link = "100FD";
};
The good thing about this would be that every ethernet driver could just
use such a fixed phy, any external mdio phy (like on Marvell Armada) or
just a phy connected to the internal mdio interface provided by the ethernet
core.
I probably should write a RFC to devicetree-discuss.
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 24/24] u8500-charger: Delay for USB enumeration
From: Lee Jones @ 2013-01-21 12:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358769840-4763-1-git-send-email-lee.jones@linaro.org>
From: Paer-Olof Haakansson <par-olof.hakansson@stericsson.com>
If charging is started before USB enumeration of an Accessory
Charger Adapter has finished, the AB8500 will generate a
VBUS_ERROR. This in turn results in timeouts and delays the
enumeration with around 15 seconds. This patch delays the
charging and then ramps currents slowly to avoid VBUS errors.
The delay allows the enumeration to have finished before
charging is turned on.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Martin Sjoblom <martin.w.sjoblom@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
Tested-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
drivers/mfd/ab8500-core.c | 6 +
drivers/power/ab8500_charger.c | 444 ++++++++++++++++++++++++++++++----------
2 files changed, 343 insertions(+), 107 deletions(-)
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index 30b9265..4c4aa19 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -749,6 +749,12 @@ static struct resource ab8500_charger_resources[] = {
.end = AB8500_INT_CH_WD_EXP,
.flags = IORESOURCE_IRQ,
},
+ {
+ .name = "VBUS_CH_DROP_END",
+ .start = AB8500_INT_VBUS_CH_DROP_END,
+ .end = AB8500_INT_VBUS_CH_DROP_END,
+ .flags = IORESOURCE_IRQ,
+ },
};
static struct resource ab8500_btemp_resources[] = {
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 432f6bc..000c8cf 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -55,6 +55,7 @@
#define MAIN_CH_INPUT_CURR_SHIFT 4
#define VBUS_IN_CURR_LIM_SHIFT 4
+#define AUTO_VBUS_IN_CURR_LIM_SHIFT 4
#define LED_INDICATOR_PWM_ENA 0x01
#define LED_INDICATOR_PWM_DIS 0x00
@@ -88,14 +89,13 @@
/* 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 */
#define CHG_WD_INTERVAL (60 * HZ)
#define AB8500_SW_CONTROL_FALLBACK 0x03
+/* Wait for enumeration before charing in us */
+#define WAIT_ACA_RID_ENUMERATION (5 * 1000)
/* UsbLineStatus register - usb types */
enum ab8500_charger_link_status {
@@ -185,12 +185,14 @@ struct ab8500_charger_event_flags {
bool usbchargernotok;
bool chgwdexp;
bool vbus_collapse;
+ bool vbus_drop_end;
};
struct ab8500_charger_usb_state {
- bool usb_changed;
int usb_current;
+ int usb_current_tmp;
enum ab8500_usb_state state;
+ enum ab8500_usb_state state_tmp;
spinlock_t usb_lock;
};
@@ -212,6 +214,10 @@ struct ab8500_charger_usb_state {
* @autopower Indicate if we should have automatic pwron after pwrloss
* @autopower_cfg platform specific power config support for "pwron after pwrloss"
* @invalid_charger_detect_state State when forcing AB to use invalid charger
+ * @is_usb_host: Indicate if last detected USB type is host
+ * @is_aca_rid: Incicate if accessory is ACA type
+ * @current_stepping_sessions:
+ * Counter for current stepping sessions
* @parent: Pointer to the struct ab8500
* @gpadc: Pointer to the struct gpadc
* @bm: Platform specific battery management information
@@ -223,12 +229,13 @@ struct ab8500_charger_usb_state {
* @usb: Structure that holds the USB charger properties
* @regu: Pointer to the struct regulator
* @charger_wq: Work queue for the IRQs and checking HW state
+ * @usb_ipt_crnt_lock: Lock to protect VBUS input current setting from mutuals
+ * @pm_lock: Lock to prevent system to suspend
* @check_vbat_work Work for checking vbat threshold to adjust vbus current
* @check_hw_failure_work: Work for checking HW 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
@@ -237,6 +244,8 @@ struct ab8500_charger_usb_state {
* @detect_usb_type_work: Work for detecting the USB type connected
* @usb_link_status_work: Work for checking the new USB link status
* @usb_state_changed_work: Work for checking USB state
+ * @attach_work: Work for detecting USB type
+ * @vbus_drop_end_work: Work for detecting VBUS drop end
* @check_main_thermal_prot_work:
* Work for checking Main thermal status
* @check_usb_thermal_prot_work:
@@ -257,6 +266,9 @@ struct ab8500_charger {
bool autopower;
bool autopower_cfg;
int invalid_charger_detect_state;
+ bool is_usb_host;
+ int is_aca_rid;
+ atomic_t current_stepping_sessions;
struct ab8500 *parent;
struct ab8500_gpadc *gpadc;
struct abx500_bm_data *bm;
@@ -268,17 +280,20 @@ struct ab8500_charger {
struct ab8500_charger_info usb;
struct regulator *regu;
struct workqueue_struct *charger_wq;
+ struct mutex usb_ipt_crnt_lock;
+ struct wake_lock pm_lock;
struct delayed_work check_vbat_work;
struct delayed_work check_hw_failure_work;
struct delayed_work check_usbchgnotok_work;
struct delayed_work kick_wd_work;
+ struct delayed_work usb_state_changed_work;
struct delayed_work attach_work;
struct delayed_work ac_charger_attached_work;
struct delayed_work usb_charger_attached_work;
+ struct delayed_work vbus_drop_end_work;
struct work_struct ac_work;
struct work_struct detect_usb_type_work;
struct work_struct usb_link_status_work;
- struct work_struct usb_state_changed_work;
struct work_struct check_main_thermal_prot_work;
struct work_struct check_usb_thermal_prot_work;
struct usb_phy *usb_phy;
@@ -568,6 +583,7 @@ static int ab8500_charger_usb_cv(struct ab8500_charger *di)
/**
* ab8500_charger_detect_chargers() - Detect the connected chargers
* @di: pointer to the ab8500_charger structure
+ * @probe: if probe, don't delay and wait for HW
*
* Returns the type of charger connected.
* For USB it will not mean we can actually charge from it
@@ -581,7 +597,7 @@ static int ab8500_charger_usb_cv(struct ab8500_charger *di)
* USB_PW_CONN if the USB power supply is connected
* AC_PW_CONN + USB_PW_CONN if USB and AC power supplies are both connected
*/
-static int ab8500_charger_detect_chargers(struct ab8500_charger *di)
+static int ab8500_charger_detect_chargers(struct ab8500_charger *di, bool probe)
{
int result = NO_PW_CONN;
int ret;
@@ -599,13 +615,25 @@ static int ab8500_charger_detect_chargers(struct ab8500_charger *di)
result = AC_PW_CONN;
/* Check for USB charger */
+
+ if (!probe) {
+ /*
+ * AB8500 says VBUS_DET_DBNC1 & VBUS_DET_DBNC100
+ * when disconnecting ACA even though no
+ * charger was connected. Try waiting a little
+ * longer than the 100 ms of VBUS_DET_DBNC100...
+ */
+ msleep(110);
+ }
ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
AB8500_CH_USBCH_STAT1_REG, &val);
if (ret < 0) {
dev_err(di->dev, "%s ab8500 read failed\n", __func__);
return ret;
}
-
+ dev_dbg(di->dev,
+ "%s AB8500_CH_USBCH_STAT1_REG %x\n", __func__,
+ val);
if ((val & VBUS_DET_DBNC1) && (val & VBUS_DET_DBNC100))
result |= USB_PW_CONN;
@@ -628,33 +656,47 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
di->usb_device_is_unrecognised = false;
+ /*
+ * Platform only supports USB 2.0.
+ * This means that charging current from USB source
+ * is maximum 500 mA. Every occurence of USB_STAT_*_HOST_*
+ * should set USB_CH_IP_CUR_LVL_0P5.
+ */
+
switch (link_status) {
case USB_STAT_STD_HOST_NC:
case USB_STAT_STD_HOST_C_NS:
case USB_STAT_STD_HOST_C_S:
dev_dbg(di->dev, "USB Type - Standard host is "
- "detected through USB driver\n");
- di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P09;
+ "detected through USB driver\n");
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
+ di->is_usb_host = true;
+ di->is_aca_rid = 0;
break;
case USB_STAT_HOST_CHG_HS_CHIRP:
di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
- dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
- di->max_usb_in_curr);
+ di->is_usb_host = true;
+ di->is_aca_rid = 0;
break;
case USB_STAT_HOST_CHG_HS:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
+ di->is_usb_host = true;
+ di->is_aca_rid = 0;
+ break;
case USB_STAT_ACA_RID_C_HS:
di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P9;
- dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
- di->max_usb_in_curr);
+ di->is_usb_host = false;
+ di->is_aca_rid = 0;
break;
case USB_STAT_ACA_RID_A:
/*
* Dedicated charger level minus maximum current accessory
- * can consume (300mA). Closest level is 1100mA
+ * can consume (900mA). Closest level is 500mA
*/
- di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P1;
- dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
- di->max_usb_in_curr);
+ dev_dbg(di->dev, "USB_STAT_ACA_RID_A detected\n");
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
+ di->is_usb_host = false;
+ di->is_aca_rid = 1;
break;
case USB_STAT_ACA_RID_B:
/*
@@ -664,14 +706,24 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P3;
dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
di->max_usb_in_curr);
+ di->is_usb_host = false;
+ di->is_aca_rid = 1;
break;
case USB_STAT_HOST_CHG_NM:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
+ di->is_usb_host = true;
+ di->is_aca_rid = 0;
+ break;
case USB_STAT_DEDICATED_CHG:
- case USB_STAT_ACA_RID_C_NM:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P5;
+ di->is_usb_host = false;
+ di->is_aca_rid = 0;
+ break;
case USB_STAT_ACA_RID_C_HS_CHIRP:
+ case USB_STAT_ACA_RID_C_NM:
di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P5;
- dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
- di->max_usb_in_curr);
+ di->is_usb_host = false;
+ di->is_aca_rid = 1;
break;
case USB_STAT_NOT_CONFIGURED:
if (di->vbus_detected) {
@@ -788,6 +840,8 @@ static int ab8500_charger_detect_usb_type(struct ab8500_charger *di)
ret = abx500_get_register_interruptible(di->dev,
AB8500_INTERRUPT, AB8500_IT_SOURCE21_REG,
&val);
+ dev_dbg(di->dev, "%s AB8500_IT_SOURCE21_REG %x\n",
+ __func__, val);
if (ret < 0) {
dev_err(di->dev, "%s ab8500 read failed\n", __func__);
return ret;
@@ -803,6 +857,8 @@ static int ab8500_charger_detect_usb_type(struct ab8500_charger *di)
dev_err(di->dev, "%s ab8500 read failed\n", __func__);
return ret;
}
+ dev_dbg(di->dev, "%s AB8500_USB_LINE_STAT_REG %x\n", __func__,
+ val);
/*
* Until the IT source register is read the UsbLineStatus
* register is not updated, hence doing the same
@@ -1062,69 +1118,125 @@ static int ab8500_charger_get_usb_cur(struct ab8500_charger *di)
static int ab8500_charger_set_current(struct ab8500_charger *di,
int ich, int reg)
{
- int ret, i;
- int curr_index, prev_curr_index, shift_value;
+ int ret = 0;
+ int auto_curr_index, curr_index, prev_curr_index, shift_value, i;
u8 reg_value;
+ u32 step_udelay;
+ bool no_stepping = false;
+
+ atomic_inc(&di->current_stepping_sessions);
+
+ ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+ reg, ®_value);
+ if (ret < 0) {
+ dev_err(di->dev, "%s read failed\n", __func__);
+ goto exit_set_current;
+ }
switch (reg) {
case AB8500_MCH_IPT_CURLVL_REG:
shift_value = MAIN_CH_INPUT_CURR_SHIFT;
+ prev_curr_index = (reg_value >> shift_value);
curr_index = ab8500_current_to_regval(ich);
+ step_udelay = STEP_UDELAY;
+ if (!di->ac.charger_connected)
+ no_stepping = true;
break;
case AB8500_USBCH_IPT_CRNTLVL_REG:
shift_value = VBUS_IN_CURR_LIM_SHIFT;
+ prev_curr_index = (reg_value >> shift_value);
curr_index = ab8500_vbus_in_curr_to_regval(ich);
+ step_udelay = STEP_UDELAY * 100;
+
+ ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CH_USBCH_STAT2_REG, ®_value);
+ if (ret < 0) {
+ dev_err(di->dev, "%s read failed\n", __func__);
+ goto exit_set_current;
+ }
+ auto_curr_index =
+ reg_value >> AUTO_VBUS_IN_CURR_LIM_SHIFT;
+
+ dev_dbg(di->dev, "%s Auto VBUS curr is %d mA\n",
+ __func__,
+ ab8500_charger_vbus_in_curr_map[auto_curr_index]);
+
+ prev_curr_index = min(prev_curr_index, auto_curr_index);
+
+ if (!di->usb.charger_connected)
+ no_stepping = true;
break;
case AB8500_CH_OPT_CRNTLVL_REG:
shift_value = 0;
+ prev_curr_index = (reg_value >> shift_value);
curr_index = ab8500_current_to_regval(ich);
+ step_udelay = STEP_UDELAY;
+ if (curr_index && (curr_index - prev_curr_index) > 1)
+ step_udelay *= 100;
+
+ if (!di->usb.charger_connected && !di->ac.charger_connected)
+ no_stepping = true;
+
break;
default:
dev_err(di->dev, "%s current register not valid\n", __func__);
- return -ENXIO;
+ ret = -ENXIO;
+ goto exit_set_current;
}
if (curr_index < 0) {
dev_err(di->dev, "requested current limit out-of-range\n");
- return -ENXIO;
- }
-
- ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
- reg, ®_value);
- if (ret < 0) {
- dev_err(di->dev, "%s read failed\n", __func__);
- return ret;
+ ret = -ENXIO;
+ goto exit_set_current;
}
- prev_curr_index = (reg_value >> shift_value);
/* only update current if it's been changed */
- if (prev_curr_index == curr_index)
- return 0;
+ if (prev_curr_index == curr_index) {
+ dev_dbg(di->dev, "%s current not changed for reg: 0x%02x\n",
+ __func__, reg);
+ ret = 0;
+ goto exit_set_current;
+ }
dev_dbg(di->dev, "%s set charger current: %d mA for reg: 0x%02x\n",
__func__, ich, reg);
- if (prev_curr_index > curr_index) {
+ if (no_stepping) {
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ reg, (u8)curr_index << shift_value);
+ if (ret)
+ dev_err(di->dev, "%s write failed\n", __func__);
+ } else if (prev_curr_index > curr_index) {
for (i = prev_curr_index - 1; i >= curr_index; i--) {
+ dev_dbg(di->dev, "curr change_1 to: %x for 0x%02x\n",
+ (u8) i << shift_value, reg);
ret = abx500_set_register_interruptible(di->dev,
- AB8500_CHARGER, reg, (u8) i << shift_value);
+ AB8500_CHARGER, reg, (u8)i << shift_value);
if (ret) {
dev_err(di->dev, "%s write failed\n", __func__);
- return ret;
+ goto exit_set_current;
}
- usleep_range(STEP_UDELAY, STEP_UDELAY * 2);
+ if (i != curr_index)
+ usleep_range(step_udelay, step_udelay * 2);
}
} else {
for (i = prev_curr_index + 1; i <= curr_index; i++) {
+ dev_dbg(di->dev, "curr change_2 to: %x for 0x%02x\n",
+ (u8)i << shift_value, reg);
ret = abx500_set_register_interruptible(di->dev,
- AB8500_CHARGER, reg, (u8) i << shift_value);
+ AB8500_CHARGER, reg, (u8)i << shift_value);
if (ret) {
dev_err(di->dev, "%s write failed\n", __func__);
- return ret;
+ goto exit_set_current;
}
- usleep_range(STEP_UDELAY, STEP_UDELAY * 2);
+ if (i != curr_index)
+ usleep_range(step_udelay, step_udelay * 2);
}
}
+
+exit_set_current:
+ atomic_dec(&di->current_stepping_sessions);
+
return ret;
}
@@ -1140,6 +1252,7 @@ static int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di,
int ich_in)
{
int min_value;
+ int ret;
/* We should always use to lowest current limit */
min_value = min(di->bm->chg_params->usb_curr_max, ich_in);
@@ -1157,8 +1270,14 @@ static int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di,
break;
}
- return ab8500_charger_set_current(di, min_value,
+ dev_info(di->dev, "VBUS input current limit set to %d mA\n", min_value);
+
+ mutex_lock(&di->usb_ipt_crnt_lock);
+ ret = ab8500_charger_set_current(di, min_value,
AB8500_USBCH_IPT_CRNTLVL_REG);
+ mutex_unlock(&di->usb_ipt_crnt_lock);
+
+ return ret;
}
/**
@@ -1469,25 +1588,13 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
dev_err(di->dev, "%s write failed\n", __func__);
return ret;
}
- /* USBChInputCurr: current that can be drawn from the usb */
- ret = ab8500_charger_set_vbus_in_curr(di, di->max_usb_in_curr);
- if (ret) {
- dev_err(di->dev, "setting USBChInputCurr failed\n");
- return ret;
- }
- /* ChOutputCurentLevel: protected output current */
- ret = ab8500_charger_set_output_curr(di, ich_out);
- if (ret) {
- dev_err(di->dev, "%s "
- "Failed to set ChOutputCurentLevel\n",
- __func__);
- return ret;
- }
/* Check if VBAT overshoot control should be enabled */
if (!di->bm->enable_overshoot)
overshoot = USB_CHG_NO_OVERSHOOT_ENA_N;
/* Enable USB Charger */
+ dev_dbg(di->dev,
+ "Enabling USB with write to AB8500_USBCH_CTRL1_REG\n");
ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
AB8500_USBCH_CTRL1_REG, USB_CH_ENA | overshoot);
if (ret) {
@@ -1500,11 +1607,29 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
if (ret < 0)
dev_err(di->dev, "failed to enable LED\n");
+ di->usb.charger_online = 1;
+
+ /* USBChInputCurr: current that can be drawn from the usb */
+ ret = ab8500_charger_set_vbus_in_curr(di, di->max_usb_in_curr);
+ if (ret) {
+ dev_err(di->dev, "setting USBChInputCurr failed\n");
+ return ret;
+ }
+
+ /* ChOutputCurentLevel: protected output current */
+ ret = ab8500_charger_set_output_curr(di, ich_out);
+ if (ret) {
+ dev_err(di->dev, "%s "
+ "Failed to set ChOutputCurentLevel\n",
+ __func__);
+ return ret;
+ }
+
queue_delayed_work(di->charger_wq, &di->check_vbat_work, HZ);
- di->usb.charger_online = 1;
} else {
/* Disable USB charging */
+ dev_dbg(di->dev, "%s Disabled USB charging\n", __func__);
ret = abx500_set_register_interruptible(di->dev,
AB8500_CHARGER,
AB8500_USBCH_CTRL1_REG, 0);
@@ -1517,7 +1642,21 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
ret = ab8500_charger_led_en(di, false);
if (ret < 0)
dev_err(di->dev, "failed to disable LED\n");
+ /* USBChInputCurr: current that can be drawn from the usb */
+ ret = ab8500_charger_set_vbus_in_curr(di, 0);
+ if (ret) {
+ dev_err(di->dev, "setting USBChInputCurr failed\n");
+ return ret;
+ }
+ /* ChOutputCurentLevel: protected output current */
+ ret = ab8500_charger_set_output_curr(di, 0);
+ if (ret) {
+ dev_err(di->dev, "%s "
+ "Failed to reset ChOutputCurentLevel\n",
+ __func__);
+ return ret;
+ }
di->usb.charger_online = 0;
di->usb.wd_expired = false;
@@ -1800,7 +1939,7 @@ static void ab8500_charger_ac_work(struct work_struct *work)
* synchronously, we have the check if the main charger is
* connected by reading the status register
*/
- ret = ab8500_charger_detect_chargers(di);
+ ret = ab8500_charger_detect_chargers(di, false);
if (ret < 0)
return;
@@ -1911,16 +2050,18 @@ static void ab8500_charger_detect_usb_type_work(struct work_struct *work)
* synchronously, we have the check if is
* connected by reading the status register
*/
- ret = ab8500_charger_detect_chargers(di);
+ ret = ab8500_charger_detect_chargers(di, false);
if (ret < 0)
return;
if (!(ret & USB_PW_CONN)) {
- di->vbus_detected = 0;
+ dev_dbg(di->dev, "%s di->vbus_detected = false\n", __func__);
+ di->vbus_detected = false;
ab8500_charger_set_usb_connected(di, false);
ab8500_power_supply_changed(di, &di->usb_chg.psy);
} else {
- di->vbus_detected = 1;
+ dev_dbg(di->dev, "%s di->vbus_detected = true\n", __func__);
+ di->vbus_detected = true;
if (is_ab8500_1p1_or_earlier(di->parent)) {
ret = ab8500_charger_detect_usb_type(di);
@@ -1930,7 +2071,8 @@ static void ab8500_charger_detect_usb_type_work(struct work_struct *work)
&di->usb_chg.psy);
}
} else {
- /* For ABB cut2.0 and onwards we have an IRQ,
+ /*
+ * For ABB cut2.0 and onwards we have an IRQ,
* USB_LINK_STATUS that will be triggered when the USB
* link status changes. The exception is USB connected
* during startup. Then we don't get a
@@ -1951,7 +2093,7 @@ static void ab8500_charger_detect_usb_type_work(struct work_struct *work)
}
/**
- * ab8500_charger_usb_link_attach_work() - delayd work to detect USB type
+ * ab8500_charger_usb_link_attach_work() - work to detect USB type
* @work: pointer to the work_struct structure
*
* Detect the type of USB plugged
@@ -1993,7 +2135,7 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
* synchronously, we have the check if is
* connected by reading the status register
*/
- detected_chargers = ab8500_charger_detect_chargers(di);
+ detected_chargers = ab8500_charger_detect_chargers(di, false);
if (detected_chargers < 0)
return;
@@ -2042,32 +2184,46 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work)
}
if (!(detected_chargers & USB_PW_CONN)) {
- di->vbus_detected = 0;
+ di->vbus_detected = false;
ab8500_charger_set_usb_connected(di, false);
ab8500_power_supply_changed(di, &di->usb_chg.psy);
return;
}
- di->vbus_detected = 1;
+ dev_dbg(di->dev,"%s di->vbus_detected = true\n",__func__);
+ di->vbus_detected = true;
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);
+ if (ret) {
+ if (ret == -ENXIO) {
+ /* No valid charger type detected */
+ ab8500_charger_set_usb_connected(di, false);
+ 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);
+ return;
+ }
+
+ 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_ACA_RID_ENUMERATION);
+ queue_delayed_work(di->charger_wq,
+ &di->attach_work,
+ msecs_to_jiffies(WAIT_ACA_RID_ENUMERATION));
+ } else if (di->is_aca_rid == 1) {
+ /* Only wait once */
+ di->is_aca_rid++;
+ dev_dbg(di->dev,
+ "%s Wait %d msec for USB enum to finish",
+ __func__, WAIT_ACA_RID_ENUMERATION);
+ queue_delayed_work(di->charger_wq,
+ &di->attach_work,
+ msecs_to_jiffies(WAIT_ACA_RID_ENUMERATION));
+ } else {
+ queue_delayed_work(di->charger_wq,
+ &di->attach_work,
+ 0);
}
}
@@ -2077,24 +2233,20 @@ static void ab8500_charger_usb_state_changed_work(struct work_struct *work)
unsigned long flags;
struct ab8500_charger *di = container_of(work,
- struct ab8500_charger, usb_state_changed_work);
+ struct ab8500_charger, usb_state_changed_work.work);
- if (!di->vbus_detected)
+ if (!di->vbus_detected) {
+ dev_dbg(di->dev,
+ "%s !di->vbus_detected\n",
+ __func__);
return;
+ }
spin_lock_irqsave(&di->usb_state.usb_lock, flags);
- di->usb_state.usb_changed = false;
+ di->usb_state.state = di->usb_state.state_tmp;
+ di->usb_state.usb_current = di->usb_state.usb_current_tmp;
spin_unlock_irqrestore(&di->usb_state.usb_lock, flags);
- /*
- * wait for some time until you get updates from the usb stack
- * and negotiations are completed
- */
- msleep(250);
-
- if (di->usb_state.usb_changed)
- return;
-
dev_dbg(di->dev, "%s USB state: 0x%02x mA: %d\n",
__func__, di->usb_state.state, di->usb_state.usb_current);
@@ -2336,6 +2488,21 @@ static irqreturn_t ab8500_charger_mainchthprotf_handler(int irq, void *_di)
return IRQ_HANDLED;
}
+static void ab8500_charger_vbus_drop_end_work(struct work_struct *work)
+{
+ struct ab8500_charger *di = container_of(work,
+ struct ab8500_charger, vbus_drop_end_work.work);
+
+ di->flags.vbus_drop_end = false;
+
+ /* Reset the drop counter */
+ abx500_set_register_interruptible(di->dev,
+ AB8500_CHARGER, AB8500_CHARGER_CTRL, 0x01);
+
+ if (di->usb.charger_connected)
+ ab8500_charger_set_vbus_in_curr(di, di->max_usb_in_curr);
+}
+
/**
* ab8500_charger_vbusdetf_handler() - VBUS falling detected
* @irq: interrupt number
@@ -2347,6 +2514,7 @@ static irqreturn_t ab8500_charger_vbusdetf_handler(int irq, void *_di)
{
struct ab8500_charger *di = _di;
+ di->vbus_detected = false;
dev_dbg(di->dev, "VBUS falling detected\n");
queue_work(di->charger_wq, &di->detect_usb_type_work);
@@ -2366,6 +2534,16 @@ static irqreturn_t ab8500_charger_vbusdetr_handler(int irq, void *_di)
di->vbus_detected = true;
dev_dbg(di->dev, "VBUS rising detected\n");
+
+ /*
+ * When already called suspend handler we can not guarantee that
+ * USB detect type work is able to run complete.
+ * Need to wake lock with timeout to make sure work is starting to
+ * execute. Upon suspend and work is not complete it will be handled
+ * by flushing the work.
+ */
+ wake_lock_timeout(&di->pm_lock, HZ / 2);
+
queue_work(di->charger_wq, &di->detect_usb_type_work);
return IRQ_HANDLED;
@@ -2474,6 +2652,25 @@ static irqreturn_t ab8500_charger_chwdexp_handler(int irq, void *_di)
}
/**
+ * ab8500_charger_vbuschdropend_handler() - VBUS drop removed
+ * @irq: interrupt number
+ * @_di: pointer to the ab8500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_charger_vbuschdropend_handler(int irq, void *_di)
+{
+ struct ab8500_charger *di = _di;
+
+ dev_dbg(di->dev, "VBUS charger drop ended\n");
+ di->flags.vbus_drop_end = true;
+ queue_delayed_work(di->charger_wq, &di->vbus_drop_end_work,
+ round_jiffies(30 * HZ));
+
+ return IRQ_HANDLED;
+}
+
+/**
* ab8500_charger_vbusovv_handler() - VBUS overvoltage detected
* @irq: interrupt number
* @_di: pointer to the ab8500_charger structure
@@ -2743,6 +2940,20 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di)
goto out;
}
+ /* Set charger watchdog timeout */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CH_WD_TIMER_REG, WD_TIMER);
+ if (ret) {
+ dev_err(di->dev, "failed to set charger watchdog timeout\n");
+ goto out;
+ }
+
+ ret = ab8500_charger_led_en(di, false);
+ if (ret < 0) {
+ dev_err(di->dev, "failed to disable LED\n");
+ goto out;
+ }
+
/* Backup battery voltage and current */
ret = abx500_set_register_interruptible(di->dev,
AB8500_RTC,
@@ -2782,6 +2993,7 @@ static struct ab8500_charger_interrupts ab8500_charger_irq[] = {
{"USB_CHARGER_NOT_OKR", ab8500_charger_usbchargernotokr_handler},
{"VBUS_OVV", ab8500_charger_vbusovv_handler},
{"CH_WD_EXP", ab8500_charger_chwdexp_handler},
+ {"VBUS_CH_DROP_END", ab8500_charger_vbuschdropend_handler},
};
static int ab8500_charger_usb_notifier_call(struct notifier_block *nb,
@@ -2818,13 +3030,15 @@ static int ab8500_charger_usb_notifier_call(struct notifier_block *nb,
__func__, bm_usb_state, mA);
spin_lock(&di->usb_state.usb_lock);
- di->usb_state.usb_changed = true;
+ di->usb_state.state_tmp = bm_usb_state;
+ di->usb_state.usb_current_tmp = mA;
spin_unlock(&di->usb_state.usb_lock);
- di->usb_state.state = bm_usb_state;
- di->usb_state.usb_current = mA;
-
- queue_work(di->charger_wq, &di->usb_state_changed_work);
+ /*
+ * wait for some time until you get updates from the usb stack
+ * and negotiations are completed
+ */
+ queue_delayed_work(di->charger_wq, &di->usb_state_changed_work, HZ/2);
return NOTIFY_OK;
}
@@ -2864,6 +3078,9 @@ static int ab8500_charger_resume(struct platform_device *pdev)
&di->check_hw_failure_work, 0);
}
+ if (di->flags.vbus_drop_end)
+ queue_delayed_work(di->charger_wq, &di->vbus_drop_end_work, 0);
+
return 0;
}
@@ -2876,6 +3093,9 @@ static int ab8500_charger_suspend(struct platform_device *pdev,
if (delayed_work_pending(&di->check_hw_failure_work))
cancel_delayed_work(&di->check_hw_failure_work);
+ if (delayed_work_pending(&di->vbus_drop_end_work))
+ cancel_delayed_work(&di->vbus_drop_end_work);
+
flush_delayed_work(&di->attach_work);
flush_delayed_work(&di->usb_charger_attached_work);
flush_delayed_work(&di->ac_charger_attached_work);
@@ -2887,6 +3107,9 @@ static int ab8500_charger_suspend(struct platform_device *pdev,
flush_work(&di->ac_work);
flush_work(&di->detect_usb_type_work);
+ if (atomic_read(&di->current_stepping_sessions))
+ return -EAGAIN;
+
return 0;
}
#else
@@ -2924,11 +3147,13 @@ static int ab8500_charger_remove(struct platform_device *pdev)
destroy_workqueue(di->charger_wq);
flush_scheduled_work();
+ wake_lock_destroy(&di->pm_lock);
if(di->usb_chg.enabled)
power_supply_unregister(&di->usb_chg.psy);
+#if !defined(CONFIG_CHARGER_PM2301)
if(di->ac_chg.enabled)
power_supply_unregister(&di->ac_chg.psy);
-
+#endif
platform_set_drvdata(pdev, NULL);
return 0;
@@ -2976,6 +3201,8 @@ static int ab8500_charger_probe(struct platform_device *pdev)
/* initialize lock */
spin_lock_init(&di->usb_state.usb_lock);
+ mutex_init(&di->usb_ipt_crnt_lock);
+ wake_lock_init(&di->pm_lock, WAKE_LOCK_SUSPEND, "ab8500-charger");
di->autopower = false;
di->invalid_charger_detect_state = 0;
@@ -3061,6 +3288,12 @@ static int ab8500_charger_probe(struct platform_device *pdev)
INIT_DELAYED_WORK(&di->attach_work,
ab8500_charger_usb_link_attach_work);
+ INIT_DELAYED_WORK(&di->usb_state_changed_work,
+ ab8500_charger_usb_state_changed_work);
+
+ INIT_DELAYED_WORK(&di->vbus_drop_end_work,
+ ab8500_charger_vbus_drop_end_work);
+
/* Init work for charger detection */
INIT_WORK(&di->usb_link_status_work,
ab8500_charger_usb_link_status_work);
@@ -3068,9 +3301,6 @@ static int ab8500_charger_probe(struct platform_device *pdev)
INIT_WORK(&di->detect_usb_type_work,
ab8500_charger_detect_usb_type_work);
- INIT_WORK(&di->usb_state_changed_work,
- ab8500_charger_usb_state_changed_work);
-
/* Init work for checking HW status */
INIT_WORK(&di->check_main_thermal_prot_work,
ab8500_charger_check_main_thermal_prot_work);
@@ -3129,7 +3359,7 @@ static int ab8500_charger_probe(struct platform_device *pdev)
}
/* Identify the connected charger types during startup */
- charger_status = ab8500_charger_detect_chargers(di);
+ charger_status = ab8500_charger_detect_chargers(di, true);
if (charger_status & AC_PW_CONN) {
di->ac.charger_connected = 1;
di->ac_conn = true;
@@ -3164,7 +3394,7 @@ static int ab8500_charger_probe(struct platform_device *pdev)
mutex_lock(&di->charger_attached_mutex);
- ch_stat = ab8500_charger_detect_chargers(di);
+ ch_stat = ab8500_charger_detect_chargers(di, false);
if ((ch_stat & AC_PW_CONN) == AC_PW_CONN) {
queue_delayed_work(di->charger_wq,
--
1.7.9.5
^ permalink raw reply related
* [PATCH 23/24] ab8500-bm: Fix minor niggles experienced during testing
From: Lee Jones @ 2013-01-21 12:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358769840-4763-1-git-send-email-lee.jones@linaro.org>
When compile testing the new AB8500 Battery Management changes
due for inclusion into upstream, there were a few minor niggles
which required repairing, or adapting for use against the
Mainline kernel. This patch is a collection of them all.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
drivers/power/ab8500_fg.h | 6 ++++--
drivers/power/abx500_chargalg.c | 4 ++--
drivers/power/pm2301_charger.c | 4 ++--
3 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/drivers/power/ab8500_fg.h b/drivers/power/ab8500_fg.h
index 946840b..bb78dc9 100644
--- a/drivers/power/ab8500_fg.h
+++ b/drivers/power/ab8500_fg.h
@@ -182,6 +182,7 @@ struct ab8500_fg_test {
* @fg_check_hw_failure_work: Work for checking HW state
* @cc_lock: Mutex for locking the CC
* @fg_kobject: Structure of type kobject
+ * @test: Structure of type ab8500_fg_test for test purpose
*/
struct ab8500_fg {
struct device *dev;
@@ -224,6 +225,7 @@ struct ab8500_fg {
struct delayed_work fg_check_hw_failure_work;
struct mutex cc_lock;
struct kobject fg_kobject;
+ struct ab8500_fg_test test;
};
extern char *discharge_state[];
@@ -232,8 +234,8 @@ extern char *charge_state[];
int ab8500_fg_coulomb_counter(struct ab8500_fg *di, bool enable);
void ab8500_fg_charge_state_to(struct ab8500_fg *di,
enum ab8500_fg_charge_state new_state);
-void ab8500_fg_discharge_state_to(struct ab8500_fg *di,
- enum ab8500_fg_charge_state new_state);
+static void ab8500_fg_discharge_state_to(struct ab8500_fg *di,
+ enum ab8500_fg_discharge_state new_state);
/* test initialization */
#ifdef CONFIG_AB8500_BM_DEEP_DEBUG
void ab8500_fg_test_init(struct ab8500_fg *di);
diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
index 694f592..cacf512 100644
--- a/drivers/power/abx500_chargalg.c
+++ b/drivers/power/abx500_chargalg.c
@@ -1664,8 +1664,8 @@ static int abx500_chargalg_get_property(struct power_supply *psy,
static ssize_t ab8500_chargalg_sysfs_show(struct kobject *kobj,
struct attribute *attr, char *buf)
{
- struct ab8500_chargalg *di = container_of(kobj,
- struct ab8500_chargalg, chargalg_kobject);
+ struct abx500_chargalg *di = container_of(kobj,
+ struct abx500_chargalg, chargalg_kobject);
return sprintf(buf, "%d\n",
di->susp_status.ac_suspended &&
diff --git a/drivers/power/pm2301_charger.c b/drivers/power/pm2301_charger.c
index 14e37b2..5ebae88 100644
--- a/drivers/power/pm2301_charger.c
+++ b/drivers/power/pm2301_charger.c
@@ -22,8 +22,8 @@
#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.h>
#include <linux/mfd/abx500/ab8500-bm.h>
#include <linux/mfd/abx500/ab8500-gpadc.h>
#include <linux/mfd/abx500/ux500_chargalg.h>
@@ -867,7 +867,7 @@ static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
/* get parent data */
pm2->dev = &i2c_client->dev;
- pm2->gpadc = ab8500_gpadc_get();
+ pm2->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
pm2->pm2_int = &pm2xxx_int;
--
1.7.9.5
^ permalink raw reply related
* [PATCH 22/24] ab8500-bm: Remove individual [charger|btemp|fg|chargalg] pdata structures
From: Lee Jones @ 2013-01-21 12:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358769840-4763-1-git-send-email-lee.jones@linaro.org>
None of the aforementioned components have their own dedicated
platform data structures anymore. Instead they have all been
merged into one big Battery Management container. Let's remove
them and place all the nice newly added attributes into the core
container.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
drivers/power/ab8500_charger.c | 4 ++--
include/linux/mfd/abx500.h | 3 +++
include/linux/mfd/abx500/ab8500-bm.h | 22 ----------------------
3 files changed, 5 insertions(+), 24 deletions(-)
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 871bf5a..432f6bc 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -2998,7 +2998,7 @@ static int ab8500_charger_probe(struct platform_device *pdev)
di->ac_chg.max_out_curr = ab8500_charger_current_map[
ARRAY_SIZE(ab8500_charger_current_map) - 1];
di->ac_chg.wdt_refresh = CHG_WD_INTERVAL;
- di->ac_chg.enabled = di->pdata->ac_enabled;
+ di->ac_chg.enabled = di->bm->ac_enabled;
di->ac_chg.external = false;
/* USB supply */
@@ -3019,7 +3019,7 @@ static int ab8500_charger_probe(struct platform_device *pdev)
di->usb_chg.max_out_curr = ab8500_charger_current_map[
ARRAY_SIZE(ab8500_charger_current_map) - 1];
di->usb_chg.wdt_refresh = CHG_WD_INTERVAL;
- di->usb_chg.enabled = di->pdata->usb_enabled;
+ di->usb_chg.enabled = di->bm->usb_enabled;
di->usb_chg.external = false;
/* Create a work queue for the charger */
diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h
index 0e6e90b..1beaa05 100644
--- a/include/linux/mfd/abx500.h
+++ b/include/linux/mfd/abx500.h
@@ -254,6 +254,9 @@ struct abx500_bm_data {
int usb_safety_tmr_h;
int bkup_bat_v;
int bkup_bat_i;
+ bool autopower_cfg;
+ bool ac_enabled;
+ bool usb_enabled;
bool no_maintenance;
bool capacity_scaling;
bool chg_unknown_bat;
diff --git a/include/linux/mfd/abx500/ab8500-bm.h b/include/linux/mfd/abx500/ab8500-bm.h
index b800332..802521a 100644
--- a/include/linux/mfd/abx500/ab8500-bm.h
+++ b/include/linux/mfd/abx500/ab8500-bm.h
@@ -404,28 +404,6 @@ struct ab8500_bm_data {
const struct ab8500_fg_parameters *fg_params;
};
-struct ab8500_charger_platform_data {
- char **supplied_to;
- size_t num_supplicants;
- bool autopower_cfg;
- bool ac_enabled;
- bool usb_enabled;
-};
-
-struct ab8500_btemp_platform_data {
- char **supplied_to;
- size_t num_supplicants;
-};
-
-struct ab8500_fg_platform_data {
- char **supplied_to;
- size_t num_supplicants;
-};
-
-struct ab8500_chargalg_platform_data {
- char **supplied_to;
- size_t num_supplicants;
-};
struct ab8500_btemp;
struct ab8500_gpadc;
struct ab8500_fg;
--
1.7.9.5
^ permalink raw reply related
* [PATCH 21/24] ab8500-charger: Do not touch VBUSOVV bits
From: Lee Jones @ 2013-01-21 12:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358769840-4763-1-git-send-email-lee.jones@linaro.org>
From: Jonas Aaberg <jonas.aberg@stericsson.com>
Do not touch the VBUSOVV in USBCHTRL2 when running on AB8505.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
Tested-by: Mian Yousaf KAUKAB <mian.yousaf.kaukab@stericsson.com>
---
drivers/power/ab8500_charger.c | 22 ++++++++++++++++------
include/linux/mfd/abx500/ab8500.h | 19 +++++++++++++++++++
2 files changed, 35 insertions(+), 6 deletions(-)
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index a632b94..871bf5a 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -2671,13 +2671,23 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di)
}
}
- /* VBUS OVV set to 6.3V and enable automatic current limitiation */
- ret = abx500_set_register_interruptible(di->dev,
- AB8500_CHARGER,
- AB8500_USBCH_CTRL2_REG,
- VBUS_OVV_SELECT_6P3V | VBUS_AUTO_IN_CURR_LIM_ENA);
+ if (is_ab9540_2p0(di->parent) || is_ab8505_2p0(di->parent))
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_CHARGER,
+ AB8500_USBCH_CTRL2_REG,
+ VBUS_AUTO_IN_CURR_LIM_ENA,
+ VBUS_AUTO_IN_CURR_LIM_ENA);
+ else
+ /*
+ * VBUS OVV set to 6.3V and enable automatic current limitation
+ */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_CHARGER,
+ AB8500_USBCH_CTRL2_REG,
+ VBUS_OVV_SELECT_6P3V | VBUS_AUTO_IN_CURR_LIM_ENA);
if (ret) {
- dev_err(di->dev, "failed to set VBUS OVV\n");
+ dev_err(di->dev,
+ "failed to set automatic current limitation\n");
goto out;
}
diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h
index 9dd9b99..b9a6a84 100644
--- a/include/linux/mfd/abx500/ab8500.h
+++ b/include/linux/mfd/abx500/ab8500.h
@@ -346,4 +346,23 @@ static inline int is_ab8500_2p0(struct ab8500 *ab)
return (is_ab8500(ab) && (ab->chip_id == AB8500_CUT2P0));
}
+static inline int is_ab8505_1p0_or_earlier(struct ab8500 *ab)
+{
+ return (is_ab8505(ab) && (ab->chip_id <= AB8500_CUT1P0));
+}
+
+static inline int is_ab8505_2p0(struct ab8500 *ab)
+{
+ return (is_ab8505(ab) && (ab->chip_id == AB8500_CUT2P0));
+}
+
+static inline int is_ab9540_1p0_or_earlier(struct ab8500 *ab)
+{
+ return (is_ab9540(ab) && (ab->chip_id <= AB8500_CUT1P0));
+}
+
+static inline int is_ab9540_2p0(struct ab8500 *ab)
+{
+ return (is_ab9540(ab) && (ab->chip_id == AB8500_CUT2P0));
+}
#endif /* MFD_AB8500_H */
--
1.7.9.5
^ permalink raw reply related
* [PATCH 20/24] ab8500-fg: Use correct battery charge full design
From: Lee Jones @ 2013-01-21 12:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1358769840-4763-1-git-send-email-lee.jones@linaro.org>
From: Rajkumar Kasirajan <rajkumar.kasirajan@stericsson.com>
If battery is not identified while fg probe, mah_max_design gets
initialized with unknown battery's charge full design. Reinitialize
mah_max_design if battery is identified after fg probe.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Rajkumar Kasirajan <rajkumar.kasirajan@stericsson.com>
Reviewed-by: Vijaya Kumar K-1 <vijay.kilari@stericsson.com>
Reviewed-by: Marcus COOPER <marcus.xm.cooper@stericsson.com>
Reviewed-by: Olivier CLERGEAUD <olivier.clergeaud@stericsson.com>
Reviewed-by: Arun MURTHY <arun.murthy@stericsson.com>
Reviewed-by: Rabin VINCENT <rabin.vincent@stericsson.com>
Tested-by: Rupesh KUMAR <rupesh.kumar@stericsson.com>
Tested-by: Jonas ABERG <jonas.aberg@stericsson.com>
---
drivers/power/ab8500_fg.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index 1a78116..0780b60 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -2090,7 +2090,8 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data)
case POWER_SUPPLY_PROP_TECHNOLOGY:
switch (ext->type) {
case POWER_SUPPLY_TYPE_BATTERY:
- if (!di->flags.batt_id_received) {
+ if (!di->flags.batt_id_received &&
+ di->bm->batt_id != BATTERY_UNKNOWN) {
const struct abx500_battery_type *b;
b = &(di->bm->bat_type[di->bm->batt_id]);
--
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